diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml index 456e54bd1..0619d1b15 100644 --- a/.github/workflows/build_release.yml +++ b/.github/workflows/build_release.yml @@ -140,4 +140,4 @@ jobs: webhook_token: ${{ secrets.DISCORD_TOKEN }} color: "#088c7d" username: "Maykr Bot" - message: "**:package: ${{ needs.changelog.outputs.tag }}**\n\n**Download**\n${{ fromJSON(steps.create_release.outputs.assets)[0].browser_download_url }}\n\n**Changelog**\n${{ steps.create_release.outputs.url }}" + message: "**:package: ${{ needs.changelog.outputs.tag }}**\n\n**Download**\n${{ fromJSON(steps.create_release.outputs.assets)[1].browser_download_url }}\n\n**Changelog**\n${{ steps.create_release.outputs.url }}" diff --git a/.github/workflows/gh_release.yml b/.github/workflows/gh_release.yml index da716af61..e47560d92 100644 --- a/.github/workflows/gh_release.yml +++ b/.github/workflows/gh_release.yml @@ -148,4 +148,4 @@ jobs: webhook_token: ${{ secrets.DISCORD_TOKEN }} color: "#088c7d" username: "Maykr Bot" - message: "**:package: ${{ needs.changelog.outputs.tag }}**\n\n**Download**\n${{ fromJSON(steps.create_release.outputs.assets)[0].browser_download_url }}\n\n**Changelog**\n${{ steps.create_release.outputs.url }}" + message: "**:package: ${{ needs.changelog.outputs.tag }}**\n\n**Download**\n${{ fromJSON(steps.create_release.outputs.assets)[1].browser_download_url }}\n\n**Changelog**\n${{ steps.create_release.outputs.url }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index ea22e4cf6..69213db85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,149 +1,84 @@ -## [2.0.0](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.1.13.0...1.19.2-v2.2.0.0) (2023-09-09) +## [2.9.0](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.2.8.0...1.19.2-v2.2.9.0) (2023-12-29) -### ⚠ BREAKING CHANGES - -* move menu package from inventory package into main package -* rename item interfaces -* **recipes:** change json structure and key names of recipes - ### Features -* add ability to "pet" Flesh Blobs ([377bff8](https://github.com/Elenterius/Biomancy/commit/377bff842406bc25c378ac202d82eb15e7050719)) -* add Caro Invitica font by Tyfin ([ccd2845](https://github.com/Elenterius/Biomancy/commit/ccd2845f53b65151e0ec17911013afce7568aef0)) -* add Chiseled Flesh block ([6597d23](https://github.com/Elenterius/Biomancy/commit/6597d234f1049dbb4f5940aa870fe02c61bc6f58)) -* add claws & fangs item tags for crafting ([3aa4eac](https://github.com/Elenterius/Biomancy/commit/3aa4eac074c19fb2482f9192be11f667f177de73)) -* add Corrosive Swipe Attack VFX to the Toxicus ([048c428](https://github.com/Elenterius/Biomancy/commit/048c4289ef5e874468598fa0a21a1be06bf1bfbe)) -* add Drowsy Status Effect which prevents Phantoms from attacking ([2a2449d](https://github.com/Elenterius/Biomancy/commit/2a2449d4c4507bd1bf232be5b2ab5064fc3091ea)) -* add Fibrous Flesh block ([fc0085e](https://github.com/Elenterius/Biomancy/commit/fc0085ea27157f6bdc6bdf2df535fddeb3815732)) -* add Fleshy Membrane block ([f9bf0ef](https://github.com/Elenterius/Biomancy/commit/f9bf0ef413ee86707eaf7c0ae12494dca53478c6)) -* add HUD for the serum amount inside an Injector ([62e17a2](https://github.com/Elenterius/Biomancy/commit/62e17a264ecc57e3ed12132678e39e15816784ca)) -* add item gui icon for the Ravenous Claws ([c0ca2b9](https://github.com/Elenterius/Biomancy/commit/c0ca2b9342d0d3e9817401fe4088d0cd52a0f7ad)) -* add item texture for the Flesh Spike ([666a5df](https://github.com/Elenterius/Biomancy/commit/666a5df3a6e5e46295eeecf093b024847cde23cb)) -* add item texture for the Tendon Chain ([d0c1110](https://github.com/Elenterius/Biomancy/commit/d0c1110285dcf0e403598f5d2294a039a706ff5b)) -* add malignant and primal flesh walls ([3c44be5](https://github.com/Elenterius/Biomancy/commit/3c44be5384846e25646ad173533c22941927557d)) -* add Malignant Flesh Block recipes ([f088d72](https://github.com/Elenterius/Biomancy/commit/f088d72928d84529aecee9ded5f80424950ee5c7)) -* add Ornate Flesh block ([82a222c](https://github.com/Elenterius/Biomancy/commit/82a222c43c6fdadfe7215796bbb40d63494543f7)) -* add Primal Flesh Block recipes ([bd4c5ff](https://github.com/Elenterius/Biomancy/commit/bd4c5ff44d4f6f45cbfc3227a5e37c9ae4e0ec86)) -* add primordial bio lantern ([c5ca95a](https://github.com/Elenterius/Biomancy/commit/c5ca95a5b3c5792eb6285a713332e74f28bcec48)) -* add specialised membrane variants for items, babies and adult mobs ([c472856](https://github.com/Elenterius/Biomancy/commit/c472856fb97a4f791ace1a6097c063141d3b8fbd)) -* add Toxicus ([ed0a733](https://github.com/Elenterius/Biomancy/commit/ed0a7333607301a48ec4e92d447c39c3ce93767b)) -* add Tubular Flesh block ([26216e3](https://github.com/Elenterius/Biomancy/commit/26216e3bb7e5190caca141eb831e6a7223f8212d)) -* buff Flesh Plunderer damage from 10 to 12 and durability from 60 to 250 ([a8b7219](https://github.com/Elenterius/Biomancy/commit/a8b721969427f17da3422fc75725aff8b1c47142)) -* change injector item icon texture ([cddccb0](https://github.com/Elenterius/Biomancy/commit/cddccb0c5a4a9b2cc6b92c6580cb522d41665bc4)) -* enable shading for tendon chain & hanging bio lantern model ([d68742e](https://github.com/Elenterius/Biomancy/commit/d68742eaef58710ddd00a5fe191a88e5e9c2f360)) -* fix and improve malignant flesh spreading mechanics ([ba6d415](https://github.com/Elenterius/Biomancy/commit/ba6d41594ce6af3b25379d70a0df5256d34a1a53)) -* fix Injector not reloading with max serum amount ([bf641d9](https://github.com/Elenterius/Biomancy/commit/bf641d9089d83094ab1c7ba3b46e92095f9c2396)) -* fix primordial flesh blobs spawning with tumors when summoned via spawn eggs ([6ae6103](https://github.com/Elenterius/Biomancy/commit/6ae6103bfe6ee50c533618d990fe673a45a49909)) -* give creative players the ability to see all Bio-Forge recipe regardless of if they have unlocked them ([1e3cfd0](https://github.com/Elenterius/Biomancy/commit/1e3cfd09baf076569569b961a1874bdd205d5976)) -* increase Breeding Stimulant duration from 12 to 14 seconds ([1e5c5f0](https://github.com/Elenterius/Biomancy/commit/1e5c5f0dea6c43c2acb3c25c74907e01a1da5a5a)) -* make all Flesh Blob sounds resource pack driven ([7d896ff](https://github.com/Elenterius/Biomancy/commit/7d896ffa9c51fc2ca52dd2d59cccdb38f4ec6710)) -* make Cleansing Serum consume saturation ([2ec8754](https://github.com/Elenterius/Biomancy/commit/2ec8754b666439824757cb47b32f863fb215b1ae)) -* make crafting cost (nutrients) data driven by including them in recipes ([6b38c12](https://github.com/Elenterius/Biomancy/commit/6b38c1299cb7235ab51b80fa63155167997122db)) -* make flesh plunderer magic outline emissive ([a75f2b9](https://github.com/Elenterius/Biomancy/commit/a75f2b911b1e142d0c41f77590834222075c6bfa)) -* make Flesh Spikes fall-on damage scale with the spike amount ([845e38e](https://github.com/Elenterius/Biomancy/commit/845e38eefcac1f4adfff61da01a11c3f159ad74a)) -* make Insomnia Cure serum apply the Drowsy status effect ([9e894cd](https://github.com/Elenterius/Biomancy/commit/9e894cdd1f815c9535b33f76fe70082538dc8ceb)) -* make Malignant Veins harvestable with silk touch tools (additionally to shears) ([2a0d2fc](https://github.com/Elenterius/Biomancy/commit/2a0d2fcc7c69c2922fa5267f7940642e82cf5a98)) -* make membrane block deal reduced fall damage ([d595417](https://github.com/Elenterius/Biomancy/commit/d595417fbd678c726a9d5d662926fdb7c2c0bd72)) -* make part of the Primordial Core item emissive ([2717963](https://github.com/Elenterius/Biomancy/commit/271796386426284c082c214e8e513c64fb621c3b)) -* mirco-buff Ravenous Claws ([ae231b2](https://github.com/Elenterius/Biomancy/commit/ae231b2f3699041b099c1c7ec8834f9a13780542)) -* **mod-compat:** add Create compacting recipes for flesh blocks ([47c2148](https://github.com/Elenterius/Biomancy/commit/47c21482dfce02d4d95c63d401d822b8e3494555)) -* obfuscate primal and malignant flesh block tooltips ([7ccefc8](https://github.com/Elenterius/Biomancy/commit/7ccefc8f79c31710097fd4535298cb22a6a8e1ca)) -* **recipes:** add Claw recipe to Bio-Forge ([c9912b6](https://github.com/Elenterius/Biomancy/commit/c9912b689f37c408e8705e677df011017c00332b)) -* **recipes:** add Fang recipe to Bio-Forge ([0cb2708](https://github.com/Elenterius/Biomancy/commit/0cb2708397b889cf99fe4c89058bd3339af06d14)) -* **recipes:** buff decomposing recipe of Golden Apples ([146a7ae](https://github.com/Elenterius/Biomancy/commit/146a7ae0af4481b669515cb639de1197072aba01)) -* **recipes:** change json structure and key names of recipes ([f488a0f](https://github.com/Elenterius/Biomancy/commit/f488a0f4ba48413e0144922af70df01285d7cb96)) -* **recipes:** nerf Flesh Spike recipe ([85489f9](https://github.com/Elenterius/Biomancy/commit/85489f9a5815942a28dae3f24bc91b1e870f49e8)) -* **recipes:** nerf kelp decomposing recipe ([a773441](https://github.com/Elenterius/Biomancy/commit/a773441f543baf86bee03986f277a14aa5c53b43)) -* **recipes:** nerf kelp digesting recipe ([85e843c](https://github.com/Elenterius/Biomancy/commit/85e843c9e1426390fbc5e3472f15e4c7e4ec9d80)) -* **recipes:** nerf Ravenous Claws recipe ([6205b23](https://github.com/Elenterius/Biomancy/commit/6205b2398914f55744125034ebff47f3561978b2)) -* **recipes:** remove Primordial Cradle recipe from Bio-Forge ([26ac3eb](https://github.com/Elenterius/Biomancy/commit/26ac3eb47f0d0773f03fbcc7de050ebb861e42af)) -* remove experimental Item-Membrane block ([9d2e108](https://github.com/Elenterius/Biomancy/commit/9d2e10826fffe0f67a4752a12b96fffae7a88921)) -* remove name tag for legacy flesh blob texture (legacy flesh blob is now a cradle easter egg) ([0750904](https://github.com/Elenterius/Biomancy/commit/075090440e940f883ccf0db5badf82841c9cae98)) -* remove trash slot from Bio-Lab UI ([dd94f80](https://github.com/Elenterius/Biomancy/commit/dd94f80d5f0aec87965e8507e9fcfb042ba4e461)) -* remove unused corrupted primal flesh block ([817861b](https://github.com/Elenterius/Biomancy/commit/817861b5f049d00918662015b335b31ab40b0597)) -* replace Malignant Flesh Blob with Primordial Flesh Blob variants ([5dc5d3e](https://github.com/Elenterius/Biomancy/commit/5dc5d3e1b49de6ff4f36a8f6a6504700b34955e0)) -* rework Cradle sacrifice system to prefer to spawn hostile mobs and occasionally anomalies ([cde0e36](https://github.com/Elenterius/Biomancy/commit/cde0e361a0461da44076488db0c7fcdfc585ef99)) -* tweak Adult-Membrane color ([67383e4](https://github.com/Elenterius/Biomancy/commit/67383e4e458672a038bd93c31b1ced09055f0587)) -* tweak flesh block recipes ([382948c](https://github.com/Elenterius/Biomancy/commit/382948c8d315eabfe4b2530dad1e402a53dc6acb)) -* tweak tooltip colors ([7a47e29](https://github.com/Elenterius/Biomancy/commit/7a47e29537647c76f9c811a6747f1939f1ae1ef9)) -* update flesh blob troll texture ([96152b6](https://github.com/Elenterius/Biomancy/commit/96152b64b42fc8891d1dd871d9c874c3506b739c)) -* update Flesh Block variant textures ([34557eb](https://github.com/Elenterius/Biomancy/commit/34557eb48dc866c77b24d9ae71b6ec71bfaf481a)) - - -### Bug Fixes +* **flesh-mound:** add bone pillar generation ([6c65cc1](https://github.com/Elenterius/Biomancy/commit/6c65cc17cf131864d5129b6052a07f7efaf43958)) +* **flesh-mound:** allow destruction of melons, pumpkins and moss ([f7acee8](https://github.com/Elenterius/Biomancy/commit/f7acee8d751e00fc07b7790f8928b615b69fe6f4)) +* **flesh-mound:** make mounds slightly smaller ([0448cd8](https://github.com/Elenterius/Biomancy/commit/0448cd8c30cf00f499a8a4efa637b8f7dada1028)) +* **flesh-mound:** make Primal Membrane self-spreading inside flesh mounds ([3db8c25](https://github.com/Elenterius/Biomancy/commit/3db8c256584c2ac8d1dd664e77fb2d12fa810aa5)) +* make primal membrane consider golems as not alive ([6435e3a](https://github.com/Elenterius/Biomancy/commit/6435e3aafbf866698f42d52ecad25f6e13c798fd)) -* fix alex's mobs kangaroo and moose ribs decomposer recipes (remove nutrients output) ([77e0baa](https://github.com/Elenterius/Biomancy/commit/77e0baa705baffa1c71807c915a4395a2dc5c736)) -* fix buggy Vial Holder block breaking ([c190dc4](https://github.com/Elenterius/Biomancy/commit/c190dc49125193468caaa88b58f30778ab6a0ebc)) -* fix critical hits not being detected ([0e89f24](https://github.com/Elenterius/Biomancy/commit/0e89f24f182f0771bae1f25ea65b8d6cf8653687)) -* fix Flesh Spike block not dropping the correct amount of spikes ([f91c641](https://github.com/Elenterius/Biomancy/commit/f91c641d346f52da6a49d6738c48468b5970571c)) -* fix Flesh Spikes destroying Items ([0a6f1f6](https://github.com/Elenterius/Biomancy/commit/0a6f1f661e7fba3f1ea7c22b62c06df4f8786a5c)) -* fix inability to inject tamed mobs with Serums ([310af51](https://github.com/Elenterius/Biomancy/commit/310af512857c87dbd64df3626d37a817d6b715cd)) -* fix incompatibility with outdated versions of Create by simply not setting up any compat for it (min. Create version is now 0.5.0) ([0c98e12](https://github.com/Elenterius/Biomancy/commit/0c98e123ad91dfc0dfbe295aa8399ffb4590e3ce)) -* fix Insomnia Cure "not sleepy" check not working properly ([7c628e5](https://github.com/Elenterius/Biomancy/commit/7c628e565e611eb945eaff129e99d001d407a5d3)) -* fix Primordial Cradle consuming Tetra tools ([9ee49e4](https://github.com/Elenterius/Biomancy/commit/9ee49e4853a6bf052e13c33e69040f3acd3356e9)) -* fix Primordial Cradle not being marked as changed when the spread charge is consumed ([faffc4d](https://github.com/Elenterius/Biomancy/commit/faffc4d99c67e716964fbd3ee5f01e401dc6eeb5)) -* fix SacrificeHandler incorrectly tracking if it has any modifiers applied ([674e703](https://github.com/Elenterius/Biomancy/commit/674e703a77637a965cc0d6043c13a2241efcfa85)) -* fix white line on the bottom of the Flesh Door model ([2a774b2](https://github.com/Elenterius/Biomancy/commit/2a774b27089abf21f58955a1789d3f0a142faacc)) +## [2.8.0](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.2.7.0...1.19.2-v2.2.8.0) (2023-12-24) -### Performance Improvements - -* use cached animations for Flesh Blobs ([002def3](https://github.com/Elenterius/Biomancy/commit/002def3110d3913ba117eab56e56ab185d88b476)) - - -### Miscellaneous Chores +### Features -* rename item interfaces ([19ea1db](https://github.com/Elenterius/Biomancy/commit/19ea1db2ba4e314e939f3eb5ef5bf62a72a4e699)) +* **acid:** reduce probability for acid particles to produce fizzling smoke when landing on ground ([df8e315](https://github.com/Elenterius/Biomancy/commit/df8e31512cc59eada311f5d7584daec4d6673e53)) +* add bloomlight recipe ([2de5134](https://github.com/Elenterius/Biomancy/commit/2de51343c0140e8fe6dbc827f1c3020371e55eb0)) +* buff strength of `FleshyBone` material (increase destroy time from 3 to 4 and explosion resistance from 3 to 6) ([9b7bf5c](https://github.com/Elenterius/Biomancy/commit/9b7bf5cd45e08f88da93ba9984335c455095a8ae)) +* change Primordial Lantern recipe to require a Sapberry ([33f3c25](https://github.com/Elenterius/Biomancy/commit/33f3c25cff5bad886656e8cbf55a0a032c0824d9)) +* change the block material of Flesh Pillar, Chiseled Flesh and Ornate Flesh to `FleshyBone` material ([2c9074a](https://github.com/Elenterius/Biomancy/commit/2c9074af6775f61db591d025e6864940f4d07894)) +* **flesh-mound:** add chamber decorations such as pillars, orifices, etc. ([8f0bdb2](https://github.com/Elenterius/Biomancy/commit/8f0bdb2c836c63dd6c7d47e6f61957c652de53f9)) +* increase explosion resistance of Packed Flesh from 6 to 12 ([cf05380](https://github.com/Elenterius/Biomancy/commit/cf05380ee3e8eb7b9a921517984657cc364b2f18)) +* **item-tag:** add `cannot_be_eaten_by_cradle` tag for items that should not be eaten by the Cradle ([52d2b60](https://github.com/Elenterius/Biomancy/commit/52d2b60e3f61c75ea849404549019fc09247d74f)) +* **item-tag:** add items to forge tags for doors, trapdoors and chests ([138f2f1](https://github.com/Elenterius/Biomancy/commit/138f2f1ed6a68eaa4e6af9ea461f14faf340b53b)) +* make bony flesh blocks play flesh or bone sounds (with equal probability) ([648650c](https://github.com/Elenterius/Biomancy/commit/648650c3ee001f232fedc7ffb9bf71bf655234ee)) +* make primal orifices milk-able with empty buckets ([2949671](https://github.com/Elenterius/Biomancy/commit/2949671e004e770ec831bed03fd68e294c532010)) +* tweak decomposing recipe of bloomlight ([844b217](https://github.com/Elenterius/Biomancy/commit/844b21763b9dcc55673f87a79c20a45a3c572c81)) -### Code Refactoring +### Bug Fixes -* move menu package from inventory package into main package ([3b4796b](https://github.com/Elenterius/Biomancy/commit/3b4796b8d05d515117ce84f4ffccd476894160da)) +* prevent storage sac from being eaten by the Cradle ([d1e9c66](https://github.com/Elenterius/Biomancy/commit/d1e9c663a22917063e89459384582d468d16dad1)) -## [1.13.0](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.1.12.0...1.19.2-v2.1.13.0) (2023-07-29) +## [2.7.0](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.2.6.0...1.19.2-v2.2.7.0) (2023-12-11) ### Features -* enhance tooltip clarity and consistency ([bdbbcb9](https://github.com/Elenterius/Biomancy/commit/bdbbcb96bea8c5b7b8bbb1f2c9772be8ab98ab31)) - -## [1.12.0](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.1.11.3...1.19.2-v2.1.12.0) (2023-07-18) +* **acid:** add acid fluid ([61da539](https://github.com/Elenterius/Biomancy/commit/61da539e64936ad6d8020c95a4e408d67d60cd7c)) +* **acid:** add dripping acid particle ([dd32f9d](https://github.com/Elenterius/Biomancy/commit/dd32f9de834d39906799c357f77702a563a7c2a2)) +* **acid:** add fluid compat for open pipes from Create ([dc17391](https://github.com/Elenterius/Biomancy/commit/dc173912e85795326cc80b94c2fb209ae9314b20)) +* **acid:** add fluid interaction with lava & water ([050bf47](https://github.com/Elenterius/Biomancy/commit/050bf47bf08ab59297716c84f380f21cb57be3e7)) +* add primal orifice block ([02a2c84](https://github.com/Elenterius/Biomancy/commit/02a2c84e141bba509855cba92da8bebb30367b75)) +* change chiseled flesh block/texture ([be7e5d9](https://github.com/Elenterius/Biomancy/commit/be7e5d9273be27dd16849993419b1b580dea206e)) +* **flesh-mound:** make Malignant Bloom spawn less likely inside the mound and more likely at the edges or outside ([3acb916](https://github.com/Elenterius/Biomancy/commit/3acb91623456fb86f247a1dfb60857cc79d1976c)) +* **flesh-mound:** tweak default mound gen settings ([7f06b15](https://github.com/Elenterius/Biomancy/commit/7f06b15508f5c86aa85836a3ce391c88fba4084f)) -### Features +### Bug Fixes -* add attack reach indicator for the Ravenous Claws ([ccc850f](https://github.com/Elenterius/Biomancy/commit/ccc850fce7a7d08aa86a97676eb2068f9167009b)) -* add Spanish translation ([4c3c35b](https://github.com/Elenterius/Biomancy/commit/4c3c35ba18f09cf94b8c3a8581927ed809fada13)) -* add status effect version of Despoil for use by datapacks etc ([fe2ae46](https://github.com/Elenterius/Biomancy/commit/fe2ae46e524d0b862f5a7ccae769878436589259)) -* improve flesh spike tooltip ([4e4c042](https://github.com/Elenterius/Biomancy/commit/4e4c042654d80f0cc11dab20d3f18fbca87b6082)) -* make bio-machines retain their fuel when destroyed ([8ec768d](https://github.com/Elenterius/Biomancy/commit/8ec768dcf6a5e92717a9be2ac2d6336b8098bb2c)) -* make flesh block texture less repetitive (uses placeholder textures) ([20fb879](https://github.com/Elenterius/Biomancy/commit/20fb87945492eb2cef904b323487dd281bdb7ddc)) +* **flesh-mound:** fix flesh veins being unable to find the flesh mound when other types of shapes exist in the same area ([8750164](https://github.com/Elenterius/Biomancy/commit/8750164116ab17518679a615ed8b2e545ec79392)) +* **gradle:** fix build configuration not including the api sourceset in the primary build artifact ([95c32e7](https://github.com/Elenterius/Biomancy/commit/95c32e7f04836a4a0189b2b8321e4e4a9910dc67)) +## [2.6.0](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.2.5.0...1.19.2-v2.2.6.0) (2023-12-09) -### Bug Fixes -* fix full block slabs not dropping two slab blocks ([54bb038](https://github.com/Elenterius/Biomancy/commit/54bb038109267982d1b5594df40cbfe589f3de19)) -* fix inability to switch claw modes when holding down ctrl to sprint ([870a033](https://github.com/Elenterius/Biomancy/commit/870a03359169f68ef0d7cd20963b24182918e61f)) -* fix misleading fleshkin chest tooltip ([47b2758](https://github.com/Elenterius/Biomancy/commit/47b2758b5eee547d2bbe7d9eec01402f5ce38cf2)) +### Features -### [1.11.3](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.1.11.2...1.19.2-v2.1.11.3) (2023-06-30) +* add neural interceptor block ([4ecc094](https://github.com/Elenterius/Biomancy/commit/4ecc09448bedd3d2c3fcb9b7ba1f7a417d3991aa)) +* add primal membrane block for living mobs ([a0ea6e8](https://github.com/Elenterius/Biomancy/commit/a0ea6e87eb5a29014a5f9a24bedc7cc057615d81)) +* add undead-permeable membrane ([0902a3b](https://github.com/Elenterius/Biomancy/commit/0902a3b936ae782a4b43cc75591124c3c2a630f7)) +* **flesh-mound:** add bloomlight block as flesh type replacement of shroomlight in mounds ([bdfa12f](https://github.com/Elenterius/Biomancy/commit/bdfa12f363b72874f1009d1d8f3b80218f74e98a)) +* **flesh-mound:** allow flesh mounds to excavate chambers and convert the destroyed blocks into primal energy ([593f9c9](https://github.com/Elenterius/Biomancy/commit/593f9c92971a9e8fdb198aec97fbd8d02a4c0b03)) +* **flesh-mound:** create "Doors" between two adjacent chambers with Primal Membranes ([2680d0e](https://github.com/Elenterius/Biomancy/commit/2680d0ed802d75c0abb892627be786a64051e0cb)) +* **flesh-mound:** improve storage and lookup of mound shapes ([e127cf6](https://github.com/Elenterius/Biomancy/commit/e127cf622b0aac3eb1f892f3dcc02421e1386929)) +* **flesh-mound:** make mound shapes persist across unloaded chunks and store mound shape seed in primordial cradle block/item ([32c2efd](https://github.com/Elenterius/Biomancy/commit/32c2efd8a74fdea3e45cd67d231d27217ae2e042)) +* **flesh-mound:** prevent flesh veins from eating living flesh ([36b6e65](https://github.com/Elenterius/Biomancy/commit/36b6e65e96f8c0cc1eb2d973ba99dd16585b1481)) +* **flesh-mound:** prevent natural spawning of mobs inside flesh mounds ([0af5924](https://github.com/Elenterius/Biomancy/commit/0af592432e1ac3dc7f071ec3c3419242ce94328a)) ### Bug Fixes -* fix Ravenous Claws ability tooltips not using translation keys ([989ff50](https://github.com/Elenterius/Biomancy/commit/989ff50e67110b0037d145f3a804a5455124ae91)) +* **flesh-mound:** fix unintentional MoundShape removal when the Cradle BlockEntity is unloaded ([a5cb46b](https://github.com/Elenterius/Biomancy/commit/a5cb46b5f2a8112de9a240bf1b85b61ba02cb0db)) -### [1.11.2](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.1.11.1...1.19.2-v2.1.11.2) (2023-06-25) +## [2.5.0](https://github.com/Elenterius/Biomancy/compare/1.19.2-v2.2.4.0...1.19.2-v2.2.5.0) (2023-11-19) -### Bug Fixes +### Features -* add missing language key for missing blood charge ([478c585](https://github.com/Elenterius/Biomancy/commit/478c58549ac677f3f960c8bd95ee87ac06bcb69d)) -* fix Maw Hopper not inserting items on the correct block face ([db57423](https://github.com/Elenterius/Biomancy/commit/db57423c451de0caf7f7529cad2cffdf47e27762)) -* fix Ravenous Claws item name ([ea9f79c](https://github.com/Elenterius/Biomancy/commit/ea9f79ce56afbcb5896f8b90618ade0dee21270a)) -* fix Ravenous Claws recipe ([272071d](https://github.com/Elenterius/Biomancy/commit/272071db69a6f57bbc93c8d938e948ccaac06139)) -* fix wrong fleshkin chest recipe ([a6be325](https://github.com/Elenterius/Biomancy/commit/a6be3254b4d77deed47f8824bf735776f1b7701f)) -* make Ravenous Claws tooltip less confusing ([c8940a6](https://github.com/Elenterius/Biomancy/commit/c8940a62b88a10f59b7857dec73904fbeb5a6832)) -* make tooltip text for pressing the v key on living tools less confusing ([7c7160f](https://github.com/Elenterius/Biomancy/commit/7c7160fd8515a767ae027f75a8586a5e8b62007e)) +* add block tags to explicitly allow/disallow attachment of flesh veins to specific blocks ([77c1fee](https://github.com/Elenterius/Biomancy/commit/77c1feee17f736fe2a6768f24b210e072077aedd)) +* **flesh-mound:** add first iteration of the flesh mound growth to the primordial cradle ([8175714](https://github.com/Elenterius/Biomancy/commit/8175714e23668e19e0685edfa14e2b40f9fc22f8)) +* **flesh-mound:** decrease volume of growth ([e528ba8](https://github.com/Elenterius/Biomancy/commit/e528ba833adb903db90877fc5d93e1183a55e281)) diff --git a/README.md b/README.md index e47d00c40..9e2011956 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ style. * [Download] * [Discord] * [Wiki] -* [Trello] +* [Github Project] ## Tech Stack @@ -73,7 +73,7 @@ the [Creative Commons Attribution-NonCommercial 4.0 International License](http: ### Maven -Atm there is no dedicated maven, you can use https://www.cursemaven.com/ to include the mod as a dependency. +Atm there is no dedicated maven, you can use [cursemaven](https://www.cursemaven.com/) or [Modrinth Maven](https://docs.modrinth.com/maven) to include the mod as a dependency. ### Contributing @@ -84,7 +84,7 @@ request to join the dev team.
This will give you access to the private mod development channels and resources such as the biomancy design document and concept board. -You can track the development progress via our [Trello Board][Trello]. +You can track the development progress via our [Dev Board][Github Project]. This project uses **Conventional Commits Messages** (https://www.conventionalcommits.org/en/v1.0.0/) to automatically genereate @@ -96,7 +96,7 @@ If you need help feel free to [join our Discord][Discord]. ## User Guide The mod provides no ingame guide book but uses tooltip descriptions & flavor texts instead. If you need further information you can read the github [Wiki]. -Read the [Getting Started Guide](https://github.com/Elenterius/Biomancy/wiki/v2/Getting-Started) section if you don't know what to do at all. +Read the [Getting Started Guide](https://github.com/Elenterius/Biomancy/wiki/Biomancy-2-Getting-Started) section if you don't know what to do at all. ### Recipes To conveniently look up recipes ingame I recommend the use of the [JEI] mod. @@ -104,8 +104,9 @@ To conveniently look up recipes ingame I recommend the use of the [JEI] mod. [Download]: https://www.curseforge.com/minecraft/mc-mods/biomancy [Discord]: https://discord.gg/424awTDdJJ -[Wiki]: https://github.com/Elenterius/Biomancy/wiki/v2 -[Trello]: https://trello.com/b/GUKjOSAl -[JitPack]: https://jitpack.io/#Elenterius/Biomancy + +[Wiki]: https://github.com/Elenterius/Biomancy/wiki/Biomancy-2 + +[Github Project]: https://github.com/orgs/Creative-Chasm/projects/2/ [JEI]:https://www.curseforge.com/minecraft/mc-mods/jei diff --git a/build.gradle b/build.gradle index 7a097e3d4..a1e93e01f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,8 @@ plugins { id 'org.spongepowered.mixin' version '0.7-SNAPSHOT' } +jarJar.enable() + import groovy.json.JsonSlurper import java.util.concurrent.TimeUnit @@ -34,6 +36,7 @@ sourceSets { main { resources { srcDir 'src/generated/resources' // Include resources generated by data generators + exclude '.cache/' } } test {} @@ -65,6 +68,10 @@ minecraft { // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. property 'forge.enabledGameTestNamespaces', 'biomancy' + // Hot-Swapping via JBR, includes schema changes + // Guide: https://forge.gemwire.uk/wiki/Hotswap#Applying_schema_changes + jvmArgs '-XX:+IgnoreUnrecognizedVMOptions', '-XX:+AllowEnhancedClassRedefinition' + mods { biomancy { source sourceSets.main @@ -78,13 +85,6 @@ minecraft { args '--username', 'Dev2' } - // Hot-Swapping via JBR, includes schema changes - // Guide: https://forge.gemwire.uk/wiki/Hotswap#Applying_schema_changes - clientWithJBR { - parent runs.client - jvmArg '-XX:+AllowEnhancedClassRedefinition' - } - server { workingDirectory project.file('run') @@ -180,8 +180,10 @@ repositories { maven { url = 'https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/' } maven { url = 'https://maven.blamejared.com' } maven { - name = "KliKli Maven (Modnomicon)" - url = "https://repo.repsy.io/mvn/klikli-dev/mods" + url "https://dl.cloudsmith.io/public/klikli-dev/mods/maven/" + content { + includeGroup "com.klikli_dev" + } } maven { name = 'tterrag maven' @@ -206,8 +208,12 @@ dependencies { implementation fg.deobf("software.bernie.geckolib:geckolib-forge-1.19:3.1.40") datagenImplementation fg.deobf("software.bernie.geckolib:geckolib-forge-1.19:3.1.40") - implementation fg.deobf("com.klikli_dev:modonomicon:1.19.2-1.30.2") - datagenImplementation fg.deobf("com.klikli_dev:modonomicon:1.19.2-1.30.2") + //https://h2database.com/html/mvstore.html + minecraftLibrary(group: "com.h2database", name: "h2-mvstore", version: "2.2.224") + jarJar(group: "com.h2database", name: "h2-mvstore", version: "[2.2.220,3.0.0)") + + implementation fg.deobf("com.klikli_dev:modonomicon-${mc_version}:1.34.0") + datagenImplementation fg.deobf("com.klikli_dev:modonomicon-${mc_version}:1.34.0") compileOnly fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}") compileOnly fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}") // compile against the JEI API but do not include it at runtime @@ -230,6 +236,8 @@ dependencies { // implementation fg.deobf("se.mickelus.mutil:mutil:1.19.2-5.1.0") compileOnly fg.deobf("curse.maven:tetra-289712:4738567") // 1.19.2-5.5.0 +// runtimeOnly fg.deobf("maven.modrinth:nerb:ANmCMdMt") //NERB 0.3 + //TODO: Morph Mod Integration? // implementation fg.deobf("curse.maven:identity-391390:3807264") // https://www.curseforge.com/minecraft/mc-mods/identity // runtimeOnly fg.deobf("curse.maven:architectury-419699:4040966") @@ -240,7 +248,6 @@ dependencies { } processResources { - exclude '.cache' exclude '**/*.psd' exclude '**/*.bbmodel' exclude '**/*.pdn' @@ -263,7 +270,14 @@ void minifyJsons(String fdir) { printf "%7s sec, %s ms \n", (int) (ms / 1000), ms % 1000 } +tasks.jarJar { + archiveClassifier = '' + from sourceSets.api.output + from { ["CREDITS.md", "LICENSE.txt"] } //copy files from the repository root into the jar +} + jar { + archiveClassifier = "slim" from sourceSets.api.output // manifest properties for reading on runtime @@ -281,10 +295,13 @@ jar { from { ["CREDITS.md", "LICENSE.txt"] } //copy files from the repository root into the jar } -// This is the preferred method to reobfuscate your jar file -jar.finalizedBy('reobfJar') -// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing -//publish.dependsOn('reobfJar') +reobf { + jarJar {} +} + +//jar.finalizedBy('reobfJar') +tasks.build.dependsOn tasks.jarJar +jar.finalizedBy('reobfJarJar') publishing { publications { diff --git a/mod_version.json b/mod_version.json index 512051c82..d972fac93 100644 --- a/mod_version.json +++ b/mod_version.json @@ -1,4 +1,4 @@ { "major": "2", - "semantic": "2.0.0" + "semantic": "2.9.0" } \ No newline at end of file diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/DataGenerators.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/DataGenerators.java index f1e5a09b1..96b86f0f8 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/DataGenerators.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/DataGenerators.java @@ -60,7 +60,7 @@ public static void gatherData(final GatherDataEvent event) { generator.addProvider(true, new ModAdvancementProvider(generator, existingFileHelper, translationProvider)); //guide book - generator.addProvider(false, new GuideBookProvider(generator, translationProvider)); //TODO: re-enable when ready + generator.addProvider(true, new GuideBookProvider(generator, translationProvider)); //TODO: re-enable when ready generator.addProvider(true, translationProvider); } diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/ModSoundProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/ModSoundProvider.java index ddb527f83..41fcce36a 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/ModSoundProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/ModSoundProvider.java @@ -35,6 +35,12 @@ public void registerSounds() { addSimpleSounds(ModSoundEvents.FLESH_BLOCK_BREAK, 4); addSimpleSounds(ModSoundEvents.FLESH_BLOCK_FALL, 3); + addSimpleRedirects(ModSoundEvents.BONY_FLESH_BLOCK_HIT, ModSoundEvents.FLESH_BLOCK_HIT.get(), SoundEvents.BONE_BLOCK_HIT); + addSimpleRedirects(ModSoundEvents.BONY_FLESH_BLOCK_PLACE, ModSoundEvents.FLESH_BLOCK_PLACE.get(), SoundEvents.BONE_BLOCK_PLACE); + addSimpleRedirects(ModSoundEvents.BONY_FLESH_BLOCK_STEP, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundEvents.BONE_BLOCK_STEP); + addSimpleRedirects(ModSoundEvents.BONY_FLESH_BLOCK_BREAK, ModSoundEvents.FLESH_BLOCK_BREAK.get(), SoundEvents.BONE_BLOCK_BREAK); + addSimpleRedirects(ModSoundEvents.BONY_FLESH_BLOCK_FALL, ModSoundEvents.FLESH_BLOCK_FALL.get(), SoundEvents.BONE_BLOCK_FALL); + addSimpleSounds(ModSoundEvents.FLESH_DOOR_OPEN, 2); addSimpleSounds(ModSoundEvents.FLESH_DOOR_CLOSE, 2); @@ -82,7 +88,7 @@ public void registerSounds() { addSimpleRedirect(ModSoundEvents.DIGESTER_CRAFTING_COMPLETED, SoundEvents.PLAYER_BURP, 1f, 1.25f); addSimpleRedirect(ModSoundEvents.FLESH_BLOB_JUMP, SoundEvents.SLIME_JUMP); - addSimpleRedirect(ModSoundEvents.FLESH_BLOB_HURT, ModSoundEvents.FLESH_BLOCK_BREAK.get(), 0.8f, 0.9f); + addSimpleRedirect(ModSoundEvents.FLESH_BLOB_HURT, ModSoundEvents.FLESH_BLOCK_BREAK, 0.8f, 0.9f); addSimpleRedirect(ModSoundEvents.FLESH_BLOB_DEATH, SoundEvents.GENERIC_DEATH); addSimpleSounds(ModSoundEvents.FLESH_BLOB_AMBIENT, 3, 0.8f, 0.9f); addSimpleSound(ModSoundEvents.GENERIC_MEW_PURR, 0.7f, 0.45f); @@ -169,4 +175,13 @@ protected void addSimpleRedirect(RegistryObject soundHolder, Registr ); } + protected void addSimpleRedirects(RegistryObject soundHolder, SoundEvent... redirectTargets) { + SoundDefinition soundDefinition = definition().subtitle(translationKey(soundHolder)); + + for (SoundEvent redirectTarget : redirectTargets) { + soundDefinition.with().with(sound(redirectTarget.getLocation(), SoundDefinition.SoundType.EVENT)); + } + + add(soundHolder, soundDefinition); + } } diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/advancements/AdvancementBuilder.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/advancements/AdvancementBuilder.java index e597db322..605deef67 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/advancements/AdvancementBuilder.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/advancements/AdvancementBuilder.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.datagen.advancements; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.datagen.lang.LangProvider; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.advancements.Advancement; import net.minecraft.advancements.CriterionTriggerInstance; import net.minecraft.advancements.FrameType; diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/lang/EnglishLangProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/lang/EnglishLangProvider.java index 9d3e8cf52..491dbf8e0 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/lang/EnglishLangProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/lang/EnglishLangProvider.java @@ -160,6 +160,7 @@ protected void addTranslations() { addItemTranslations(); addBlockTranslations(); + addFluidTranslations(); addEntityTranslations(); addEnchantmentTranslations(); addStatusEffectTranslations(); @@ -169,6 +170,7 @@ protected void addTranslations() { addTooltip("empty", "Empty"); addTooltip("contains", "Contains: %1$s"); addTooltip("nutrients_fuel", "Nutrients"); + addTooltip("primal_energy", "Primal Energy"); addTooltip("nutrients_consumes", "Consumes %1$s u"); addTooltip("consumption", "Consumption"); addTooltip("bile_fuel", "Bile"); @@ -252,6 +254,12 @@ private void addSoundTranslations() { addSound(ModSoundEvents.FLESH_BLOCK_BREAK, "Break Fleshy Block"); addSound(ModSoundEvents.FLESH_BLOCK_FALL, "Fall on Fleshy Block"); + addSound(ModSoundEvents.BONY_FLESH_BLOCK_HIT, "Hit Bony Flesh Block"); + addSound(ModSoundEvents.BONY_FLESH_BLOCK_PLACE, "Place Bony Flesh Block"); + addSound(ModSoundEvents.BONY_FLESH_BLOCK_STEP, "Step on Bony Flesh Block"); + addSound(ModSoundEvents.BONY_FLESH_BLOCK_BREAK, "Break Bony Flesh Block"); + addSound(ModSoundEvents.BONY_FLESH_BLOCK_FALL, "Fall on Bony Flesh Block"); + addSound(ModSoundEvents.FLESH_DOOR_OPEN, "Open Fleshy Door"); addSound(ModSoundEvents.FLESH_DOOR_CLOSE, "Close Fleshy Door"); @@ -361,6 +369,7 @@ private void addItemTranslations() { addItem(ModItems.NUTRIENTS, "Nutrients", "Very hard pellets rich in energy that almost look like vitamin pills."); addItem(ModItems.NUTRIENT_PASTE, "Nutrient Paste", "Nutrients combined with Biotic Matter to moisten the hard pellets, producing a paste.\nIt almost looks like yellowish cake, and is a convenient source of energy."); addItem(ModItems.NUTRIENT_BAR, "Nutrient Bar", "Nutrient Paste compressed into the shape of a bar. Looks edible, if a bit bland."); + addItem(ModItems.BLOOMBERRY, "Bloomberry", "An exotic delicacy which has a bioluminescent sheen and grants random potion effects when eaten."); addItem(ModItems.REGENERATIVE_FLUID, "Regenerative Fluid", "A fluid with regenerative properties, used to concoct healing additives."); addItem(ModItems.WITHERING_OOZE, "Withering Ooze", "A corrosive extract. It likely has uses in bio-alchemy."); @@ -388,6 +397,7 @@ private void addItemTranslations() { Looks like some objects are wrapped in an organic layer of skin. Might be filled with items or toxin if your language is set to German. Right Click the Sac to retrieve the Items."""); + addItem(ModItems.ACID_BUCKET, "Acid Bucket"); addItem(ModItems.RAVENOUS_CLAWS, "Ravenous Claws", """ Extremely hungry and vicious Claws forged by starving living flesh and grafting claws onto it. @@ -480,12 +490,16 @@ private void addBlockTranslations() { addBlock(ModBlocks.FLESH_LADDER, "Flesh Ladder", "Ladder mainly made of bones and a little bit of flesh..."); addBlock(ModBlocks.YELLOW_BIO_LANTERN, "Yellow Bio-Lantern", "A bioluminescent light source that is energy-efficient and environmentally friendly."); addBlock(ModBlocks.BLUE_BIO_LANTERN, "Blue Bio-Lantern", "A bioluminescent light source. This one is blue!"); - addBlock(ModBlocks.PRIMORDIAL_BIO_LANTERN, "Primordial Bio-Lantern", "A primal light source. This one is magenta!"); + addBlock(ModBlocks.PRIMORDIAL_BIO_LANTERN, "Bloom-Lantern", "A magenta light source made from a bioluminescent berry."); + addBlock(ModBlocks.BLOOMLIGHT, "Bloomlight", "A malignant light source. This one is magenta as well!"); addBlock(ModBlocks.TENDON_CHAIN, "Tendon Chain", "A chain made of tendons."); addBlock(ModBlocks.VIAL_HOLDER, "Vial Holder", "Display and organize your serums."); addBlock(ModBlocks.IMPERMEABLE_MEMBRANE, "Impermeable Membrane", "Gelatinous-like membrane reinforced with elastic fibers."); addBlock(ModBlocks.BABY_PERMEABLE_MEMBRANE, "Baby-Permeable Membrane", "Gelatinous-like membrane reinforced with elastic fibers.\n\nBaby mobs can diffuse through the membrane."); addBlock(ModBlocks.ADULT_PERMEABLE_MEMBRANE, "Adult-Permeable Membrane", "Gelatinous-like membrane reinforced with elastic fibers.\n\nAdult mobs can diffuse through the membrane."); + addBlock(ModBlocks.PRIMAL_PERMEABLE_MEMBRANE, "Primal Membrane", "Gelatinous-like membrane reinforced with elastic fibers.\n\nOnly mobs that are alive can diffuse through the membrane."); + addBlock(ModBlocks.UNDEAD_PERMEABLE_MEMBRANE, "Phantom Membrane", "Gelatinous-like membrane reinforced with elastic fibers.\n\nOnly mobs that are undead can diffuse through the membrane."); + //addBlock(ModBlocks.NEURAL_INTERCEPTOR, "Neural Interceptor", "A psychic node that prevents natural mob spawning in a 48 block radius."); addBlock(ModBlocks.PRIMAL_FLESH, "Primal Flesh Block", "Primitive and pure, you better not touch this with your dirty mitts."); addBlock(ModBlocks.PRIMAL_FLESH_SLAB, "Primal Flesh Slab", "Primitive and pure, you better not touch this with your dirty mitts."); @@ -496,6 +510,10 @@ private void addBlockTranslations() { addBlock(ModBlocks.MALIGNANT_FLESH_STAIRS, "Malignant Flesh Stairs", "Stairs made of malignant flesh.\nLooks diseased..."); addBlock(ModBlocks.MALIGNANT_FLESH_WALL, "Malignant Flesh Wall", "Wall of malignant flesh.\nIt's coming for you! ;)"); addBlock(ModBlocks.MALIGNANT_FLESH_VEINS, "Malignant Flesh Veins", "They look almost feral...\nyou better not touch them."); + addBlock(ModBlocks.MALIGNANT_BLOOM, "Primal Bloom", "An exotic flower of primordial beauty.\n\nIt will spread itself by launching it's ripe berry into the air.\nOn impact the berry explodes and spreads malignant veins as well."); + addBlock(ModBlocks.PRIMAL_ORIFICE, "Primal Orifice", "A primitive piece full of holes. It seems to leak an acidic substance."); + + addBlock(ModBlocks.ACID_FLUID_BLOCK, "Acid"); } private void addEntityTranslations() { @@ -506,4 +524,7 @@ private void addEntityTranslations() { addEntityType(ModEntityTypes.PRIMORDIAL_HUNGRY_FLESH_BLOB, "Primordial Hungry Flesh Blob"); } + private void addFluidTranslations() { + addFluidType(ModFluids.ACID_TYPE, "Acid"); + } } diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/loot/ModBlockLoot.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/loot/ModBlockLoot.java index eed42ff6e..3ddd54b16 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/loot/ModBlockLoot.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/loot/ModBlockLoot.java @@ -57,6 +57,15 @@ protected static LootTable.Builder createNameableBioMachineTable(Block block) { ))); } + protected static LootTable.Builder createPrimordialCradleTable(Block block) { + return LootTable.lootTable().withPool(applyExplosionCondition(block, LootPool.lootPool().setRolls(ConstantValue.exactly(1)) + .add(LootItem.lootTableItem(block) + .apply(CopyNbtFunction.copyData(ContextNbtProvider.BLOCK_ENTITY).copy("PrimalEnergy", "BlockEntityTag.PrimalEnergy")) + .apply(CopyNbtFunction.copyData(ContextNbtProvider.BLOCK_ENTITY).copy("ProcGenValues", "BlockEntityTag.ProcGenValues")) + .apply(CopyNbtFunction.copyData(ContextNbtProvider.BLOCK_ENTITY).copy("SacrificeHandler", "BlockEntityTag.SacrificeHandler")) + ))); + } + protected static LootTable.Builder dropWithInventory(Block block) { return LootTable.lootTable().withPool(applyExplosionCondition(block, LootPool.lootPool().setRolls(ConstantValue.exactly(1)) .add(LootItem.lootTableItem(block) @@ -104,9 +113,9 @@ protected static LootTable.Builder createFleshSpikeTable(FleshSpikeBlock block) return LootTable.lootTable().withPool(LootPool.lootPool() .setRolls(ConstantValue.exactly(1)) .add(applyExplosionDecay(block, LootItem.lootTableItem(block).apply( - IntStream.range(FleshSpikeBlock.MIN_SPIKES + 1, FleshSpikeBlock.MAX_SPIKES + 1).boxed().toList(), + IntStream.range(FleshSpikeBlock.SPIKES.getMin() + 1, FleshSpikeBlock.SPIKES.getMax() + 1).boxed().toList(), spikes -> SetItemCountFunction.setCount(ConstantValue.exactly(spikes)) - .when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(block).setProperties(StatePropertiesPredicate.Builder.properties().hasProperty(FleshSpikeBlock.SPIKES, spikes))) + .when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(block).setProperties(StatePropertiesPredicate.Builder.properties().hasProperty(FleshSpikeBlock.SPIKES.get(), spikes))) )))); } @@ -114,7 +123,7 @@ protected static LootTable.Builder createFleshSpikeTable(FleshSpikeBlock block) protected void addTables() { LOGGER.info(logMarker, "registering block loot..."); - dropSelf(ModBlocks.PRIMORDIAL_CRADLE.get()); + add(ModBlocks.PRIMORDIAL_CRADLE.get(), ModBlockLoot::createPrimordialCradleTable); dropSelf(ModBlocks.TONGUE.get()); dropSelf(ModBlocks.MAW_HOPPER.get()); add(ModBlocks.STORAGE_SAC.get(), ModBlockLoot::dropWithInventory); @@ -155,17 +164,24 @@ protected void addTables() { dropSelf(ModBlocks.MALIGNANT_FLESH_STAIRS.get()); dropSelf(ModBlocks.MALIGNANT_FLESH_WALL.get()); add(ModBlocks.MALIGNANT_FLESH_VEINS.get(), block -> createMultifaceBlockDrops(block, HAS_SHEARS_OR_SILK_TOUCH)); + add(ModBlocks.MALIGNANT_BLOOM.get(), noDrop()); + dropSelf(ModBlocks.PRIMAL_ORIFICE.get()); dropSelf(ModBlocks.VOICE_BOX.get()); dropSelf(ModBlocks.IMPERMEABLE_MEMBRANE.get()); dropSelf(ModBlocks.BABY_PERMEABLE_MEMBRANE.get()); dropSelf(ModBlocks.ADULT_PERMEABLE_MEMBRANE.get()); + dropSelf(ModBlocks.PRIMAL_PERMEABLE_MEMBRANE.get()); + dropSelf(ModBlocks.UNDEAD_PERMEABLE_MEMBRANE.get()); + //dropSelf(ModBlocks.NEURAL_INTERCEPTOR.get()); + dropSelf(ModBlocks.FLESH_IRIS_DOOR.get()); dropSelf(ModBlocks.FLESH_FENCE.get()); dropSelf(ModBlocks.FLESH_FENCE_GATE.get()); dropSelf(ModBlocks.FLESH_LADDER.get()); dropSelf(ModBlocks.YELLOW_BIO_LANTERN.get()); dropSelf(ModBlocks.PRIMORDIAL_BIO_LANTERN.get()); + dropSelf(ModBlocks.BLOOMLIGHT.get()); dropSelf(ModBlocks.BLUE_BIO_LANTERN.get()); dropSelf(ModBlocks.TENDON_CHAIN.get()); dropSelf(ModBlocks.VIAL_HOLDER.get()); @@ -174,6 +190,8 @@ protected void addTables() { addCustom(ModBlocks.FULL_FLESH_DOOR.get(), ModBlockLoot::createFleshDoorTable); addCustom(ModBlocks.FLESH_SPIKE.get(), ModBlockLoot::createFleshSpikeTable); + + add(ModBlocks.ACID_FLUID_BLOCK.get(), noDrop()); } protected void addCustom(T block, Function function) { diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/models/ModBlockStateProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/models/ModBlockStateProvider.java index 86538541b..aae49d7e2 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/models/ModBlockStateProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/models/ModBlockStateProvider.java @@ -3,6 +3,8 @@ import com.github.elenterius.biomancy.BiomancyMod; import com.github.elenterius.biomancy.block.*; import com.github.elenterius.biomancy.block.fleshspike.FleshSpikeBlock; +import com.github.elenterius.biomancy.block.malignantbloom.MalignantBloomBlock; +import com.github.elenterius.biomancy.block.orifice.OrificeBlock; import com.github.elenterius.biomancy.block.ownable.OwnablePressurePlateBlock; import com.github.elenterius.biomancy.block.property.DirectionalSlabType; import com.github.elenterius.biomancy.block.property.Orientation; @@ -75,7 +77,7 @@ protected void registerStatesAndModels() { axisBlockWithItem(ModBlocks.FLESH_PILLAR); simpleBlockWithItem(ModBlocks.FIBROUS_FLESH); - simpleBlockWithItem(ModBlocks.CHISELED_FLESH); + existingBlockWithItem(ModBlocks.CHISELED_FLESH); axisBlockWithItem(ModBlocks.ORNATE_FLESH); axisBlockWithItem(ModBlocks.TUBULAR_FLESH_BLOCK); @@ -89,6 +91,8 @@ protected void registerStatesAndModels() { stairsBlockWithItem(ModBlocks.MALIGNANT_FLESH_STAIRS, ModBlocks.MALIGNANT_FLESH); wallBlock(ModBlocks.MALIGNANT_FLESH_WALL, ModBlocks.MALIGNANT_FLESH); veinsBlock(ModBlocks.MALIGNANT_FLESH_VEINS); + malignantBloom(ModBlocks.MALIGNANT_BLOOM); + orifice(ModBlocks.PRIMAL_ORIFICE); irisDoor(ModBlocks.FLESH_IRIS_DOOR, true); fleshDoor(ModBlocks.FLESH_DOOR); @@ -96,9 +100,15 @@ protected void registerStatesAndModels() { translucentBlockWithItem(ModBlocks.IMPERMEABLE_MEMBRANE); membraneWithItem(ModBlocks.BABY_PERMEABLE_MEMBRANE); membraneWithItem(ModBlocks.ADULT_PERMEABLE_MEMBRANE); + membraneWithItem(ModBlocks.PRIMAL_PERMEABLE_MEMBRANE); + membraneWithItem(ModBlocks.UNDEAD_PERMEABLE_MEMBRANE); + + //horizontalBlockWithItem(ModBlocks.NEURAL_INTERCEPTOR); + bioLantern(ModBlocks.YELLOW_BIO_LANTERN); bioLantern(ModBlocks.BLUE_BIO_LANTERN); bioLantern(ModBlocks.PRIMORDIAL_BIO_LANTERN); + simpleBlockWithItem(ModBlocks.BLOOMLIGHT); tendonChain(ModBlocks.TENDON_CHAIN); vialHolder(ModBlocks.VIAL_HOLDER); @@ -283,6 +293,16 @@ public void existingBlockWithItem(Block block) { simpleBlockItem(block, existingModel); } + public void horizontalBlockWithItem(RegistryObject block) { + horizontalBlockWithItem(block.get()); + } + + public void horizontalBlockWithItem(Block block) { + ModelFile.ExistingModelFile existingModel = models().getExistingFile(blockAsset(block)); + horizontalBlock(block, blockState -> existingModel); + simpleBlockItem(block, existingModel); + } + public void directionalBlockWithItem(Block block) { ModelFile.ExistingModelFile existingModel = models().getExistingFile(blockAsset(block)); directionalBlock(block, blockState -> existingModel, BlockStateProperties.WATERLOGGED); @@ -309,6 +329,53 @@ public void directionalBlock(Block block, Function modelF }, ignored); } + public void orifice(RegistryObject block) { + orifice(block.get()); + } + + public void orifice(OrificeBlock block) { + ResourceLocation model = blockAsset(block); + + ModelFile.ExistingModelFile defaultModel = models().getExistingFile(model); + ModelFile.ExistingModelFile leakingModel = models().getExistingFile(extend(model, "_leaking")); + + ModelFile.ExistingModelFile[] models = { + defaultModel, + defaultModel, + leakingModel + }; + + getVariantBuilder(block) + .forAllStatesExcept(blockState -> { + Integer age = OrificeBlock.AGE.getValue(blockState); + return ConfiguredModel.builder() + .modelFile(models[age]) + .build(); + }); + + simpleBlockItem(block, models[0]); + } + + public void malignantBloom(RegistryObject block) { + malignantBloom(block.get()); + } + + public void malignantBloom(MalignantBloomBlock block) { + ResourceLocation model = blockAsset(block); + + ModelFile.ExistingModelFile[] models = { + models().getExistingFile(extend(model, "_1")), + models().getExistingFile(extend(model, "_2")), + models().getExistingFile(extend(model, "_3")), + models().getExistingFile(extend(model, "_4")), + models().getExistingFile(extend(model, "_5")) + }; + + directionalBlock(block, blockState -> models[MalignantBloomBlock.getStage(blockState)], BlockStateProperties.WATERLOGGED); + + simpleBlockItem(block, models[0]); + } + public void fleshSpikes(Block block) { ResourceLocation model = blockAsset(block); ModelFile.ExistingModelFile[] models = { diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/models/ModItemModelProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/models/ModItemModelProvider.java index cd4511e5d..511b90e53 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/models/ModItemModelProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/models/ModItemModelProvider.java @@ -86,6 +86,7 @@ protected void registerModels() { basicItem(ModItems.CREATOR_MIX); basicItem(ModItems.NUTRIENT_PASTE); basicItem(ModItems.NUTRIENT_BAR); + basicItem(ModItems.BLOOMBERRY); serumItem(ModItems.VIAL); basicItem(ModItems.GIFT_SAC); basicItem(ModItems.FERTILIZER); @@ -102,6 +103,8 @@ protected void registerModels() { flatBlockItem(ModItems.FLESH_LADDER); flatBlockItem(ModItems.MALIGNANT_FLESH_VEINS); + dynamicBucket(ModItems.ACID_BUCKET.get()); + //generate models for all eggs ModItems.ITEMS.getEntries().stream().map(RegistryObject::get).filter(SpawnEggItem.class::isInstance).forEach(this::spawnEggItem); } diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/modonomicon/GuideBookProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/modonomicon/GuideBookProvider.java index d1ac2d92d..d5e3c87e5 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/modonomicon/GuideBookProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/modonomicon/GuideBookProvider.java @@ -12,11 +12,15 @@ import com.klikli_dev.modonomicon.api.datagen.book.BookEntryModel; import com.klikli_dev.modonomicon.api.datagen.book.BookEntryParentModel; import com.klikli_dev.modonomicon.api.datagen.book.BookModel; -import com.klikli_dev.modonomicon.api.datagen.book.page.*; +import com.klikli_dev.modonomicon.api.datagen.book.page.BookCraftingRecipePageModel; +import com.klikli_dev.modonomicon.api.datagen.book.page.BookEntityPageModel; +import com.klikli_dev.modonomicon.api.datagen.book.page.BookSpotlightPageModel; +import com.klikli_dev.modonomicon.api.datagen.book.page.BookTextPageModel; import net.minecraft.data.DataGenerator; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.phys.Vec2; public class GuideBookProvider extends AbstractBookProvider { @@ -30,217 +34,222 @@ private ResourceLocation entryId(BookLangHelper helper) { @Override protected void generate() { + BookLangHelper langHelper = ModonomiconAPI.get().getLangHelper(modId); - BookLangHelper helper = ModonomiconAPI.get().getLangHelper(modId); - helper.book("guide_book"); + langHelper.book("guide_book"); + BookModel book = BookModel.create(GuideBookItem.GUIDE_BOOK_ID, langHelper.bookName()) + .withTooltip(langHelper.bookTooltip()) + .withCustomBookItem(ModItems.GUIDE_BOOK.getId()).withGenerateBookItem(false) + .withCraftingTexture(BiomancyMod.createRL("textures/gui/modonomicon/crafting_textures.png")) + .withBookContentTexture(BiomancyMod.createRL("textures/gui/modonomicon/book_content.png")) + .withBookOverviewTexture(BiomancyMod.createRL("textures/gui/modonomicon/book_overview.png")); - BookCategoryModel featuresCategory = makeFeaturesCategory(helper); + lang.add(book.getName(), "Biomancy Index"); + lang.add(book.getTooltip(), "A book to test Modonomicon features for Biomancy."); - BookModel demoBook = BookModel.create(GuideBookItem.GUIDE_BOOK_ID, helper.bookName()) - .withTooltip(helper.bookTooltip()) - .withCustomBookItem(ModItems.GUIDE_BOOK.getId()).withGenerateBookItem(false) - .withCategories(featuresCategory); + BookCategoryModel featuresCategory = makeFeaturesCategory(langHelper); - lang.add(demoBook.getName(), "[PH] Biomancy Index"); - lang.add(demoBook.getTooltip(), "[PH] A book to test Modonomicon features for Biomancy."); + book.withCategories(featuresCategory); - add(demoBook); + add(book); } - private BookCategoryModel makeFeaturesCategory(BookLangHelper helper) { - helper.category("features"); + private BookCategoryModel makeFeaturesCategory(BookLangHelper langHelper) { + langHelper.category("features"); - EntryLocationHelper entryHelper = ModonomiconAPI.get().getEntryLocationHelper(); - entryHelper.setMap( + EntryLocationHelper locationHelper = ModonomiconAPI.get().getEntryLocationHelper(); + locationHelper.setMap( + "_____________________", + "__________e_d________", "_____________________", - "__m______________d___", - "__________r__________", - "__c__________________", - "__________2___3___i__", - "__s_____e____________" + "__c_______p__________", + "__s__________________", + "_____________________" ); - var multiBlockEntry = makeMultiBlockEntry(helper, entryHelper, 'm'); + BookEntryModel primordialCradleRecipe = makeCradleEntry(langHelper, locationHelper.get('p')).build(); - var recipeEntry = makeRecipeEntry(helper, entryHelper, 'c'); + BookEntryModel spotlightTestEntry = makeSpotlightTestEntry(langHelper, locationHelper.get('s')).build(); - var spotlightEntry = makeSpotlightEntry(helper, entryHelper, 's') - .withParent(BookEntryParentModel.builder().withEntryId(recipeEntry.getId()).build()) + BookEntryModel fleshBlobEntry = makeFleshBlobEntry(langHelper, locationHelper.get('e')) + .withParent(BookEntryParentModel.builder().withEntryId(primordialCradleRecipe.getId()).build()) .build(); - var entityEntry = makeEntityEntry(helper, entryHelper, 'd'); - - BookCategoryModel categoryModel = BookCategoryModel.create(modLoc(helper.category), helper.categoryName()) - .withIcon("minecraft:nether_star") //the icon for the category. In this case we simply use an existing item. - .withEntries(multiBlockEntry) - .withEntries(recipeEntry) - .withEntries(spotlightEntry) - .withEntries(entityEntry); - lang.add(helper.categoryName(), "[PH] Features Category"); - - return categoryModel; - } - - private BookEntryModel makeRecipeEntry(BookLangHelper helper, EntryLocationHelper entryHelper, char location) { - helper.entry("recipe"); - - helper.page("intro"); - var introPage = BookTextPageModel.builder() - .withText(helper.pageText()) - .withTitle(helper.pageTitle()) + BookEntryModel decomposerEntry = makeDecomposerEntry(langHelper, locationHelper.get('d')) + .withParent(BookEntryParentModel.builder().withEntryId(fleshBlobEntry.getId()).build()) .build(); - lang.add(helper.pageTitle(), "[PH] Recipe Entry"); - lang.add(helper.pageText(), "[PH] Recipe pages allow to show recipes in the book."); - - helper.page("crafting"); - var crafting = BookCraftingRecipePageModel.builder() - .withRecipeId1("minecraft:crafting_table") - .withRecipeId2("minecraft:oak_planks") - .withText(helper.pageText()) - .withTitle2("test.test.test") - .build(); - lang.add(helper.pageText(), "[PH] A sample recipe page."); - lang.add("test.test.test", "[PH] Book of Binding: Afrit (Bound)"); + BookCategoryModel category = BookCategoryModel.create(modLoc(langHelper.category), langHelper.categoryName()) + .withIcon(ModItems.LIVING_FLESH.get()) + .withEntryTextures(BiomancyMod.createRL("textures/gui/modonomicon/entry_textures.png")) + .withBackground(BiomancyMod.createRL("textures/gui/modonomicon/main_background.png")) + .withEntries(primordialCradleRecipe, decomposerEntry, fleshBlobEntry) + .withEntries(spotlightTestEntry); + lang.add(langHelper.categoryName(), "Fleshy Constructs"); - helper.page("smelting"); - var smelting = BookSmeltingRecipePageModel.builder() - .withRecipeId1("minecraft:charcoal") - .withRecipeId2("minecraft:cooked_beef") - .build(); - lang.add(helper.pageText(), "[PH] A smelting recipe page with one recipe and some text."); - - helper.page("blasting"); - var blasting = BookBlastingRecipePageModel.builder() - .withRecipeId2("biomancy:glass_pane_from_blasting") - .build(); - - BookEntryModel entryModel = BookEntryModel.builder() - .withId(entryId(helper)) - .withName(helper.entryName()) - .withDescription(helper.entryDescription()) - .withIcon("minecraft:crafting_table") - .withLocation(entryHelper.get('c')) - .withPages(introPage, crafting, smelting, blasting) - .build(); - lang.add(helper.entryName(), "[PH] Recipe Entry"); - lang.add(helper.entryDescription(), "[PH] An entry showcasing recipe pages."); - - return entryModel; + return category; } - private BookEntryModel.Builder makeSpotlightEntry(BookLangHelper helper, EntryLocationHelper entryHelper, char location) { - helper.entry("spotlight"); + private BookEntryModel.Builder makeSpotlightTestEntry(BookLangHelper langHelper, Vec2 location) { + langHelper.entry("spotlight"); - helper.page("intro"); + langHelper.page("intro"); var introPage = BookTextPageModel.builder() - .withText(helper.pageText()) - .withTitle(helper.pageTitle()) + .withText(langHelper.pageText()) + .withTitle(langHelper.pageTitle()) .build(); - lang.add(helper.pageTitle(), "[PH] Spotlight Entry"); - lang.add(helper.pageText(), "[PH] Spotlight pages allow to show items (actually, ingredients)."); + lang.add(langHelper.pageTitle(), "[PH] Spotlight Entry"); + lang.add(langHelper.pageText(), "[PH] Spotlight pages allow to show items (actually, ingredients)."); - helper.page("spotlight1"); + langHelper.page("spotlight1"); var spotlight1 = BookSpotlightPageModel.builder() - .withTitle(helper.pageTitle()) - .withText(helper.pageText()) + .withTitle(langHelper.pageTitle()) + .withText(langHelper.pageText()) .withItem(Ingredient.of(Items.APPLE)) .build(); - lang.add(helper.pageTitle(), "[PH] Custom Title"); - lang.add(helper.pageText(), "[PH] A sample spotlight page with custom title."); + lang.add(langHelper.pageTitle(), "[PH] Custom Title"); + lang.add(langHelper.pageText(), "[PH] A sample spotlight page with custom title."); - helper.page("spotlight2"); + langHelper.page("spotlight2"); var spotlight2 = BookSpotlightPageModel.builder() - .withText(helper.pageText()) + .withText(langHelper.pageText()) .withItem(Ingredient.of(Items.DIAMOND)) .build(); - lang.add(helper.pageText(), "[PH] A sample spotlight page with automatic title."); - - BookEntryModel.Builder builder = BookEntryModel.builder() - .withId(entryId(helper)) - .withName(helper.entryName()) - .withDescription(helper.entryDescription()) - .withIcon("minecraft:beacon") - .withLocation(entryHelper.get(location)) + lang.add(langHelper.pageText(), "[PH] A sample spotlight page with automatic title."); + + var builder = BookEntryModel.builder() + .withId(entryId(langHelper)) + .withName(langHelper.entryName()) + .withDescription(langHelper.entryDescription()) + .withIcon(Items.BEACON) + .withLocation(location) .withPages(introPage, spotlight1, spotlight2); - lang.add(helper.entryName(), "[PH] Spotlight Entry"); - lang.add(helper.entryDescription(), "[PH] An entry showcasing spotlight pages."); + + lang.add(langHelper.entryName(), "[PH] Spotlight Entry"); + lang.add(langHelper.entryDescription(), "[PH] An entry showcasing spotlight pages."); + return builder; } - private BookEntryModel makeEntityEntry(BookLangHelper helper, EntryLocationHelper entryHelper, char location) { - helper.entry("entity"); + private BookEntryModel.Builder makeFleshBlobEntry(BookLangHelper langHelper, Vec2 location) { + langHelper.entry("mob"); - helper.page("intro"); - BookTextPageModel introPage = BookTextPageModel.builder() - .withText(helper.pageText()) - .withTitle(helper.pageTitle()) + langHelper.page("living_flesh_spotlight"); + var livingFleshSpotlight = BookSpotlightPageModel.builder() + .withText(langHelper.pageText()) + .withItem(Ingredient.of(ModItems.LIVING_FLESH.get())) .build(); - lang.add(helper.pageTitle(), "[PH] Entity Entry"); - lang.add(helper.pageText(), "[PH] Entity pages allow to show entities."); + lang.add(langHelper.pageText(), """ + Living Flesh is the remains of a Flesh Blob after is has been killed. + \\ + It's most definitely alive, although it lacks any real intelligence or selfish will. + \\ + ++That isn't necessarily a bad thing though...++"""); - helper.page("flesh_blob"); + langHelper.page("flesh_blob"); BookEntityPageModel fleshBlobPage = BookEntityPageModel.builder() - .withEntityName(helper.pageTitle()) .withEntityId(ModEntityTypes.FLESH_BLOB.getId().toString()) .withScale(1f) .build(); - lang.add(helper.pageTitle(), "[PH] Flesh Blob"); - helper.page("hungry_flesh_blob"); + langHelper.page("flesh_blob_page"); + BookTextPageModel fleshBlobText = BookTextPageModel.builder() + .withText(langHelper.pageText()) + .build(); + lang.add(langHelper.pageText(), """ + A regular Flesh Blob is formed with just typical raw meat and healing agents. + \\ + Has no redeeming qualities, but makes for a good house pet."""); + langHelper.page("hungry_flesh_blob"); BookEntityPageModel hungryFleshBlobPage = BookEntityPageModel.builder() - .withText(helper.pageText()) .withEntityId(ModEntityTypes.HUNGRY_FLESH_BLOB.getId().toString()) .withScale(1f) .build(); - lang.add(helper.pageText(), "[PH] A sample entity page with automatic title."); - - BookEntryModel entryModel = BookEntryModel.builder() - .withId(entryId(helper)) - .withName(helper.entryName()) - .withDescription(helper.entryDescription()) - .withIcon("minecraft:spider_eye") - .withLocation(entryHelper.get(location)) - .withPages(introPage, fleshBlobPage, hungryFleshBlobPage) - .build(); - lang.add(helper.entryName(), "[PH] Entity Entry"); - lang.add(helper.entryDescription(), "[PH] An entry showcasing entity pages."); - return entryModel; + langHelper.page("hungry_flesh_blob_text"); + BookTextPageModel hungryFleshBlobText = BookTextPageModel.builder() + .withText(langHelper.pageText()) + .build(); + lang.add(langHelper.pageText(), """ + A Hungry Flesh Blob is formed by adding a few Sharp Fangs into the cradle with some raw meat and a healing agent. + \\ + Maybe don't try petting this one..."""); + + var builder = BookEntryModel.builder() + .withId(entryId(langHelper)) + .withName(langHelper.entryName()) + .withDescription(langHelper.entryDescription()) + .withIcon(ModItems.LIVING_FLESH.get()) + .withLocation(location) + .withPages( + fleshBlobPage, fleshBlobText, + hungryFleshBlobPage, hungryFleshBlobText, + livingFleshSpotlight + ); + lang.add(langHelper.entryName(), "Flesh Blobs"); + lang.add(langHelper.entryDescription(), "Bouncy lil guys"); + + return builder; } - private BookEntryModel makeMultiBlockEntry(BookLangHelper helper, EntryLocationHelper entryHelper, char location) { - helper.entry("multiblock"); + private BookEntryModel.Builder makeDecomposerEntry(BookLangHelper langHelper, Vec2 location) { + langHelper.entry("decomposer"); - helper.page("intro"); - var introPage = BookTextPageModel.builder() - .withText(helper.pageText()) - .withTitle(helper.pageTitle()) + langHelper.page("spotlight"); + var introPage = BookSpotlightPageModel.builder() + .withText(langHelper.pageText()) + .withItem(Ingredient.of(ModItems.DECOMPOSER.get())) .build(); - lang.add(helper.pageTitle(), "[PH] Multi-block Page"); - lang.add(helper.pageText(), "[PH] Multi-block pages allow to preview multi-blocks both in the book and in the world."); - - helper.page("preview"); - var multiBlockPreviewPage = BookMultiblockPageModel.builder() - .withMultiblockId("modonomicon:blockentity") //sample multi-block from modonomicon - .withMultiblockName("multiblocks.modonomicon.blockentity") //and the lang key for its name - .withText(helper.pageText()) + lang.add(langHelper.pageText(), "By giving a Living Flesh some more meat, a few Sharp Fangs, and a Bile Gland, you make a creature that will chew up items and give you useful components for the Bio-Forge"); + + langHelper.page("crafting"); + var crafting = BookCraftingRecipePageModel.builder() + .withRecipeId1("biomancy:decomposer") .build(); - lang.add("multiblocks.modonomicon.blockentity", "[PH] Blockentity Multi-Block."); - lang.add(helper.pageText(), "[PH] A sample multi-block."); - - BookEntryModel entryModel = BookEntryModel.builder() - .withId(entryId(helper)) - .withName(helper.entryName()) - .withDescription(helper.entryDescription()) - .withIcon("minecraft:furnace") //we use furnace as icon - .withLocation(entryHelper.get(location)) //and we place it at the location we defined earlier in the entry helper mapping - .withPages(introPage, multiBlockPreviewPage) //finally we add our pages to the entry + + var builder = BookEntryModel.builder() + .withId(entryId(langHelper)) + .withName(langHelper.entryName()) + .withDescription(langHelper.entryDescription()) + .withIcon(ModItems.DECOMPOSER.get()) + .withLocation(location) + .withPages(introPage, crafting); + lang.add(langHelper.entryName(), "Decomposer"); + lang.add(langHelper.entryDescription(), "Munch, munch!"); + + return builder; + } + + private BookEntryModel.Builder makeCradleEntry(BookLangHelper langHelper, Vec2 location) { + langHelper.entry("primordial_cradle"); + + langHelper.page("intro"); + var introPage = BookTextPageModel.builder() + .withText(langHelper.pageText()) + .withTitle(langHelper.pageTitle()) .build(); - lang.add(helper.entryName(), "[PH] Multi-block Entry"); - lang.add(helper.entryDescription(), "[PH] An entry showcasing a multi-block."); + lang.add(langHelper.pageTitle(), "The Primordial Cradle"); + lang.add(langHelper.pageText(), "By filling the cradle with raw flesh and a healing agent (Instant Health Potions, Healing Additive, or Regenerative Fluid) you gain the ability to form new living beings"); + + langHelper.page("crafting"); + var crafting = BookCraftingRecipePageModel.builder() + .withRecipeId1("biomancy:primordial_core") + .withRecipeId2("biomancy:primordial_cradle") + .withText(langHelper.pageText()) + .build(); + lang.add(langHelper.pageText(), "Primordial Cradle Recipe"); + + var builder = BookEntryModel.builder() + .withId(entryId(langHelper)) + .withName(langHelper.entryName()) + .withDescription(langHelper.entryDescription()) + .withIcon(ModItems.PRIMORDIAL_CRADLE.get()) + .withLocation(location) + .withPages(introPage, crafting); + lang.add(langHelper.entryName(), "The Primordial Cradle"); + lang.add(langHelper.entryDescription(), "The Fun Begins"); - return entryModel; + return builder; } } diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/particles/ModParticleSpriteProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/particles/ModParticleSpriteProvider.java index e908877f4..99b353d1f 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/particles/ModParticleSpriteProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/particles/ModParticleSpriteProvider.java @@ -6,6 +6,7 @@ import net.minecraftforge.common.data.ExistingFileHelper; public class ModParticleSpriteProvider extends ParticleSpriteProvider { + public ModParticleSpriteProvider(DataGenerator generator, ExistingFileHelper fileHelper) { super(generator, BiomancyMod.MOD_ID, fileHelper); } @@ -16,6 +17,9 @@ public void registerParticles() { addParticle(ModParticleTypes.FALLING_BLOOD, "minecraft:drip_fall"); addParticle(ModParticleTypes.LANDING_BLOOD, "minecraft:drip_land"); addParticle(ModParticleTypes.CORROSIVE_SWIPE_ATTACK, 8, 1); + addParticle(ModParticleTypes.DRIPPING_ACID, "minecraft:drip_hang"); + addParticle(ModParticleTypes.FALLING_ACID, "minecraft:drip_fall"); + addParticle(ModParticleTypes.LANDING_ACID, "minecraft:drip_land"); } } diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/BioForgeRecipeBuilder.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/BioForgeRecipeBuilder.java index 4e1d6849d..ca575c5d4 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/BioForgeRecipeBuilder.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/BioForgeRecipeBuilder.java @@ -1,11 +1,11 @@ package com.github.elenterius.biomancy.datagen.recipes; import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.crafting.recipe.DecomposerRecipe; +import com.github.elenterius.biomancy.crafting.recipe.IngredientStack; import com.github.elenterius.biomancy.init.ModBioForgeTabs; import com.github.elenterius.biomancy.init.ModRecipes; import com.github.elenterius.biomancy.menu.BioForgeTab; -import com.github.elenterius.biomancy.recipe.DecomposerRecipe; -import com.github.elenterius.biomancy.recipe.IngredientStack; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import net.minecraft.advancements.Advancement; diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/BioLabRecipeBuilder.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/BioLabRecipeBuilder.java index 42dba5415..d2a81ddcf 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/BioLabRecipeBuilder.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/BioLabRecipeBuilder.java @@ -1,10 +1,10 @@ package com.github.elenterius.biomancy.datagen.recipes; import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.crafting.recipe.BioLabRecipe; +import com.github.elenterius.biomancy.crafting.recipe.IngredientStack; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; -import com.github.elenterius.biomancy.recipe.BioLabRecipe; -import com.github.elenterius.biomancy.recipe.IngredientStack; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import net.minecraft.advancements.Advancement; diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/DecomposerRecipeBuilder.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/DecomposerRecipeBuilder.java index 3298195c0..d45a6e759 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/DecomposerRecipeBuilder.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/DecomposerRecipeBuilder.java @@ -1,12 +1,12 @@ package com.github.elenterius.biomancy.datagen.recipes; import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.crafting.recipe.DecomposerRecipe; +import com.github.elenterius.biomancy.crafting.recipe.IngredientStack; +import com.github.elenterius.biomancy.crafting.recipe.ItemCountRange; +import com.github.elenterius.biomancy.crafting.recipe.VariableProductionOutput; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; -import com.github.elenterius.biomancy.recipe.DecomposerRecipe; -import com.github.elenterius.biomancy.recipe.IngredientStack; -import com.github.elenterius.biomancy.recipe.ItemCountRange; -import com.github.elenterius.biomancy.recipe.VariableProductionOutput; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import net.minecraft.advancements.Advancement; diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/DigesterRecipeBuilder.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/DigesterRecipeBuilder.java index f2e6cb37d..43f02badb 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/DigesterRecipeBuilder.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/DigesterRecipeBuilder.java @@ -1,9 +1,9 @@ package com.github.elenterius.biomancy.datagen.recipes; import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.crafting.recipe.DigesterRecipe; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; -import com.github.elenterius.biomancy.recipe.DigesterRecipe; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import net.minecraft.advancements.Advancement; diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/IRecipeBuilder.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/IRecipeBuilder.java index e8729cf2c..10b65a1ef 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/IRecipeBuilder.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/IRecipeBuilder.java @@ -46,6 +46,10 @@ private String getTagName(TagKey tag) { IRecipeBuilder unlockedBy(String name, CriterionTriggerInstance criterionTrigger); + default IRecipeBuilder unlockedBy(String name, ItemPredicate predicate) { + return unlockedBy(name, inventoryTrigger(predicate)); + } + default IRecipeBuilder unlockedBy(ItemLike itemLike, CriterionTriggerInstance criterionTrigger) { return unlockedBy("has_" + getItemName(itemLike), criterionTrigger); } diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/ModRecipeProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/ModRecipeProvider.java index 9f03ad716..e0c9ad66b 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/ModRecipeProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/recipes/ModRecipeProvider.java @@ -3,11 +3,12 @@ import com.github.alexthe666.alexsmobs.AlexsMobs; import com.github.alexthe666.alexsmobs.item.AMItemRegistry; import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.advancements.predicate.FoodItemPredicate; +import com.github.elenterius.biomancy.crafting.recipe.FoodNutritionIngredient; import com.github.elenterius.biomancy.init.ModBioForgeTabs; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.tags.ModItemTags; import com.github.elenterius.biomancy.item.weapon.RavenousClawsItem; -import com.github.elenterius.biomancy.recipe.FoodNutritionIngredient; import net.minecraft.advancements.critereon.InventoryChangeTrigger; import net.minecraft.advancements.critereon.ItemPredicate; import net.minecraft.data.DataGenerator; @@ -148,15 +149,6 @@ private void registerWorkbenchRecipes(Consumer consumer) { .pattern("MC ") .unlockedBy(hasName(ModItems.PRIMORDIAL_CORE.get()), has(ModItems.PRIMORDIAL_CORE.get())).save(consumer); - // WorkbenchRecipeBuilder.shapeless(ModItems.PRIMORDIAL_LIVING_OCULUS.get()) - // .requires(Items.SPIDER_EYE) - // .requires(ModTags.Items.RAW_MEATS) - // .requires(ModTags.Items.RAW_MEATS) - // .requires(ModItems.MOB_SINEW.get()) - // .requires(ModItems.MOB_MARROW.get()) - // .requires(ModItems.PRIMORDIAL_LIVING_FLESH.get()) - // .unlockedBy(hasName(ModItems.PRIMORDIAL_LIVING_FLESH.get()), has(ModItems.PRIMORDIAL_LIVING_FLESH.get())).save(consumer); - // // WorkbenchRecipeBuilder.shapeless(ModItems.GUIDE_BOOK.get()) // .requires(ModItems.MOB_SINEW.get()) // .requires(Items.BOOK) @@ -165,13 +157,6 @@ private void registerWorkbenchRecipes(Consumer consumer) { // .requires(ModItems.MOB_FANG.get()) // .unlockedBy(hasName(ModItems.PRIMORDIAL_LIVING_FLESH.get()), has(ModItems.PRIMORDIAL_LIVING_FLESH.get())).save(consumer); - // WorkbenchRecipeBuilder.shaped(ModItems.VIAL.get(), 8) - // .define('G', Tags.Items.GLASS).define('T', Items.CLAY_BALL) - // .pattern("GTG") - // .pattern("G G") - // .pattern(" G ") - // .unlockedBy(hasName(Items.GLASS), has(Tags.Items.GLASS)).save(consumer); - // machines //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// WorkbenchRecipeBuilder.shaped(ModItems.PRIMORDIAL_CRADLE.get()) @@ -322,13 +307,14 @@ private void registerWorkbenchRecipes(Consumer consumer) { stonecutterResultFromBase(consumer, ModItems.PRIMAL_FLESH_STAIRS.get(), ModItems.PRIMAL_FLESH_BLOCK.get()); stonecutterResultFromBase(consumer, ModItems.PRIMAL_FLESH_SLAB.get(), ModItems.PRIMAL_FLESH_BLOCK.get(), 2); - WorkbenchRecipeBuilder.shaped(ModItems.PRIMAL_FLESH_BLOCK.get(), 8) - .define('G', ModItems.WITHERING_OOZE.get()) - .define('F', ModItems.FLESH_BLOCK.get()) + WorkbenchRecipeBuilder.shaped(ModItems.PRIMAL_FLESH_BLOCK.get(), 4) + .define('S', ModItems.STONE_POWDER.get()) + .define('E', ModItems.EXOTIC_DUST.get()) + .define('F', ModItemTags.RAW_MEATS) .define('M', ModItems.MALIGNANT_FLESH_BLOCK.get()) + .pattern("SME") .pattern("MFM") - .pattern("FGF") - .pattern("MFM") + .pattern("EMS") .unlockedBy(hasName(ModItems.MALIGNANT_FLESH_BLOCK.get()), has(ModItems.MALIGNANT_FLESH_BLOCK.get())) .save(consumer); @@ -350,49 +336,51 @@ private void registerWorkbenchRecipes(Consumer consumer) { .save(consumer); WorkbenchRecipeBuilder.shaped(ModItems.PRIMORDIAL_BIO_LANTERN.get()) - .define('F', ModItems.YELLOW_BIO_LANTERN.get()) + .define('B', ModItems.BLOOMBERRY.get()) .define('V', ModItems.MALIGNANT_FLESH_VEINS.get()) - .pattern("VVV") - .pattern("VFV") - .pattern("VVV") - .unlockedBy(hasName(ModItems.MALIGNANT_FLESH_VEINS.get()), has(ModItems.MALIGNANT_FLESH_VEINS.get())) - .save(consumer, getConversionRecipeId(ModItems.PRIMORDIAL_BIO_LANTERN.get(), ModItems.YELLOW_BIO_LANTERN.get())); + .define('C', ModItems.TENDON_CHAIN.get()) + .pattern(" C ") + .pattern("VBV") + .pattern(" V ") + .unlockedBy(hasName(ModItems.BLOOMBERRY.get()), has(ModItems.BLOOMBERRY.get())) + .save(consumer); - WorkbenchRecipeBuilder.shaped(ModItems.PRIMORDIAL_BIO_LANTERN.get()) - .define('F', ModItems.BLUE_BIO_LANTERN.get()) + WorkbenchRecipeBuilder.shaped(ModItems.BLOOMLIGHT.get(), 4) + .define('B', ModItems.BLOOMBERRY.get()) .define('V', ModItems.MALIGNANT_FLESH_VEINS.get()) - .pattern("VVV") - .pattern("VFV") - .pattern("VVV") - .unlockedBy(hasName(ModItems.PRIMORDIAL_BIO_LANTERN.get()), has(ModItems.BLUE_BIO_LANTERN.get())) + .pattern("BVB") + .pattern("VBV") + .pattern("BVB") + .unlockedBy(hasName(ModItems.BLOOMBERRY.get()), has(ModItems.BLOOMBERRY.get())) .save(consumer); } private void registerDigestingRecipes(Consumer consumer) { + FoodItemPredicate foodItemPredicate = new FoodItemPredicate(); DigesterRecipeBuilder.create(ModItems.NUTRIENT_PASTE.get(), 1, "poor_food") .setIngredient(new FoodNutritionIngredient(0, 1)) - .unlockedBy(ModItemTags.POOR_BIOMASS).save(consumer); + .unlockedBy("has_food", foodItemPredicate).save(consumer); DigesterRecipeBuilder.create(ModItems.NUTRIENT_PASTE.get(), 2, "average_food") .setIngredient(new FoodNutritionIngredient(2, 3)) - .unlockedBy(ModItemTags.AVERAGE_BIOMASS).save(consumer); + .unlockedBy("has_food", foodItemPredicate).save(consumer); DigesterRecipeBuilder.create(ModItems.NUTRIENT_PASTE.get(), 4, "good_food") .setIngredient(new FoodNutritionIngredient(4, 5)) - .unlockedBy(ModItemTags.GOOD_BIOMASS).save(consumer); + .unlockedBy("has_food", foodItemPredicate).save(consumer); DigesterRecipeBuilder.create(ModItems.NUTRIENT_PASTE.get(), 6, "superb_food") .setIngredient(new FoodNutritionIngredient(6, 7)) - .unlockedBy(ModItemTags.SUPERB_BIOMASS).save(consumer); + .unlockedBy("has_food", foodItemPredicate).save(consumer); DigesterRecipeBuilder.create(ModItems.NUTRIENT_PASTE.get(), 8, "excellent_food") .setIngredient(new FoodNutritionIngredient(8, 9)) - .unlockedBy(ModItemTags.SUPERB_BIOMASS).save(consumer); + .unlockedBy("has_food", foodItemPredicate).save(consumer); DigesterRecipeBuilder.create(ModItems.NUTRIENT_PASTE.get(), 10, "godly_food") .setIngredient(new FoodNutritionIngredient(10, Integer.MAX_VALUE)) - .unlockedBy(ModItemTags.SUPERB_BIOMASS).save(consumer); + .unlockedBy("has_food", foodItemPredicate).save(consumer); ////////////////////////// @@ -610,9 +598,23 @@ private void registerDecomposingRecipes(Consumer consumer) { .addOutput(ModItems.EXOTIC_DUST.get(), 0, 2) .unlockedBy(ModItems.LIVING_FLESH).save(consumer); + DecomposerRecipeBuilder.create().setIngredient(ModItems.BLOOMBERRY) + .addOutput(ModItems.BIO_LUMENS.get(), 0, 2) + .addOutput(ModItems.ORGANIC_MATTER.get(), 0, 1) + .addOutput(ModItems.EXOTIC_DUST.get(), 0, 3) + .addOutput(ModItems.BILE.get(), 1, 2) + .unlockedBy(ModItems.BLOOMBERRY).save(consumer); + DecomposerRecipeBuilder.create().setIngredient(ModItems.BLOOMLIGHT) + .addOutput(ModItems.BIO_LUMENS.get(), 5, 9) + .addOutput(ModItems.ORGANIC_MATTER.get(), 2, 3) + .addOutput(ModItems.EXOTIC_DUST.get(), 0, 4) + .addOutput(ModItems.BILE.get(), 1, 2) + .unlockedBy(Items.SHROOMLIGHT).save(consumer); + registerDecomposerRecipesFor119(consumer); registerDecomposerRecipesForBiomesOPlenty(consumer); registerDecomposerRecipesForAlexsMobs(consumer); + registerDecomposerRecipesForTetra(consumer); } private void registerDecomposerRecipesFor119(Consumer consumer) { @@ -628,6 +630,14 @@ private void registerDecomposerRecipesForBiomesOPlenty(Consumer DecomposerRecipeBuilder.create().ifModLoaded("biomesoplenty").setIngredient(new DatagenIngredient("biomesoplenty:flesh_tendons")).addOutput(ModItems.ELASTIC_FIBERS.get(), 4, 8).addOutput(ModItems.FLESH_BITS.get(), 1, 2).unlockedBy(ModItems.MOB_SINEW).save(consumer); } + private void registerDecomposerRecipesForTetra(Consumer consumer) { + DecomposerRecipeBuilder.create().ifModLoaded("tetra").setIngredient(new DatagenIngredient("tetra:dragon_sinew")) + .addOutput(ModItems.ELASTIC_FIBERS.get(), 4, 8) + .addOutput(ModItems.TOUGH_FIBERS.get(), 1, 2) + .addOutput(ModItems.EXOTIC_DUST.get(), 4, 8) + .unlockedBy(ModItems.MOB_SINEW).save(consumer); + } + private void registerDecomposerRecipesForAlexsMobs(Consumer consumer) { DecomposerRecipeBuilder.create().ifModLoaded(AlexsMobs.MODID) .setIngredient(AMItemRegistry.ROADRUNNER_FEATHER) @@ -947,6 +957,13 @@ private void registerBioForgeRecipes(Consumer consumer) { .setCategory(ModBioForgeTabs.MISC) .unlockedBy(ModItems.ELASTIC_FIBERS.get()).save(consumer); + BioForgeRecipeBuilder.create(ModItems.UNDEAD_PERMEABLE_MEMBRANE.get()) + .addIngredient(ModItems.ELASTIC_FIBERS.get(), 4) + .addIngredient(ModItems.BILE.get(), 4) + .addIngredient(Items.PHANTOM_MEMBRANE, 1) + .setCategory(ModBioForgeTabs.MISC) + .unlockedBy(Items.PHANTOM_MEMBRANE).save(consumer); + BioForgeRecipeBuilder.create(ModItems.FLESH_FENCE.get(), 4) .addIngredient(ModItems.FLESH_BITS.get(), fleshBlockCost) .addIngredient(ModItems.BONE_FRAGMENTS.get(), 2) diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ForgeEntityTypeTagsProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ForgeEntityTypeTagsProvider.java index c9b3899e7..c6ead5d9a 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ForgeEntityTypeTagsProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ForgeEntityTypeTagsProvider.java @@ -3,21 +3,32 @@ import com.github.elenterius.biomancy.init.tags.ModEntityTags; import net.minecraft.data.DataGenerator; import net.minecraft.data.tags.EntityTypeTagsProvider; +import net.minecraft.tags.TagKey; import net.minecraft.world.entity.EntityType; import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.registries.ForgeRegistries; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; public class ForgeEntityTypeTagsProvider extends EntityTypeTagsProvider { - public ForgeEntityTypeTagsProvider(DataGenerator pGenerator, @Nullable ExistingFileHelper existingFileHelper) { - super(pGenerator, "forge", existingFileHelper); + public ForgeEntityTypeTagsProvider(DataGenerator generator, @Nullable ExistingFileHelper existingFileHelper) { + super(generator, "forge", existingFileHelper); } @Override protected void addTags() { - tag(ModEntityTags.FORGE_BOSSES).add(EntityType.WITHER, EntityType.ENDER_DRAGON); + tag(ModEntityTags.FORGE_BOSSES) + .add(EntityType.WITHER, EntityType.ENDER_DRAGON); + + createTag(ModEntityTags.FORGE_GOLEMS) + .add(EntityType.IRON_GOLEM, EntityType.SNOW_GOLEM) + .addOptional("strawgolem:strawgolem", "strawgolem:strawnggolem"); + } + + protected EnhancedTagAppender> createTag(TagKey> tag) { + return new EnhancedTagAppender<>(tag(tag), ForgeRegistries.ENTITY_TYPES); } @Override diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ModBlockTagsProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ModBlockTagsProvider.java index 9b7dc9c9f..abb27e8dd 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ModBlockTagsProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ModBlockTagsProvider.java @@ -1,5 +1,7 @@ package com.github.elenterius.biomancy.datagen.tags; +import com.github.elenterius.biomancy.block.FleshDoorBlock; +import com.github.elenterius.biomancy.block.FullFleshDoorBlock; import com.github.elenterius.biomancy.init.ModBlockMaterials; import com.github.elenterius.biomancy.init.ModBlocks; import com.github.elenterius.biomancy.init.tags.ModBlockTags; @@ -10,13 +12,13 @@ import net.minecraft.tags.TagKey; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.predicate.BlockMaterialPredicate; import net.minecraftforge.common.Tags; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.registries.RegistryObject; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; +import java.util.function.Predicate; import static com.github.elenterius.biomancy.BiomancyMod.MOD_ID; @@ -39,21 +41,36 @@ public String getName() { protected void addTags() { addFleshyBlocksToHoeTag(); addCreateTags(); + addQuarkTags(); - //CONVERTABLE_TO_PRIMAL_FLESH - //CONVERTABLE_TO_MALIGNANT_FLESH - tag(ModBlockTags.PRIMORDIAL_ECO_SYSTEM_REPLACEABLE) + tag(ModBlockTags.FLESH_REPLACEABLE) .add(Blocks.CLAY).addTag(BlockTags.SAND).addTag(Tags.Blocks.GRAVEL) .add(Blocks.SNOW_BLOCK, Blocks.SNOW) .addTag(BlockTags.LEAVES) - .add( - Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.PODZOL, Blocks.GRASS_BLOCK, Blocks.DIRT_PATH, Blocks.FARMLAND, - Blocks.ROOTED_DIRT, Blocks.MUDDY_MANGROVE_ROOTS, Blocks.MUD - ); + .addTag(BlockTags.OVERWORLD_NATURAL_LOGS) + .addTag(BlockTags.DIRT) + .add(Blocks.DIRT_PATH, Blocks.FARMLAND, Blocks.MOSS_BLOCK, Blocks.VINE) + .add(Blocks.MELON, Blocks.PUMPKIN) + .addTag(BlockTags.FLOWERS); + + tag(ModBlockTags.ALLOW_VEINS_TO_ATTACH) + .add(Blocks.DIRT_PATH, Blocks.FARMLAND, Blocks.VINE); + + tag(ModBlockTags.DISALLOW_VEINS_TO_ATTACH).add( + ModBlocks.MALIGNANT_BLOOM.get(), + ModBlocks.PRIMAL_PERMEABLE_MEMBRANE.get() + ); + + tag(ModBlockTags.ACID_DESTRUCTIBLE) + .addTag(BlockTags.LEAVES) + .add(Blocks.MOSS_BLOCK, Blocks.VINE) + .addTag(BlockTags.FLOWERS); tag(BlockTags.DOORS).add(ModBlocks.FLESH_DOOR.get()).add(ModBlocks.FULL_FLESH_DOOR.get()); tag(BlockTags.TRAPDOORS).add(ModBlocks.FLESH_IRIS_DOOR.get()); + tag(Tags.Blocks.CHESTS).add(ModBlocks.FLESHKIN_CHEST.get()); + tag(ModBlockTags.FLESHY_FENCES).add(ModBlocks.FLESH_FENCE.get()); tag(BlockTags.FENCES).addTag(ModBlockTags.FLESHY_FENCES); tag(BlockTags.FENCE_GATES).add(ModBlocks.FLESH_FENCE_GATE.get()); @@ -86,14 +103,15 @@ protected void addTags() { tag(BlockTags.IMPERMEABLE).add( ModBlocks.IMPERMEABLE_MEMBRANE.get(), ModBlocks.BABY_PERMEABLE_MEMBRANE.get(), - ModBlocks.ADULT_PERMEABLE_MEMBRANE.get() + ModBlocks.ADULT_PERMEABLE_MEMBRANE.get(), + ModBlocks.PRIMAL_PERMEABLE_MEMBRANE.get(), + ModBlocks.UNDEAD_PERMEABLE_MEMBRANE.get() ); } private void addFleshyBlocksToHoeTag() { - BlockMaterialPredicate predicate = BlockMaterialPredicate.forMaterial(ModBlockMaterials.FLESH_MATERIAL); TagAppender tag = tag(BlockTags.MINEABLE_WITH_HOE); - ModBlocks.BLOCKS.getEntries().stream().map(RegistryObject::get).filter(block -> predicate.test(block.defaultBlockState())).forEach(tag::add); + ModBlocks.BLOCKS.getEntries().stream().map(RegistryObject::get).filter(ModBlockMaterials.FLESH_PREDICATE).forEach(tag::add); } /** @@ -110,4 +128,15 @@ private void addCreateTags() { ); } + /** + * Quark Tags + */ + private void addQuarkTags() { + String modId = "quark"; + + TagKey noDoubleDoor = tagKey(modId, "non_double_door"); + TagAppender tag = tag(noDoubleDoor); + Predicate predicate = block -> block instanceof FleshDoorBlock || block instanceof FullFleshDoorBlock; + ModBlocks.BLOCKS.getEntries().stream().map(RegistryObject::get).filter(predicate).forEach(tag::add); + } } diff --git a/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ModItemTagsProvider.java b/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ModItemTagsProvider.java index 51272d830..fd66763e6 100644 --- a/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ModItemTagsProvider.java +++ b/src/datagen/java/com/github/elenterius/biomancy/datagen/tags/ModItemTagsProvider.java @@ -10,7 +10,9 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.ItemTags; import net.minecraft.tags.TagKey; +import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraftforge.common.Tags; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.registries.ForgeRegistries; @@ -25,6 +27,10 @@ public ModItemTagsProvider(DataGenerator dataGenerator, BlockTagsProvider blockT super(dataGenerator, blockTagProvider, BiomancyMod.MOD_ID, existingFileHelper); } + private static TagKey forgeTag(String path) { + return ItemTags.create(new ResourceLocation("forge", path)); + } + @Override protected void addTags() { addBiomancyTags(); @@ -33,18 +39,12 @@ protected void addTags() { } private void addBiomancyTags() { + createTag(ModItemTags.SUGARS) .add(SUGAR, COOKIE, CAKE, HONEYCOMB, HONEY_BLOCK, HONEYCOMB_BLOCK, HONEY_BOTTLE, SWEET_BERRIES, COCOA_BEANS, APPLE) .addOptional("create:sweet_roll", "create:chocolate_glazed_berries", "create:honeyed_apple", "create:bar_of_chocolate") .addOptional("createaddition:chocolate_cake"); - createTag(ModItemTags.POOR_BIOMASS); - createTag(ModItemTags.AVERAGE_BIOMASS); - createTag(ModItemTags.GOOD_BIOMASS); - createTag(ModItemTags.SUPERB_BIOMASS); - createTag(ModItemTags.BIOMASS) - .addTag(ModItemTags.POOR_BIOMASS, ModItemTags.AVERAGE_BIOMASS, ModItemTags.GOOD_BIOMASS, ModItemTags.SUPERB_BIOMASS); - createTag(ModItemTags.RAW_MEATS) .add(BEEF, PORKCHOP, CHICKEN, RABBIT, MUTTON, COD, SALMON, TROPICAL_FISH, PUFFERFISH) .add(AMItemRegistry.MOOSE_RIBS.get(), AMItemRegistry.KANGAROO_MEAT.get(), AMItemRegistry.RAW_CATFISH.get(), AMItemRegistry.BLOBFISH.get(), AMItemRegistry.MAGGOT.get()) @@ -68,29 +68,49 @@ private void addBiomancyTags() { createTag(ModItemTags.FANGS) .add(ModItems.MOB_FANG.get()) .add(AMItemRegistry.BONE_SERPENT_TOOTH.get()); + + createTag(ModItemTags.CANNOT_BE_EATEN_BY_CRADLE) + .add(DRAGON_EGG, SPAWNER, HEART_OF_THE_SEA) + .add(NAME_TAG) + .addTag(ItemTags.MUSIC_DISCS) + .add(ELYTRA) + .addTag(Tags.Items.ARMORS, Tags.Items.TOOLS) + .addTag(Tags.Items.ORES_NETHERITE_SCRAP, Tags.Items.INGOTS_NETHERITE, Tags.Items.STORAGE_BLOCKS_NETHERITE) + .addTag(forgeTag("shulker_boxes")); } private void addMinecraftTags() { // tag(ItemTags.FENCES).getInternalBuilder().addTag(ModTags.Blocks.FLESHY_FENCES.getName(), BiomancyMod.MOD_ID); - } - private static TagKey forgeTag(String path) { - return ItemTags.create(new ResourceLocation("forge", path)); + createTag(ItemTags.DOORS) + .add(ModItems.FLESH_DOOR.get(), ModItems.FULL_FLESH_DOOR.get()); + + createTag(ItemTags.TRAPDOORS) + .add(ModItems.FLESH_IRIS_DOOR.get()); } private void addForgeTags() { // tag(ModItemTags.FORGE_TOOLS_KNIVES); TagKey clawsTag = forgeTag("tools/claws"); - tag(clawsTag) + createTag(clawsTag) .add(ModItems.RAVENOUS_CLAWS.get()); - tag(Tags.Items.TOOLS_SWORDS) + createTag(Tags.Items.TOOLS_SWORDS) .add(ModItems.DESPOIL_SICKLE.get(), ModItems.TOXICUS.get()); - tag(Tags.Items.TOOLS) + createTag(Tags.Items.TOOLS) .addTag(clawsTag) .add(ModItems.INJECTOR.get(), ModItems.BIO_EXTRACTOR.get()); + + createTag(Tags.Items.CHESTS).add(ModItems.FLESHKIN_CHEST.get()); + + EnhancedTagAppender shulkerBoxes = createTag(forgeTag("shulker_boxes")); + for (Item item : ForgeRegistries.ITEMS) { + if (item instanceof BlockItem blockItem && blockItem.getBlock() instanceof ShulkerBoxBlock) { + shulkerBoxes.add(item); + } + } } protected EnhancedTagAppender createTag(TagKey tag) { diff --git a/src/generated/resources/assets/biomancy/lang/en_us.json b/src/generated/resources/assets/biomancy/lang/en_us.json index cd361547e..45939d675 100644 --- a/src/generated/resources/assets/biomancy/lang/en_us.json +++ b/src/generated/resources/assets/biomancy/lang/en_us.json @@ -44,6 +44,8 @@ "item.biomancy.nutrient_paste.tooltip": "Nutrients combined with Biotic Matter to moisten the hard pellets, producing a paste.\nIt almost looks like yellowish cake, and is a convenient source of energy.", "item.biomancy.nutrient_bar": "Nutrient Bar", "item.biomancy.nutrient_bar.tooltip": "Nutrient Paste compressed into the shape of a bar. Looks edible, if a bit bland.", + "item.biomancy.sapberry": "Sapberry", + "item.biomancy.sapberry.tooltip": "An exotic delicacy which grants random potion effects.", "item.biomancy.regenerative_fluid": "Regenerative Fluid", "item.biomancy.regenerative_fluid.tooltip": "A fluid with regenerative properties, used to concoct healing additives.", "item.biomancy.withering_ooze": "Withering Ooze", @@ -76,6 +78,7 @@ "item.biomancy.fertilizer.tooltip": "Fertilizer that induces hyper-growth in plants, even for reeds, cactus, nether wart and chorus.", "item.biomancy.gift_sac": "Gift Sac", "item.biomancy.gift_sac.tooltip": "Looks like some objects are wrapped in an organic layer of skin. Might be filled with items or toxin if your language is set to German.\n\nRight Click the Sac to retrieve the Items.", + "item.biomancy.acid_bucket": "Acid Bucket", "item.biomancy.ravenous_claws": "Ravenous Claws", "item.biomancy.ravenous_claws.tooltip": "Extremely hungry and vicious Claws forged by starving living flesh and grafting claws onto it.\n\nRepair the famished claws by feeding them with food via the player inventory, as you would fill a bundle.\n\nKilling Mobs with these claws grants blood charges, which allow you to use the Awakened mode.", "item.biomancy.dev_arm_cannon": "[Dev Tool] Arm Cannon", @@ -197,6 +200,8 @@ "block.biomancy.bio_lantern_blue.tooltip": "A bioluminescent light source. This one is blue!", "block.biomancy.primordial_bio_lantern": "Primordial Bio-Lantern", "block.biomancy.primordial_bio_lantern.tooltip": "A primal light source. This one is magenta!", + "block.biomancy.bloomlight": "Bloomlight", + "block.biomancy.bloomlight.tooltip": "A malignant light source. This one is magenta as well!", "block.biomancy.tendon_chain": "Tendon Chain", "block.biomancy.tendon_chain.tooltip": "A chain made of tendons.", "block.biomancy.vial_holder": "Vial Holder", @@ -207,8 +212,12 @@ "block.biomancy.baby_permeable_membrane.tooltip": "Gelatinous-like membrane reinforced with elastic fibers.\n\nBaby mobs can diffuse through the membrane.", "block.biomancy.adult_permeable_membrane": "Adult-Permeable Membrane", "block.biomancy.adult_permeable_membrane.tooltip": "Gelatinous-like membrane reinforced with elastic fibers.\n\nAdult mobs can diffuse through the membrane.", - "block.biomancy.item_permeable_membrane": "Item-Permeable Membrane", - "block.biomancy.item_permeable_membrane.tooltip": "Gelatinous-like membrane reinforced with elastic fibers.\n\nItems can diffuse through the membrane.", + "block.biomancy.primal_permeable_membrane": "Primal Membrane", + "block.biomancy.primal_permeable_membrane.tooltip": "Gelatinous-like membrane reinforced with elastic fibers.\n\nOnly mobs that are alive can diffuse through the membrane.", + "block.biomancy.undead_permeable_membrane": "Phantom Membrane", + "block.biomancy.undead_permeable_membrane.tooltip": "Gelatinous-like membrane reinforced with elastic fibers.\n\nOnly mobs that are undead can diffuse through the membrane.", + "block.biomancy.neural_interceptor": "Neural Interceptor", + "block.biomancy.neural_interceptor.tooltip": "A psychic node that prevents natural mob spawning in a 48 block radius.", "block.biomancy.primal_flesh": "Primal Flesh Block", "block.biomancy.primal_flesh.tooltip": "Primitive and pure, you better not touch this with your dirty mitts.", "block.biomancy.primal_flesh_slab": "Primal Flesh Slab", @@ -227,6 +236,12 @@ "block.biomancy.malignant_flesh_wall.tooltip": "Wall of malignant flesh.\nIt's coming for you! ;)", "block.biomancy.malignant_flesh_veins": "Malignant Flesh Veins", "block.biomancy.malignant_flesh_veins.tooltip": "They look almost feral...\nyou better not touch them.", + "block.biomancy.malignant_bloom": "Malignant Bloom", + "block.biomancy.malignant_bloom.tooltip": "An exotic flower of primordial beauty.\n\nIt will spread itself by launching it's ripe berry into the air.\nOn impact the berry explodes and spreads malignant veins as well.", + "block.biomancy.primal_orifice": "Primal Orifice", + "block.biomancy.primal_orifice.tooltip": "A primitive piece full of holes. It seems to leak an acidic substance.", + "block.biomancy.acid_fluid_block": "Acid", + "fluid_type.biomancy.acid": "Acid", "entity.biomancy.hungry_flesh_blob": "Hungry Flesh Blob", "entity.biomancy.flesh_blob": "Flesh Blob", "entity.biomancy.legacy_flesh_blob": "Legacy Flesh Blob", @@ -257,6 +272,7 @@ "tooltip.biomancy.empty": "Empty", "tooltip.biomancy.contains": "Contains: %1$s", "tooltip.biomancy.nutrients_fuel": "Nutrients", + "tooltip.biomancy.primal_energy": "Primal Energy", "tooltip.biomancy.nutrients_consumes": "Consumes %1$s u", "tooltip.biomancy.consumption": "Consumption", "tooltip.biomancy.bile_fuel": "Bile", @@ -312,6 +328,11 @@ "sounds.biomancy.flesh_block.step": "Step on Fleshy Block", "sounds.biomancy.flesh_block.break": "Break Fleshy Block", "sounds.biomancy.flesh_block.fall": "Fall on Fleshy Block", + "sounds.biomancy.bony_flesh_block.hit": "Hit Bony Flesh Block", + "sounds.biomancy.bony_flesh_block.place": "Place Bony Flesh Block", + "sounds.biomancy.bony_flesh_block.step": "Step on Bony Flesh Block", + "sounds.biomancy.bony_flesh_block.break": "Break Bony Flesh Block", + "sounds.biomancy.bony_flesh_block.fall": "Fall on Bony Flesh Block", "sounds.biomancy.flesh_door.open": "Open Fleshy Door", "sounds.biomancy.flesh_door.close": "Close Fleshy Door", "sounds.biomancy.claws.attack.strong": "Strong Claw Attack", @@ -431,5 +452,28 @@ "advancements.biomancy.exotic_compounds.title": "Exotic Bio-Alchemy", "advancements.biomancy.exotic_compounds.description": "Combine organic things with exotic compounds to create cures and cleansing fluids.", "advancements.biomancy.genetic_compounds.title": "Genetic Bio-Alchemy", - "advancements.biomancy.genetic_compounds.description": "Combine organic things with genetic compounds to create fluids that influence growth and fertility." + "advancements.biomancy.genetic_compounds.description": "Combine organic things with genetic compounds to create fluids that influence growth and fertility.", + "book.biomancy.guide_book.name": "Biomancy Index", + "book.biomancy.guide_book.tooltip": "A book to test Modonomicon features for Biomancy.", + "book.biomancy.guide_book.features.primordial_cradle.intro.title": "The Primordial Cradle", + "book.biomancy.guide_book.features.primordial_cradle.intro.text": "By filling the cradle with raw flesh and a healing agent (Instant Health Potions, Healing Additive, or Regenerative Fluid) you gain the ability to form new living beings", + "book.biomancy.guide_book.features.primordial_cradle.crafting.text": "Primordial Cradle Recipe", + "book.biomancy.guide_book.features.primordial_cradle.name": "The Primordial Cradle", + "book.biomancy.guide_book.features.primordial_cradle.description": "The Fun Begins", + "book.biomancy.guide_book.features.spotlight.intro.title": "[PH] Spotlight Entry", + "book.biomancy.guide_book.features.spotlight.intro.text": "[PH] Spotlight pages allow to show items (actually, ingredients).", + "book.biomancy.guide_book.features.spotlight.spotlight1.title": "[PH] Custom Title", + "book.biomancy.guide_book.features.spotlight.spotlight1.text": "[PH] A sample spotlight page with custom title.", + "book.biomancy.guide_book.features.spotlight.spotlight2.text": "[PH] A sample spotlight page with automatic title.", + "book.biomancy.guide_book.features.spotlight.name": "[PH] Spotlight Entry", + "book.biomancy.guide_book.features.spotlight.description": "[PH] An entry showcasing spotlight pages.", + "book.biomancy.guide_book.features.mob.living_flesh_spotlight.text": "Living Flesh is the remains of a Flesh Blob after is has been killed.\n\\\nIt's most definitely alive, although it lacks any real intelligence or selfish will.\n\\\n++That isn't necessarily a bad thing though...++", + "book.biomancy.guide_book.features.mob.flesh_blob_page.text": "A regular Flesh Blob is formed with just typical raw meat and healing agents.\n\\\nHas no redeeming qualities, but makes for a good house pet.", + "book.biomancy.guide_book.features.mob.hungry_flesh_blob_text.text": "A Hungry Flesh Blob is formed by adding a few Sharp Fangs into the cradle with some raw meat and a healing agent.\n\\\nMaybe don't try petting this one...", + "book.biomancy.guide_book.features.mob.name": "Flesh Blobs", + "book.biomancy.guide_book.features.mob.description": "Bouncy lil guys", + "book.biomancy.guide_book.features.decomposer.spotlight.text": "By giving a Living Flesh some more meat, a few Sharp Fangs, and a Bile Gland, you make a creature that will chew up items and give you useful components for the Bio-Forge", + "book.biomancy.guide_book.features.decomposer.name": "Decomposer", + "book.biomancy.guide_book.features.decomposer.description": "Munch, munch!", + "book.biomancy.guide_book.features.name": "Fleshy Constructs" } \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/BiomancyConfig.java b/src/main/java/com/github/elenterius/biomancy/BiomancyConfig.java new file mode 100644 index 000000000..23c716b59 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/BiomancyConfig.java @@ -0,0 +1,26 @@ +package com.github.elenterius.biomancy; + +import com.github.elenterius.biomancy.config.ServerConfig; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.config.ModConfig; +import org.apache.commons.lang3.tuple.Pair; + +public final class BiomancyConfig { + + public static final ForgeConfigSpec SERVER_SPECIFICATION; + public static final ServerConfig SERVER; + + private BiomancyConfig() {} + + static { + Pair specPair = new ForgeConfigSpec.Builder().configure(ServerConfig::new); + SERVER = specPair.getLeft(); + SERVER_SPECIFICATION = specPair.getRight(); + } + + public static void register(ModLoadingContext modLoadingContext) { + modLoadingContext.registerConfig(ModConfig.Type.SERVER, BiomancyConfig.SERVER_SPECIFICATION); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/BiomancyMod.java b/src/main/java/com/github/elenterius/biomancy/BiomancyMod.java index 5f50ce94c..e8bbcbe1e 100644 --- a/src/main/java/com/github/elenterius/biomancy/BiomancyMod.java +++ b/src/main/java/com/github/elenterius/biomancy/BiomancyMod.java @@ -10,6 +10,7 @@ import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.EnchantmentInstance; import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.registries.RegistryObject; @@ -31,12 +32,17 @@ public BiomancyMod() { GeckoLib.initialize(); IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + ModLoadingContext modLoadingContext = ModLoadingContext.get(); ModBannerPatterns.BANNERS.register(modEventBus); ModBlocks.BLOCKS.register(modEventBus); ModItems.ITEMS.register(modEventBus); ModBlockEntities.BLOCK_ENTITIES.register(modEventBus); + + ModFluids.FLUID_TYPES.register(modEventBus); + ModFluids.FLUIDS.register(modEventBus); + ModEntityTypes.ENTITIES.register(modEventBus); ModAttributes.ATTRIBUTES.register(modEventBus); @@ -55,6 +61,7 @@ public BiomancyMod() { ModSoundEvents.SOUND_EVENTS.register(modEventBus); ModParticleTypes.PARTICLE_TYPES.register(modEventBus); + BiomancyConfig.register(modLoadingContext); ModsCompatHandler.onBiomancyInit(modEventBus); } diff --git a/src/main/java/com/github/elenterius/biomancy/advancements/predicate/FoodItemPredicate.java b/src/main/java/com/github/elenterius/biomancy/advancements/predicate/FoodItemPredicate.java new file mode 100644 index 000000000..e1f0f209f --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/advancements/predicate/FoodItemPredicate.java @@ -0,0 +1,33 @@ +package com.github.elenterius.biomancy.advancements.predicate; + +import com.github.elenterius.biomancy.BiomancyMod; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.advancements.critereon.ItemPredicate; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; + +public class FoodItemPredicate extends ItemPredicate { + + public static final ResourceLocation ID = BiomancyMod.createRL("is_food_item"); + + public FoodItemPredicate() {} + + @Override + public boolean matches(ItemStack stack) { + if (!stack.isEdible()) return false; + return stack.getFoodProperties(null) != null; + } + + @Override + public JsonElement serializeToJson() { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("type", ID.toString()); + return jsonObject; + } + + public static FoodItemPredicate deserializeFromJson(JsonObject jsonObject) { + return new FoodItemPredicate(); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/block/DirectionalSlabBlock.java b/src/main/java/com/github/elenterius/biomancy/block/DirectionalSlabBlock.java index 5739b0049..b7eb0259f 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/DirectionalSlabBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/DirectionalSlabBlock.java @@ -86,6 +86,19 @@ public BlockState getStateForPlacement(BlockPlaceContext context) { return defaultBlockState().setValue(TYPE, type).setValue(WATERLOGGED, isWaterlogged); } + public BlockState getStateForPlacement(BlockGetter level, BlockPos pos, Direction direction) { + BlockState state = level.getBlockState(pos); + boolean isWaterlogged = level.getFluidState(pos).getType() == Fluids.WATER; + + if (state.is(this) && state.getValue(TYPE) != DirectionalSlabType.FULL) { + return defaultBlockState().setValue(TYPE, DirectionalSlabType.FULL).setValue(WATERLOGGED, false); + } + + DirectionalSlabType type = DirectionalSlabType.getHalfFrom(direction); + + return defaultBlockState().setValue(TYPE, type).setValue(WATERLOGGED, isWaterlogged); + } + @Override public boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) { ItemStack stack = useContext.getItemInHand(); diff --git a/src/main/java/com/github/elenterius/biomancy/block/MembraneBlock.java b/src/main/java/com/github/elenterius/biomancy/block/MembraneBlock.java index 980aae6c6..b3ba09921 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/MembraneBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/MembraneBlock.java @@ -1,10 +1,12 @@ package com.github.elenterius.biomancy.block; +import com.github.elenterius.biomancy.init.tags.ModEntityTags; import net.minecraft.core.BlockPos; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobType; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; @@ -73,6 +75,8 @@ public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPo public interface IgnoreEntityCollisionPredicate { IgnoreEntityCollisionPredicate IS_BABY_MOB = (state, level, pos, entity) -> entity instanceof LivingEntity livingEntity && livingEntity.isBaby(); IgnoreEntityCollisionPredicate IS_ADULT_MOB = (state, level, pos, entity) -> entity instanceof LivingEntity livingEntity && !livingEntity.isBaby(); + IgnoreEntityCollisionPredicate IS_ALIVE_MOB = (state, level, pos, entity) -> entity instanceof LivingEntity livingEntity && !entity.getType().is(ModEntityTags.FORGE_GOLEMS) && livingEntity.getMobType() != MobType.UNDEAD; + IgnoreEntityCollisionPredicate IS_UNDEAD_MOB = (state, level, pos, entity) -> entity instanceof LivingEntity livingEntity && livingEntity.getMobType() == MobType.UNDEAD; IgnoreEntityCollisionPredicate IS_ITEM = (state, level, pos, entity) -> entity instanceof ItemEntity; IgnoreEntityCollisionPredicate NEVER = (state, level, pos, entity) -> false; diff --git a/src/main/java/com/github/elenterius/biomancy/block/SpreadingMembraneBlock.java b/src/main/java/com/github/elenterius/biomancy/block/SpreadingMembraneBlock.java new file mode 100644 index 000000000..4cc8319a3 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/block/SpreadingMembraneBlock.java @@ -0,0 +1,49 @@ +package com.github.elenterius.biomancy.block; + +import com.github.elenterius.biomancy.block.cradle.PrimalEnergyHandler; +import com.github.elenterius.biomancy.world.PrimordialEcosystem; +import com.github.elenterius.biomancy.world.mound.MoundShape; +import com.github.elenterius.biomancy.world.spatial.SpatialShapeManager; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class SpreadingMembraneBlock extends MembraneBlock { + + public SpreadingMembraneBlock(Properties properties, IgnoreEntityCollisionPredicate predicate) { + super(properties.randomTicks(), predicate); + } + + @Override + public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + if (level.random.nextFloat() >= 0.5f) return; + if (!level.isAreaLoaded(pos, 2)) return; + + BlockPos targetPos = pos.relative(Direction.getRandom(random)); + BlockState stateAtTargetPos = level.getBlockState(targetPos); + if (!stateAtTargetPos.isAir() && !PrimordialEcosystem.isReplaceable(stateAtTargetPos)) return; + + // boolean hasInvalidNeighborAtTargetPos = Direction.stream() + // .map(targetPos::relative) + // .filter(neighborPos -> !neighborPos.equals(pos)) + // .anyMatch(neighborPos -> PrimordialEcosystem.isReplaceable(level.getBlockState(neighborPos))); + // + // if (hasInvalidNeighborAtTargetPos) return; + + if (SpatialShapeManager.getClosestShape(level, pos, MoundShape.class::isInstance) instanceof MoundShape mound) { + BlockEntity blockEntity = level.getExistingBlockEntity(mound.getOrigin()); + if (blockEntity instanceof PrimalEnergyHandler energyHandler && !mound.hasChamberAt(targetPos)) { + + boolean isNeighborNextToAnyChamber = Direction.stream().anyMatch(direction -> mound.hasChamberAt(targetPos.relative(direction))); + + if (isNeighborNextToAnyChamber && energyHandler.drainPrimalEnergy(5) > 0) { + level.setBlock(targetPos, defaultBlockState(), UPDATE_CLIENTS); + } + } + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/block/bioforge/BioForgeBlock.java b/src/main/java/com/github/elenterius/biomancy/block/bioforge/BioForgeBlock.java index 9da85a3b7..57020c7f0 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/bioforge/BioForgeBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/bioforge/BioForgeBlock.java @@ -1,10 +1,10 @@ package com.github.elenterius.biomancy.block.bioforge; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.SoundUtil; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; @@ -15,6 +15,8 @@ import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; @@ -111,6 +113,11 @@ public void onRemove(BlockState state, Level level, BlockPos pos, BlockState new } } + @Override + public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) { + entity.causeFallDamage(fallDistance, 0.5f, DamageSource.FALL); + } + @Override public RenderShape getRenderShape(BlockState state) { return RenderShape.ENTITYBLOCK_ANIMATED; diff --git a/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabBlock.java b/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabBlock.java index 6a521b121..13db22297 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabBlock.java @@ -2,11 +2,11 @@ import com.github.elenterius.biomancy.block.HorizontalFacingMachineBlock; import com.github.elenterius.biomancy.block.entity.MachineBlockEntity; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.SoundUtil; import com.github.elenterius.biomancy.util.VoxelShapeUtil; import net.minecraft.ChatFormatting; diff --git a/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabBlockEntity.java b/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabBlockEntity.java index 15dc839cd..c9e55de1d 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabBlockEntity.java +++ b/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabBlockEntity.java @@ -3,6 +3,9 @@ import com.github.elenterius.biomancy.block.MachineBlock; import com.github.elenterius.biomancy.block.entity.MachineBlockEntity; import com.github.elenterius.biomancy.client.util.ClientLoopingSoundHelper; +import com.github.elenterius.biomancy.crafting.recipe.BioLabRecipe; +import com.github.elenterius.biomancy.crafting.recipe.IngredientStack; +import com.github.elenterius.biomancy.crafting.recipe.SimpleRecipeType; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModCapabilities; import com.github.elenterius.biomancy.init.ModRecipes; @@ -11,9 +14,6 @@ import com.github.elenterius.biomancy.inventory.SimpleInventory; import com.github.elenterius.biomancy.inventory.itemhandler.HandlerBehaviors; import com.github.elenterius.biomancy.menu.BioLabMenu; -import com.github.elenterius.biomancy.recipe.BioLabRecipe; -import com.github.elenterius.biomancy.recipe.IngredientStack; -import com.github.elenterius.biomancy.recipe.SimpleRecipeType; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.util.ILoopingSoundHelper; import com.github.elenterius.biomancy.util.SoundUtil; diff --git a/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabStateData.java b/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabStateData.java index 77bd80490..362757d1c 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabStateData.java +++ b/src/main/java/com/github/elenterius/biomancy/block/biolab/BioLabStateData.java @@ -1,16 +1,13 @@ package com.github.elenterius.biomancy.block.biolab; -import com.github.elenterius.biomancy.block.state.RecipeCraftingStateData; -import com.github.elenterius.biomancy.recipe.BioLabRecipe; +import com.github.elenterius.biomancy.crafting.recipe.BioLabRecipe; +import com.github.elenterius.biomancy.crafting.state.FuelConsumingRecipeCraftingStateData; import com.github.elenterius.biomancy.util.fuel.IFuelHandler; -public class BioLabStateData extends RecipeCraftingStateData { - - public static final int FUEL_INDEX = 2; - public final IFuelHandler fuelHandler; +public class BioLabStateData extends FuelConsumingRecipeCraftingStateData { public BioLabStateData(IFuelHandler fuelHandler) { - this.fuelHandler = fuelHandler; + super(fuelHandler); } @Override @@ -18,31 +15,4 @@ protected Class getRecipeType() { return BioLabRecipe.class; } - @Override - public int getFuelCost() { - return fuelHandler.getFuelCost(timeForCompletion); - } - - @Override - public int get(int index) { - validateIndex(index); - if (index == TIME_INDEX) return timeElapsed; - else if (index == TIME_FOR_COMPLETION_INDEX) return timeForCompletion; - else if (index == FUEL_INDEX) return fuelHandler.getFuelAmount(); - return 0; - } - - @Override - public void set(int index, int value) { - validateIndex(index); - if (index == TIME_INDEX) timeElapsed = value; - else if (index == TIME_FOR_COMPLETION_INDEX) timeForCompletion = value; - else if (index == FUEL_INDEX) fuelHandler.setFuelAmount(value); - } - - @Override - public int getCount() { - return 3; - } - } diff --git a/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimalEnergyHandler.java b/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimalEnergyHandler.java new file mode 100644 index 000000000..ebc979b80 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimalEnergyHandler.java @@ -0,0 +1,19 @@ +package com.github.elenterius.biomancy.block.cradle; + +public interface PrimalEnergyHandler { + + int getPrimalEnergy(); + + /** + * @param amount + * @return the amount that was successfully filled + */ + int fillPrimalEnergy(int amount); + + /** + * @param amount + * @return the amount that was successfully drained + */ + int drainPrimalEnergy(int amount); + +} diff --git a/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimordialCradleBlock.java b/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimordialCradleBlock.java index fda63e315..9c48601f3 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimordialCradleBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimordialCradleBlock.java @@ -1,14 +1,23 @@ package com.github.elenterius.biomancy.block.cradle; import com.github.elenterius.biomancy.block.fleshkinchest.FleshkinChestBlock; +import com.github.elenterius.biomancy.block.storagesac.StorageSacBlock; +import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.init.ModTriggers; +import com.github.elenterius.biomancy.init.tags.ModItemTags; import com.github.elenterius.biomancy.integration.ModsCompatHandler; +import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.SoundUtil; +import com.github.elenterius.biomancy.world.spatial.SpatialShapeManager; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; @@ -33,23 +42,28 @@ import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import net.minecraftforge.common.Tags; import net.minecraftforge.items.ItemHandlerHelper; import org.jetbrains.annotations.Nullable; +import java.text.DecimalFormat; +import java.util.List; import java.util.UUID; import java.util.function.Predicate; import java.util.stream.Stream; public class PrimordialCradleBlock extends HorizontalDirectionalBlock implements EntityBlock { - public static final Predicate EXPENSIVE_ITEMS = stack -> { - if (stack.is(Tags.Items.TOOLS)) return true; + public static final Predicate CANNOT_BE_SACRIFICED = stack -> { + if (stack.is(ModItemTags.CANNOT_BE_EATEN_BY_CRADLE)) return true; Item item = stack.getItem(); - if (item instanceof BlockItem blockItem && (blockItem.getBlock() instanceof ShulkerBoxBlock || blockItem.getBlock() instanceof FleshkinChestBlock)) - return true; + if (item instanceof BlockItem blockItem) { + Block block = blockItem.getBlock(); + if (block instanceof ShulkerBoxBlock || block instanceof FleshkinChestBlock || block instanceof StorageSacBlock) { + return true; + } + } if (ModsCompatHandler.getTetraHelper().isToolOrModularItem(item)) return true; @@ -78,6 +92,10 @@ protected static BlockEntityTicke return targetType == blockEntityType ? (BlockEntityTicker) entityTicker : null; } + public static int getPrimalEnergy(CompoundTag tag) { + return tag.contains(PrimordialCradleBlockEntity.PRIMAL_ENERGY_KEY) ? tag.getInt(PrimordialCradleBlockEntity.PRIMAL_ENERGY_KEY) : 0; + } + @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(FACING); @@ -95,6 +113,16 @@ public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return ModBlockEntities.PRIMORDIAL_CRADLE.get().create(pos, state); } + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { + if (!state.is(newState.getBlock())) { + if (level instanceof ServerLevel serverLevel) { + SpatialShapeManager.remove(serverLevel, pos); //removes mound shape from level + } + super.onRemove(state, level, pos, newState, isMoving); + } + } + @Nullable @Override public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { @@ -158,7 +186,7 @@ else if (stack.getItem() instanceof PotionItem) { private boolean increaseFillLevel(@Nullable Player player, Level level, BlockPos pos, ItemStack stack) { if (!stack.isEmpty() && !level.isClientSide()) { - if (EXPENSIVE_ITEMS.test(stack)) return false; + if (CANNOT_BE_SACRIFICED.test(stack)) return false; ItemStack copyOfStack = ItemHandlerHelper.copyStackWithSize(stack, 1); //creator#insertItem modifies the stack which may lead to it being empty if (level.getBlockEntity(pos) instanceof PrimordialCradleBlockEntity creator && !creator.isFull() && creator.insertItem(stack)) { @@ -200,4 +228,47 @@ public void animateTick(BlockState state, Level level, BlockPos pos, RandomSourc } } + @Override + public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, TooltipFlag flag) { + CompoundTag tag = BlockItem.getBlockEntityData(stack); + if (tag == null) return; + + tooltip.add(ComponentUtil.emptyLine()); + + DecimalFormat df = ClientTextUtil.getDecimalFormatter("#,###,###"); + + int primalEnergy = getPrimalEnergy(tag); + if (primalEnergy > 0) { + tooltip.add( + ComponentUtil.literal(df.format(primalEnergy)) + .withStyle(TextStyles.PRIMORDIAL_RUNES_LIGHT_GRAY) + .append(ComponentUtil.space()) + .append(ComponentUtil.translatable("tooltip.biomancy.primal_energy").withStyle(TextStyles.GRAY)) + ); + } + + if (tag.contains(PrimordialCradleBlockEntity.SACRIFICE_KEY)) { + CompoundTag sacrificeTag = tag.getCompound(PrimordialCradleBlockEntity.SACRIFICE_KEY); + byte biomass = sacrificeTag.getByte("Biomass"); + int lifeEnergy = sacrificeTag.getInt("LifeEnergy"); + int success = sacrificeTag.getInt("Success"); + int disease = sacrificeTag.getInt("Disease"); + int hostile = sacrificeTag.getInt("Hostile"); + int anomaly = sacrificeTag.getInt("Anomaly"); + + if (biomass > 0) tooltip.add(createValueComponent(df, biomass, "Biomass")); + if (lifeEnergy > 0) tooltip.add(createValueComponent(df, lifeEnergy, "Life Energy")); + if (success > 0) tooltip.add(createValueComponent(df, success, "Success")); + if (disease > 0) tooltip.add(createValueComponent(df, disease, "Disease")); + if (hostile > 0) tooltip.add(createValueComponent(df, hostile, "Hostile")); + if (anomaly > 0) tooltip.add(createValueComponent(df, anomaly, "Anomaly")); + } + } + + private static MutableComponent createValueComponent(DecimalFormat df, int value, String name) { + return ComponentUtil.literal(df.format(value)) + .withStyle(TextStyles.PRIMORDIAL_RUNES_LIGHT_GRAY) + .append(ComponentUtil.space()) + .append(ComponentUtil.literal(name).withStyle(TextStyles.GRAY)); + } } diff --git a/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimordialCradleBlockEntity.java b/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimordialCradleBlockEntity.java index 81c30c1f2..2d0a45b2b 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimordialCradleBlockEntity.java +++ b/src/main/java/com/github/elenterius/biomancy/block/cradle/PrimordialCradleBlockEntity.java @@ -1,12 +1,20 @@ package com.github.elenterius.biomancy.block.cradle; +import com.github.elenterius.biomancy.BiomancyConfig; import com.github.elenterius.biomancy.block.entity.SimpleSyncedBlockEntity; +import com.github.elenterius.biomancy.config.PrimalEnergySettings; import com.github.elenterius.biomancy.entity.fleshblob.FleshBlob; import com.github.elenterius.biomancy.init.*; import com.github.elenterius.biomancy.network.ISyncableAnimation; import com.github.elenterius.biomancy.network.ModNetworkHandler; +import com.github.elenterius.biomancy.tribute.Tribute; import com.github.elenterius.biomancy.util.SoundUtil; import com.github.elenterius.biomancy.world.PrimordialEcosystem; +import com.github.elenterius.biomancy.world.mound.MoundGenerator; +import com.github.elenterius.biomancy.world.mound.MoundShape; +import com.github.elenterius.biomancy.world.spatial.SpatialShapeManager; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import com.google.common.math.IntMath; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; @@ -23,6 +31,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; import software.bernie.geckolib3.core.AnimationState; import software.bernie.geckolib3.core.IAnimatable; import software.bernie.geckolib3.core.PlayState; @@ -35,19 +44,23 @@ import java.util.List; -public class PrimordialCradleBlockEntity extends SimpleSyncedBlockEntity implements IAnimatable, ISyncableAnimation { +public class PrimordialCradleBlockEntity extends SimpleSyncedBlockEntity implements PrimalEnergyHandler, IAnimatable, ISyncableAnimation { - public static final int DURATION_TICKS = 20 * 4; public static final String SACRIFICE_SYNC_KEY = "SyncSacrificeHandler"; public static final String SACRIFICE_KEY = "SacrificeHandler"; - public static final String PRIMAL_SPREAD_CHARGE_KEY = "PrimalSpreadCharge"; + public static final String PRIMAL_ENERGY_KEY = "PrimalEnergy"; + public static final String PROC_GEN_VALUES_KEY = "ProcGenValues"; + + public static final int DURATION_TICKS = 20 * 4; + protected static final AnimationBuilder IDLE_ANIM = new AnimationBuilder().addAnimation("cradle.idle"); protected static final AnimationBuilder SPIKE_ANIM = new AnimationBuilder().addAnimation("cradle.spike"); private final AnimationFactory animationFactory = GeckoLibUtil.createFactory(this); private final SacrificeHandler sacrificeHandler = new SacrificeHandler(); private boolean playAttackAnimation = false; private long ticks; - private int primalSpreadCharge; + private int primalEnergy; + private @Nullable MoundShape.ProcGenValues procGenValues; public PrimordialCradleBlockEntity(BlockPos pos, BlockState state) { super(ModBlockEntities.PRIMORDIAL_CRADLE.get(), pos, state); @@ -63,6 +76,27 @@ public static void serverTick(Level level, BlockPos pos, BlockState state, Primo } } + @Override + public void onLoad() { + super.onLoad(); + if (level instanceof ServerLevel serverLevel) { + Shape shape = SpatialShapeManager.getOrCreateShape(serverLevel, worldPosition, () -> { + if (procGenValues != null) { + return MoundGenerator.constructShape(worldPosition, procGenValues); + } + return MoundGenerator.constructShape(level, worldPosition, level.random.nextLong()); + }); + + if (shape instanceof MoundShape moundShape) { + MoundShape.ProcGenValues values = moundShape.getProcGenValues(); + if (!values.equals(procGenValues)) { + procGenValues = values; + setChangedSilent(); + } + } + } + } + /** * modifies the stack */ @@ -77,7 +111,7 @@ public boolean insertItem(ItemStack stack) { }); } - private void spawnTributeParticles(ServerLevel level, ITribute tribute) { + private void spawnTributeParticles(ServerLevel level, Tribute tribute) { BlockPos pos = getBlockPos(); if (tribute.successModifier() < 0) { @@ -141,11 +175,14 @@ protected void setChangedSilent() { public void onSacrifice(ServerLevel level) { BlockPos pos = getBlockPos(); + + float energyMultiplier = sacrificeHandler.getLifeEnergyPct(); + if (level.random.nextFloat() < sacrificeHandler.getSuccessChance()) { if (level.random.nextFloat() < sacrificeHandler.getAnomalyChance()) { spawnPrimordialFleshBlob(level, pos, sacrificeHandler); - primalSpreadCharge += 2048; + addPrimalEnergy(Math.round(4096 * energyMultiplier)); SoundUtil.broadcastBlockSound(level, pos, SoundEvents.FOX_SCREECH, 2f, 0.5f); } else { @@ -156,11 +193,11 @@ public void onSacrifice(ServerLevel level) { spawnFleshBlob(level, pos, sacrificeHandler); } - primalSpreadCharge += 512; + addPrimalEnergy(Math.round(2048 * energyMultiplier)); SoundUtil.broadcastBlockSound(level, pos, ModSoundEvents.CREATOR_SPAWN_MOB); } - if (level.random.nextFloat() >= sacrificeHandler.getTumorFactor() / 2) { + if (level.random.nextFloat() >= sacrificeHandler.getTumorFactor() / 3) { PrimordialEcosystem.tryToReplaceBlock(level, pos.below(), ModBlocks.PRIMAL_FLESH.get().defaultBlockState()); } @@ -172,22 +209,58 @@ public void onSacrifice(ServerLevel level) { if (!PrimordialEcosystem.spreadMalignantVeinsFromSource(level, pos, PrimordialEcosystem.MAX_CHARGE_SUPPLIER)) { PrimordialEcosystem.tryToReplaceBlock(level, pos.below(), ModBlocks.PRIMAL_FLESH.get().defaultBlockState()); } - - primalSpreadCharge += 1024; + addPrimalEnergy(Math.round(3072 * energyMultiplier)); SoundUtil.broadcastBlockSound(level, pos, ModSoundEvents.FLESH_BLOCK_STEP.get(), 1f, 0.15f + level.random.nextFloat() * 0.5f); } resetState(); } - public boolean consumePrimalSpreadCharge(ServerLevel level, int amount) { - if (amount <= 0) return false; - if (primalSpreadCharge < amount) return false; + @Override + public int getPrimalEnergy() { + PrimalEnergySettings.SupplyAmount supplyAmount = BiomancyConfig.SERVER.primalEnergySupplyOfCradle.get(); + if (supplyAmount == PrimalEnergySettings.SupplyAmount.UNLIMITED) return Integer.MAX_VALUE; + if (supplyAmount == PrimalEnergySettings.SupplyAmount.NONE) return 0; + + return primalEnergy; + } - primalSpreadCharge -= amount; + private void addPrimalEnergy(int amount) { + primalEnergy = IntMath.saturatedAdd(primalEnergy, amount); + } + + @Override + public int fillPrimalEnergy(int amount) { + if (amount <= 0) return 0; + + int prevPrimalEnergy = primalEnergy; + primalEnergy = IntMath.saturatedAdd(primalEnergy, amount); + int filled = primalEnergy - prevPrimalEnergy; + + setChanged(); + + return filled; + } + + @Override + public int drainPrimalEnergy(int amount) { + if (amount <= 0) return 0; + + PrimalEnergySettings.SupplyAmount supplyAmount = BiomancyConfig.SERVER.primalEnergySupplyOfCradle.get(); + if (supplyAmount == PrimalEnergySettings.SupplyAmount.UNLIMITED) return amount; + if (supplyAmount == PrimalEnergySettings.SupplyAmount.NONE) return 0; + + if (primalEnergy < amount) { + int prevPrimalEnergy = primalEnergy; + primalEnergy = 0; + setChanged(); + return prevPrimalEnergy; + } + + primalEnergy -= amount; setChanged(); - return true; + return amount; } public void spawnPrimordialFleshBlob(ServerLevel level, BlockPos pos, SacrificeHandler sacrificeHandler) { @@ -256,7 +329,13 @@ protected void attackAOE(ServerLevel level, BlockPos pos) { protected void saveAdditional(CompoundTag tag) { super.saveAdditional(tag); tag.put(SACRIFICE_KEY, sacrificeHandler.serializeNBT()); - tag.putInt(PRIMAL_SPREAD_CHARGE_KEY, primalSpreadCharge); + tag.putInt(PRIMAL_ENERGY_KEY, primalEnergy); + + if (procGenValues != null) { + CompoundTag tagProcGen = new CompoundTag(); + procGenValues.writeTo(tagProcGen); + tag.put(PROC_GEN_VALUES_KEY, tagProcGen); + } } @Override @@ -274,7 +353,11 @@ else if (tag.contains(SACRIFICE_SYNC_KEY)) { sacrificeHandler.deserializeNBT(tag.getCompound(SACRIFICE_SYNC_KEY)); } - primalSpreadCharge = tag.getInt(PRIMAL_SPREAD_CHARGE_KEY); + primalEnergy = tag.getInt(PRIMAL_ENERGY_KEY); + + if (tag.contains(PROC_GEN_VALUES_KEY)) { + procGenValues = MoundShape.ProcGenValues.readFrom(tag.getCompound(PROC_GEN_VALUES_KEY)); + } } @Override diff --git a/src/main/java/com/github/elenterius/biomancy/block/cradle/SacrificeHandler.java b/src/main/java/com/github/elenterius/biomancy/block/cradle/SacrificeHandler.java index bbf194dc5..389875945 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/cradle/SacrificeHandler.java +++ b/src/main/java/com/github/elenterius/biomancy/block/cradle/SacrificeHandler.java @@ -1,5 +1,8 @@ package com.github.elenterius.biomancy.block.cradle; +import com.github.elenterius.biomancy.tribute.Tribute; +import com.github.elenterius.biomancy.tribute.Tributes; +import com.google.common.math.IntMath; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; @@ -9,10 +12,10 @@ public class SacrificeHandler implements INBTSerializable { - private static final int MAX_VALUE = 100; + private static final int MAX_BIOMASS_VALUE = 100; private byte biomass; - private byte lifeEnergy; + private int lifeEnergy; private int successValue; private int diseaseValue; private int hostileValue; @@ -37,11 +40,11 @@ public void reset() { } public boolean isFull() { - return lifeEnergy >= MAX_VALUE && biomass >= MAX_VALUE; + return lifeEnergy >= MAX_BIOMASS_VALUE && biomass >= MAX_BIOMASS_VALUE; } public void setBiomass(int amount) { - biomass = (byte) Mth.clamp(amount, 0, MAX_VALUE); + biomass = (byte) Mth.clamp(amount, 0, MAX_BIOMASS_VALUE); } public boolean addBiomass(int amount) { @@ -52,7 +55,7 @@ public boolean addBiomass(int amount) { return true; } - if (amount > 0 && biomass < MAX_VALUE) { + if (amount > 0 && biomass < MAX_BIOMASS_VALUE) { setBiomass(biomass + amount); return true; } @@ -65,11 +68,11 @@ public int getBiomassAmount() { } public float getBiomassPct() { - return biomass / (float) MAX_VALUE; + return biomass / 100f; } public void setLifeEnergy(int amount) { - lifeEnergy = (byte) Mth.clamp(amount, 0, MAX_VALUE); + lifeEnergy = Mth.clamp(amount, 0, Integer.MAX_VALUE); } public boolean addLifeEnergy(int amount) { @@ -80,8 +83,8 @@ public boolean addLifeEnergy(int amount) { return true; } - if (amount > 0 && lifeEnergy < MAX_VALUE) { - setLifeEnergy(lifeEnergy + amount); + if (amount > 0) { + setLifeEnergy(IntMath.saturatedAdd(lifeEnergy, amount)); return true; } @@ -93,7 +96,7 @@ public int getLifeEnergyAmount() { } public float getLifeEnergyPct() { - return lifeEnergy / (float) MAX_VALUE; + return lifeEnergy / 100f; } public float getSuccessChance() { @@ -116,11 +119,11 @@ public boolean hasModifiers() { return hasModifiers; } - public boolean addItem(ItemStack stack, Consumer onSuccess) { + public boolean addItem(ItemStack stack, Consumer onSuccess) { if (isFull()) return false; if (stack.isEmpty()) return false; - ITribute tribute = Tributes.from(stack); + Tribute tribute = Tributes.from(stack); int count = addTribute(tribute, stack.getCount()); if (count > 0) { stack.shrink(count); @@ -144,13 +147,13 @@ public boolean addItem(ItemStack stack) { return false; } - private int addTribute(ITribute tribute, int maxCount) { + private int addTribute(Tribute tribute, int maxCount) { int n = maxCount; while (n > 0 && addTribute(tribute)) n--; return maxCount - n; } - public boolean addTribute(ITribute tribute) { + public boolean addTribute(Tribute tribute) { if (isFull()) return false; if (tribute.isEmpty()) return false; @@ -182,7 +185,7 @@ public boolean addTribute(ITribute tribute) { public CompoundTag serializeNBT() { CompoundTag tag = new CompoundTag(); tag.putByte("Biomass", biomass); - tag.putByte("LifeEnergy", lifeEnergy); + tag.putInt("LifeEnergy", lifeEnergy); tag.putInt("Success", successValue); @@ -197,7 +200,7 @@ public CompoundTag serializeNBT() { @Override public void deserializeNBT(CompoundTag tag) { biomass = tag.getByte("Biomass"); - lifeEnergy = tag.getByte("LifeEnergy"); + lifeEnergy = tag.getInt("LifeEnergy"); successValue = tag.getInt("Success"); diff --git a/src/main/java/com/github/elenterius/biomancy/block/cradle/Tributes.java b/src/main/java/com/github/elenterius/biomancy/block/cradle/Tributes.java deleted file mode 100644 index 24108c08c..000000000 --- a/src/main/java/com/github/elenterius/biomancy/block/cradle/Tributes.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.github.elenterius.biomancy.block.cradle; - -import com.github.elenterius.biomancy.init.ModItems; -import com.github.elenterius.biomancy.init.tags.ModItemTags; -import com.google.common.collect.ImmutableMap; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraftforge.common.Tags; - -import java.util.List; -import java.util.Map; -import java.util.function.Predicate; - -final class Tributes { - - static final Map ITEM_MAP = new ImmutableMap.Builder() - .put(ModItems.CREATOR_MIX.get(), Tribute.builder().biomass(20).lifeEnergy(20).successModifier(19).diseaseModifier(6).hostileModifier(-12).create()) - .put(ModItems.PRIMORDIAL_CORE.get(), Tribute.builder().biomass(80).successModifier(64).anomalyModifier(100).diseaseModifier(50).create()) - .put(ModItems.LIVING_FLESH.get(), Tribute.builder().biomass(10).lifeEnergy(10).successModifier(40).anomalyModifier(55).create()) - .put(Items.GOLDEN_APPLE, Tribute.builder().successModifier(10).hostileModifier(-100).create()) - .put(Items.ENCHANTED_GOLDEN_APPLE, Tribute.builder().lifeEnergy(15).successModifier(40).hostileModifier(-200).create()) - .put(Items.CAKE, Tribute.builder().hostileModifier(-80).diseaseModifier(10).create()) - - .put(ModItems.HEALING_ADDITIVE.get(), Tribute.builder().lifeEnergy(50).successModifier(1).diseaseModifier(-5).hostileModifier(-10).create()) - .put(ModItems.REGENERATIVE_FLUID.get(), Tribute.builder().lifeEnergy(5).hostileModifier(-1).create()) - - .put(Items.ROTTEN_FLESH, Tribute.builder().biomass(10).successModifier(-10).diseaseModifier(20).create()) - .put(ModItems.MOB_SINEW.get(), Tribute.builder().biomass(5).successModifier(2).hostileModifier(-2).create()) - .put(ModItems.FLESH_BITS.get(), Tribute.builder().biomass(5).successModifier(2).hostileModifier(-2).create()) - - .put(Items.RABBIT_FOOT, Tribute.builder().successModifier(1000).hostileModifier(-50).anomalyModifier(50).create()) - .put(Items.SPIDER_EYE, Tribute.builder().successModifier(10).diseaseModifier(10).hostileModifier(-5).create()) - .put(Items.FERMENTED_SPIDER_EYE, Tribute.builder().successModifier(-10).hostileModifier(-10).create()) - .put(ModItems.TOXIN_GLAND.get(), Tribute.builder().successModifier(-5).diseaseModifier(50).create()) - .put(ModItems.VOLATILE_GLAND.get(), Tribute.builder().successModifier(-5).diseaseModifier(20).create()) - .put(ModItems.GENERIC_MOB_GLAND.get(), Tribute.builder().diseaseModifier(-5).hostileModifier(-20).create()) - .put(Items.BONE, Tribute.builder().successModifier(3).diseaseModifier(-10).create()) - .put(Items.BONE_MEAL, Tribute.builder().successModifier(1).diseaseModifier(-1).create()) - .put(ModItems.MOB_MARROW.get(), Tribute.builder().successModifier(5).diseaseModifier(-20).hostileModifier(-10).create()) - .put(ModItems.WITHERED_MOB_MARROW.get(), Tribute.builder().successModifier(-30).diseaseModifier(-40).create()) - .put(ModItems.MOB_FANG.get(), Tribute.builder().successModifier(5).hostileModifier(5).create()) - .put(ModItems.MOB_CLAW.get(), Tribute.builder().successModifier(5).hostileModifier(5).create()) - .put(Items.ENDER_PEARL, Tribute.builder().hostileModifier(50).anomalyModifier(50).create()) - - .put(ModItems.ELASTIC_FIBERS.get(), Tribute.builder().diseaseModifier(1).anomalyModifier(1).create()) - .put(ModItems.TOUGH_FIBERS.get(), Tribute.builder().diseaseModifier(1).anomalyModifier(1).create()) - .build(); - - static final List FUZZY_TRIBUTES = List.of( - new FuzzyTribute(stack -> stack.is(ModItemTags.RAW_MEATS), Tribute.builder().biomass(20).successModifier(16).diseaseModifier(5).hostileModifier(-5).create()), - new FuzzyTribute(stack -> stack.is(ModItemTags.COOKED_MEATS), Tribute.builder().successModifier(-999).create()), - new FuzzyTribute(stack -> stack.is(Tags.Items.BONES), Tribute.builder().successModifier(5).diseaseModifier(-5).create()) - ); - static final ITribute INVALID_ITEM = Tribute.builder().successModifier(-99).diseaseModifier(5).hostileModifier(20).create(); - - private Tributes() {} - - static ITribute from(ItemStack stack) { - - MobEffectTribute mobEffectTribute = MobEffectTribute.from(stack); - ITribute tribute = findExistingTribute(stack); - - if (mobEffectTribute.isEmpty() && tribute.isEmpty()) { - return INVALID_ITEM; - } - - return new Tribute(tribute, mobEffectTribute); - } - - static ITribute findExistingTribute(ItemStack stack) { - if (stack.isEmpty()) return ITribute.EMPTY; - - ITribute foundTribute = ITEM_MAP.get(stack.getItem()); - if (foundTribute != null) return foundTribute; - - for (FuzzyTribute fuzzyTribute : FUZZY_TRIBUTES) { - if (fuzzyTribute.predicate.test(stack)) return fuzzyTribute.tribute; - } - - return ITribute.EMPTY; - } - - record FuzzyTribute(Predicate predicate, ITribute tribute) {} - -} diff --git a/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerBlock.java b/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerBlock.java index e50b14ed2..067aa7c47 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerBlock.java @@ -2,11 +2,11 @@ import com.github.elenterius.biomancy.block.HorizontalFacingMachineBlock; import com.github.elenterius.biomancy.block.entity.MachineBlockEntity; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.SoundUtil; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerBlockEntity.java b/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerBlockEntity.java index 6ac7c426e..a2ee09fd6 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerBlockEntity.java +++ b/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerBlockEntity.java @@ -3,6 +3,9 @@ import com.github.elenterius.biomancy.block.MachineBlock; import com.github.elenterius.biomancy.block.entity.MachineBlockEntity; import com.github.elenterius.biomancy.client.util.ClientLoopingSoundHelper; +import com.github.elenterius.biomancy.crafting.recipe.DecomposerRecipe; +import com.github.elenterius.biomancy.crafting.recipe.SimpleRecipeType; +import com.github.elenterius.biomancy.crafting.recipe.VariableProductionOutput; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModCapabilities; import com.github.elenterius.biomancy.init.ModRecipes; @@ -10,9 +13,6 @@ import com.github.elenterius.biomancy.inventory.BehavioralInventory; import com.github.elenterius.biomancy.inventory.itemhandler.HandlerBehaviors; import com.github.elenterius.biomancy.menu.DecomposerMenu; -import com.github.elenterius.biomancy.recipe.DecomposerRecipe; -import com.github.elenterius.biomancy.recipe.SimpleRecipeType; -import com.github.elenterius.biomancy.recipe.VariableProductionOutput; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.util.ILoopingSoundHelper; import com.github.elenterius.biomancy.util.SoundUtil; diff --git a/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerStateData.java b/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerStateData.java index cc77b33b5..47f37e7a5 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerStateData.java +++ b/src/main/java/com/github/elenterius/biomancy/block/decomposer/DecomposerStateData.java @@ -1,17 +1,13 @@ package com.github.elenterius.biomancy.block.decomposer; -import com.github.elenterius.biomancy.block.state.RecipeCraftingStateData; -import com.github.elenterius.biomancy.recipe.DecomposerRecipe; +import com.github.elenterius.biomancy.crafting.recipe.DecomposerRecipe; +import com.github.elenterius.biomancy.crafting.state.FuelConsumingRecipeCraftingStateData; import com.github.elenterius.biomancy.util.fuel.IFuelHandler; -public class DecomposerStateData extends RecipeCraftingStateData { - - public static final int FUEL_INDEX = 2; - - public final IFuelHandler fuelHandler; +public class DecomposerStateData extends FuelConsumingRecipeCraftingStateData { public DecomposerStateData(IFuelHandler fuelHandler) { - this.fuelHandler = fuelHandler; + super(fuelHandler); } @Override @@ -19,36 +15,4 @@ protected Class getRecipeType() { return DecomposerRecipe.class; } - @Override - public int getFuelCost() { - return fuelHandler.getFuelCost(timeForCompletion); - } - - @Override - public int get(int index) { - validateIndex(index); - return switch (index) { - case TIME_INDEX -> timeElapsed; - case TIME_FOR_COMPLETION_INDEX -> timeForCompletion; - case FUEL_INDEX -> fuelHandler.getFuelAmount(); - default -> 0; - }; - } - - @Override - public void set(int index, int value) { - validateIndex(index); - switch (index) { - case TIME_INDEX -> timeElapsed = value; - case TIME_FOR_COMPLETION_INDEX -> timeForCompletion = value; - case FUEL_INDEX -> fuelHandler.setFuelAmount(value); - default -> throw new IllegalStateException("Unexpected value: " + index); - } - } - - @Override - public int getCount() { - return 3; - } - } diff --git a/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterBlock.java b/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterBlock.java index 47abad31b..c5fddbc3b 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterBlock.java @@ -2,11 +2,11 @@ import com.github.elenterius.biomancy.block.HorizontalFacingMachineBlock; import com.github.elenterius.biomancy.block.entity.MachineBlockEntity; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.SoundUtil; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterBlockEntity.java b/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterBlockEntity.java index 1eb47b477..fc8f626ee 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterBlockEntity.java +++ b/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterBlockEntity.java @@ -3,6 +3,8 @@ import com.github.elenterius.biomancy.block.MachineBlock; import com.github.elenterius.biomancy.block.entity.MachineBlockEntity; import com.github.elenterius.biomancy.client.util.ClientLoopingSoundHelper; +import com.github.elenterius.biomancy.crafting.recipe.DigesterRecipe; +import com.github.elenterius.biomancy.crafting.recipe.SimpleRecipeType; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModCapabilities; import com.github.elenterius.biomancy.init.ModRecipes; @@ -10,8 +12,6 @@ import com.github.elenterius.biomancy.inventory.BehavioralInventory; import com.github.elenterius.biomancy.inventory.itemhandler.HandlerBehaviors; import com.github.elenterius.biomancy.menu.DigesterMenu; -import com.github.elenterius.biomancy.recipe.DigesterRecipe; -import com.github.elenterius.biomancy.recipe.SimpleRecipeType; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.util.ILoopingSoundHelper; import com.github.elenterius.biomancy.util.SoundUtil; diff --git a/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterStateData.java b/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterStateData.java index 1d9bbdf8d..fbaa7c938 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterStateData.java +++ b/src/main/java/com/github/elenterius/biomancy/block/digester/DigesterStateData.java @@ -1,17 +1,13 @@ package com.github.elenterius.biomancy.block.digester; -import com.github.elenterius.biomancy.block.state.RecipeCraftingStateData; -import com.github.elenterius.biomancy.recipe.DigesterRecipe; +import com.github.elenterius.biomancy.crafting.recipe.DigesterRecipe; +import com.github.elenterius.biomancy.crafting.state.FuelConsumingRecipeCraftingStateData; import com.github.elenterius.biomancy.util.fuel.IFuelHandler; -public class DigesterStateData extends RecipeCraftingStateData { - - public static final int FUEL_AMOUNT_INDEX = 2; - - public final IFuelHandler fuelHandler; +public class DigesterStateData extends FuelConsumingRecipeCraftingStateData { public DigesterStateData(IFuelHandler fuelHandler) { - this.fuelHandler = fuelHandler; + super(fuelHandler); } @Override @@ -19,36 +15,4 @@ protected Class getRecipeType() { return DigesterRecipe.class; } - @Override - public int getFuelCost() { - return fuelHandler.getFuelCost(timeForCompletion); - } - - @Override - public int get(int index) { - validateIndex(index); - return switch (index) { - case TIME_INDEX -> timeElapsed; - case TIME_FOR_COMPLETION_INDEX -> timeForCompletion; - case FUEL_AMOUNT_INDEX -> fuelHandler.getFuelAmount(); - default -> 0; - }; - } - - @Override - public void set(int index, int value) { - validateIndex(index); - switch (index) { - case TIME_INDEX -> timeElapsed = value; - case TIME_FOR_COMPLETION_INDEX -> timeForCompletion = value; - case FUEL_AMOUNT_INDEX -> fuelHandler.setFuelAmount(value); - default -> throw new IllegalStateException("Unexpected value: " + index); - } - } - - @Override - public int getCount() { - return 3; - } - } diff --git a/src/main/java/com/github/elenterius/biomancy/block/entity/MachineBlockEntity.java b/src/main/java/com/github/elenterius/biomancy/block/entity/MachineBlockEntity.java index 08dfc27d6..cbdd4469b 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/entity/MachineBlockEntity.java +++ b/src/main/java/com/github/elenterius/biomancy/block/entity/MachineBlockEntity.java @@ -2,10 +2,10 @@ import com.github.elenterius.biomancy.BiomancyMod; import com.github.elenterius.biomancy.block.MachineBlock; -import com.github.elenterius.biomancy.block.state.CraftingState; -import com.github.elenterius.biomancy.block.state.RecipeCraftingStateData; +import com.github.elenterius.biomancy.crafting.recipe.ProcessingRecipe; +import com.github.elenterius.biomancy.crafting.state.CraftingState; +import com.github.elenterius.biomancy.crafting.state.RecipeCraftingStateData; import com.github.elenterius.biomancy.init.ModBlockProperties; -import com.github.elenterius.biomancy.recipe.ProcessingRecipe; import com.github.elenterius.biomancy.util.fuel.IFuelHandler; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; diff --git a/src/main/java/com/github/elenterius/biomancy/block/fleshkinchest/FleshkinChestBlock.java b/src/main/java/com/github/elenterius/biomancy/block/fleshkinchest/FleshkinChestBlock.java index ecc34bb71..9ae2223ee 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/fleshkinchest/FleshkinChestBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/fleshkinchest/FleshkinChestBlock.java @@ -1,6 +1,5 @@ package com.github.elenterius.biomancy.block.fleshkinchest; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.init.ModBlockEntities; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.ownable.IOwnableEntityBlock; @@ -8,6 +7,7 @@ import com.github.elenterius.biomancy.permission.IRestrictedInteraction; import com.github.elenterius.biomancy.permission.UserType; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.Util; import net.minecraft.client.Minecraft; diff --git a/src/main/java/com/github/elenterius/biomancy/block/fleshspike/FleshSpikeBlock.java b/src/main/java/com/github/elenterius/biomancy/block/fleshspike/FleshSpikeBlock.java index 2a8a0df48..a0f110927 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/fleshspike/FleshSpikeBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/fleshspike/FleshSpikeBlock.java @@ -3,6 +3,7 @@ import com.github.elenterius.biomancy.block.base.WaterloggedFacingBlock; import com.github.elenterius.biomancy.init.ModBlockProperties; import com.github.elenterius.biomancy.init.ModDamageSources; +import com.github.elenterius.biomancy.util.EnhancedIntegerProperty; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; @@ -15,7 +16,6 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.CollisionContext; @@ -25,24 +25,22 @@ public class FleshSpikeBlock extends WaterloggedFacingBlock { - public static final int MIN_SPIKES = 1; - public static final int MAX_SPIKES = 3; - public static final IntegerProperty SPIKES = ModBlockProperties.SPIKES; + public static final EnhancedIntegerProperty SPIKES = ModBlockProperties.SPIKES; public FleshSpikeBlock(Properties properties) { super(properties); - registerDefaultState(defaultBlockState().setValue(SPIKES, MIN_SPIKES)); + registerDefaultState(defaultBlockState().setValue(SPIKES.get(), SPIKES.getMin())); FleshSpikeShapes.computePossibleShapes(stateDefinition.getPossibleStates()); } public static int getSpikes(BlockState blockState) { - return blockState.getValue(SPIKES); + return SPIKES.getValue(blockState); } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { super.createBlockStateDefinition(builder); - builder.add(SPIKES); + builder.add(SPIKES.get()); } @Nullable @@ -50,7 +48,7 @@ protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(AGE.get()); + } + + public BlockState getStateForPlacement(BlockGetter level, BlockPos pos, Direction direction) { + boolean isWaterlogged = level.getFluidState(pos).getType() == Fluids.WATER; + return defaultBlockState().setValue(FACING, direction).setValue(WATERLOGGED, isWaterlogged); + } + + public boolean hasUnobstructedAim(BlockGetter level, BlockPos pos, Direction direction) { + return hasUnobstructedAim(level, pos, pos.relative(direction, AIM_DISTANCE)); + } + + public boolean hasUnobstructedAim(BlockGetter level, BlockPos origin, BlockPos target) { + return level.clip(new ClipContext(Vec3.atCenterOf(origin), Vec3.atCenterOf(target), ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, null)).getType() == HitResult.Type.MISS; + } + + @Override + public boolean isRandomlyTicking(BlockState state) { + return true; + } + + @Override + public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + if (!level.isAreaLoaded(pos, 1)) return; + + Direction direction = getFacing(state); + BlockPos relativePos = pos.relative(direction); + BlockState relativeState = level.getBlockState(relativePos); + if (relativeState.getMaterial().isSolid() || !relativeState.getCollisionShape(level, relativePos).isEmpty()) return; + + int age = AGE.getValue(state); + if (age < AGE.getMax()) { + int growthSpeed = age < AGE.getMax() - 1 ? getGrowthSpeed(level, pos) : 1; + + if (ForgeHooks.onCropsGrowPre(level, pos, state, random.nextInt(growthSpeed) == 0)) { + level.setBlock(pos, AGE.addValue(state, 1), Block.UPDATE_CLIENTS); + ForgeHooks.onCropsGrowPost(level, pos, state); + } + } + else { + level.sendParticles(ParticleTypes.EXPLOSION, pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d, 1, 0, 0, 0, 0); + level.setBlock(pos, AGE.setValue(state, AGE.getMin()), Block.UPDATE_CLIENTS); + + int range = 6; + Vec3i plane = VectorUtil.axisAlignedPlane3i(direction); + int offsetX = plane.getX() * random.nextIntBetweenInclusive(-range, range); + int offsetY = plane.getY() * random.nextIntBetweenInclusive(-range, range); + int offsetZ = plane.getZ() * random.nextIntBetweenInclusive(-range, range); + BlockPos target = pos.relative(direction, AIM_DISTANCE).offset(offsetX, offsetY, offsetZ); + + ModProjectiles.SAPBERRY.shoot(level, Vec3.atCenterOf(pos), Vec3.atCenterOf(target)); + } + } + + @Override + public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + int age = AGE.getValue(state); + if (age > 5 && player.getItemInHand(hand).isEmpty()) { + if (!level.isClientSide) { + int count = 1 + (age > 6 ? level.random.nextInt(2) : 0); + popResource(level, pos, new ItemStack(ModItems.BLOOMBERRY.get(), count)); + + player.hurt(DamageSource.SWEET_BERRY_BUSH, 1f); + + level.playSound(null, pos, SoundEvents.CAVE_VINES_PICK_BERRIES, SoundSource.BLOCKS, 1f, 0.5f + level.random.nextFloat() * 0.4f); + BlockState blockState = AGE.setValue(state, AGE.getMin()); + level.setBlock(pos, blockState, Block.UPDATE_CLIENTS); + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, blockState)); + } + + return InteractionResult.sidedSuccess(level.isClientSide); + } + + return super.use(state, level, pos, player, hand, hit); + } + + @Override + public BlockState getPlant(BlockGetter level, BlockPos pos) { + BlockState state = level.getBlockState(pos); + if (state.getBlock() != this) return defaultBlockState(); + return state; + } + + @Override + public PlantType getPlantType(BlockGetter level, BlockPos pos) { + return ModPlantTypes.FLESH_PLANT_TYPE; + } + + @Override + public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos currentPos, BlockPos neighborPos) { + return !state.canSurvive(level, currentPos) ? Blocks.AIR.defaultBlockState() : super.updateShape(state, direction, neighborState, level, currentPos, neighborPos); + } + + public boolean mayPlaceOn(BlockGetter level, BlockPos pos, BlockState state) { + return state.is(ModBlocks.PRIMAL_FLESH.get()) || state.is(ModBlocks.MALIGNANT_FLESH.get()); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) { + Direction direction = state.getValue(FACING); + BlockPos blockPos = pos.relative(direction.getOpposite()); + + if (state.getBlock() == this) { + //world gen and placement + return level.getBlockState(blockPos).canSustainPlant(level, blockPos, direction, this); + } + + return mayPlaceOn(level, blockPos, level.getBlockState(blockPos)); + } + + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter level, BlockPos pos) { + return state.getFluidState().isEmpty(); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return MalignantBloomShapes.getBoundingShape(state); + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return hasCollision ? MalignantBloomShapes.getCollisionShape(state) : Shapes.empty(); + } + + @Override + public boolean isCollisionShapeFullBlock(BlockState state, BlockGetter level, BlockPos pos) { + return false; + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/block/malignantbloom/MalignantBloomShapes.java b/src/main/java/com/github/elenterius/biomancy/block/malignantbloom/MalignantBloomShapes.java new file mode 100644 index 000000000..bba50d125 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/block/malignantbloom/MalignantBloomShapes.java @@ -0,0 +1,65 @@ +package com.github.elenterius.biomancy.block.malignantbloom; + +import com.github.elenterius.biomancy.util.IntermediaryKeyCache; +import com.github.elenterius.biomancy.util.VoxelShapeUtil; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +import java.util.List; +import java.util.Objects; + +public final class MalignantBloomShapes { + + static final IntermediaryKeyCache CACHE = new IntermediaryKeyCache<>(MalignantBloomShapes::computeKey); + + private MalignantBloomShapes() {} + + static void computePossibleShapes(List possibleStates) { + possibleStates.forEach(possibleState -> CACHE.computeIfAbsent(possibleState, MalignantBloomShapes::computeShapes)); + } + + static VoxelShape getBoundingShape(BlockState blockState) { + return CACHE.get(blockState).boundingShape(); + } + + static VoxelShape getCollisionShape(BlockState blockState) { + return CACHE.get(blockState).collisionShape(); + } + + private static Integer computeKey(BlockState blockState) { + Direction direction = MalignantBloomBlock.getFacing(blockState); + int stage = MalignantBloomBlock.getStage(blockState); + + return Objects.hash(direction, stage); + } + + private static ComputedShapes computeShapes(BlockState blockState) { + Direction direction = MalignantBloomBlock.getFacing(blockState); + + int stage = MalignantBloomBlock.getStage(blockState); + + if (stage == 0) { + VoxelShape boundingShape = VoxelShapeUtil.createXZRotatedTowards(direction, 5, 0, 5, 11, 6, 11); + VoxelShape collisionShape = VoxelShapeUtil.createXZRotatedTowards(direction, 5, 0, 5, 11, 2, 11); + return new ComputedShapes(boundingShape, collisionShape); + } + + VoxelShape shape = switch (stage) { + case 1 -> Shapes.join( + VoxelShapeUtil.createXZRotatedTowards(direction, 5, 0, 5, 11, 2, 11), + VoxelShapeUtil.createXZRotatedTowards(direction, 4, 2, 4, 12, 10, 12), BooleanOp.OR); + case 2 -> Shapes.join( + VoxelShapeUtil.createXZRotatedTowards(direction, 5, 0, 5, 11, 2, 11), + VoxelShapeUtil.createXZRotatedTowards(direction, 2, 2, 2, 14, 15, 14), BooleanOp.OR); + case 3, 4 -> Shapes.block(); + default -> Shapes.empty(); + }; + + return new ComputedShapes(shape, shape); + } + + protected record ComputedShapes(VoxelShape boundingShape, VoxelShape collisionShape) {} +} diff --git a/src/main/java/com/github/elenterius/biomancy/block/malignantbloom/package-info.java b/src/main/java/com/github/elenterius/biomancy/block/malignantbloom/package-info.java new file mode 100644 index 000000000..874104113 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/block/malignantbloom/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.block.malignantbloom; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/block/neural/NeuralInterceptorBlock.java b/src/main/java/com/github/elenterius/biomancy/block/neural/NeuralInterceptorBlock.java new file mode 100644 index 000000000..a14480436 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/block/neural/NeuralInterceptorBlock.java @@ -0,0 +1,93 @@ +package com.github.elenterius.biomancy.block.neural; + +import com.github.elenterius.biomancy.util.SoundUtil; +import com.github.elenterius.biomancy.world.MobSpawnFilterShape; +import com.github.elenterius.biomancy.world.spatial.SpatialShapeManager; +import com.github.elenterius.biomancy.world.spatial.geometry.SphereShape; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.Nullable; + +public class NeuralInterceptorBlock extends HorizontalDirectionalBlock { + + protected static final VoxelShape SHAPE = createShape(); + + private static VoxelShape createShape() { + VoxelShape a = Block.box(2, 0, 2, 14, 8, 14); + VoxelShape b = Block.box(3, 8, 3, 13, 17, 13); + return Shapes.join(a, b, BooleanOp.OR); + } + + public NeuralInterceptorBlock(Properties properties) { + super(properties); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { + if (level instanceof ServerLevel serverLevel) { + SpatialShapeManager.getOrCreateShape(serverLevel, pos, () -> { + SphereShape shape = new SphereShape(pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d, 48); + return new MobSpawnFilterShape(shape); + }); + } + } + + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { + if (!state.is(newState.getBlock())) { + if (level instanceof ServerLevel serverLevel) { + SpatialShapeManager.remove(serverLevel, pos); + } + super.onRemove(state, level, pos, newState, isMoving); + } + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return SHAPE; + } + + @Override + public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { + if (random.nextInt(4) == 0) { + int particleAmount = random.nextInt(2, 8); + int color = 0x9f4576; //magenta haze + double r = (color >> 16 & 255) / 255d; + double g = (color >> 8 & 255) / 255d; + double b = (color & 255) / 255d; + for (int i = 0; i < particleAmount; i++) { + level.addParticle(ParticleTypes.ENTITY_EFFECT, pos.getX() + (random.nextFloat() * 0.8f + 0.1f), pos.getY() + 0.75f, pos.getZ() + (random.nextFloat() * 0.8f + 0.1f), r, g, b); + } + + if (random.nextInt(3) == 0) { + SoundUtil.clientPlayBlockSound(level, pos, SoundEvents.VILLAGER_AMBIENT, 0.6f, 0.6f); + } + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/block/neural/package-info.java b/src/main/java/com/github/elenterius/biomancy/block/neural/package-info.java new file mode 100644 index 000000000..f44255cba --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/block/neural/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.block.neural; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/block/orifice/OrificeBlock.java b/src/main/java/com/github/elenterius/biomancy/block/orifice/OrificeBlock.java new file mode 100644 index 000000000..d335c0ded --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/block/orifice/OrificeBlock.java @@ -0,0 +1,109 @@ +package com.github.elenterius.biomancy.block.orifice; + +import com.github.elenterius.biomancy.init.ModFluids; +import com.github.elenterius.biomancy.init.ModParticleTypes; +import com.github.elenterius.biomancy.init.ModPlantTypes; +import com.github.elenterius.biomancy.init.ModProjectiles; +import com.github.elenterius.biomancy.util.EnhancedIntegerProperty; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.BucketPickup; +import net.minecraft.world.level.block.FallingBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.IPlantable; +import net.minecraftforge.common.PlantType; + +import java.util.Optional; + +public class OrificeBlock extends Block implements BucketPickup { + + public static final EnhancedIntegerProperty AGE = EnhancedIntegerProperty.wrap(BlockStateProperties.AGE_2); + + public OrificeBlock(Properties properties) { + super(properties); + registerDefaultState(defaultBlockState().setValue(AGE.get(), AGE.getMin())); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(AGE.get()); + } + + @Override + public boolean canSustainPlant(BlockState state, BlockGetter world, BlockPos pos, Direction facing, IPlantable plantable) { + PlantType type = plantable.getPlantType(world, pos.relative(facing)); + return type == ModPlantTypes.FLESH_PLANT_TYPE; + } + + @Override + public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + if (!level.isAreaLoaded(pos, 1)) return; + + if (random.nextFloat() < 0.33f) { + int age = AGE.getValue(state); + if (age < AGE.getMax()) { + level.setBlock(pos, AGE.addValue(state, 1), Block.UPDATE_CLIENTS); + } + else { + if (random.nextFloat() < 0.5f) { + BlockPos posBelow = pos.below(); + if (FallingBlock.isFree(level.getBlockState(posBelow)) && pos.getY() >= level.getMinBuildHeight()) { + level.setBlock(pos, AGE.setValue(state, AGE.getMin()), Block.UPDATE_CLIENTS); + float x = random.nextIntBetweenInclusive(-6, 6) / 16f; + float z = random.nextIntBetweenInclusive(-6, 6) / 16f; + ModProjectiles.FALLING_ACID_BLOB.shoot(level, Vec3.atBottomCenterOf(pos).add(x, 0, z), Vec3.atBottomCenterOf(posBelow).add(x, 0, z)); + } + } + } + } + } + + @Override + public ItemStack pickupBlock(LevelAccessor level, BlockPos pos, BlockState state) { + if (AGE.getValue(state) == AGE.getMax()) { + level.setBlock(pos, AGE.setValue(state, AGE.getMin()), Block.UPDATE_CLIENTS); + return new ItemStack(ModFluids.ACID.get().getBucket()); + } + + return ItemStack.EMPTY; + } + + @Override + public Optional getPickupSound() { + return ModFluids.ACID.get().getPickupSound(); + } + + @Override + public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { + if (random.nextInt(5) == 0 && AGE.getValue(state) == AGE.getMax()) { + Direction direction = Direction.getRandom(random); + + if (direction == Direction.UP) { + return; + } + + BlockPos neighborPos = pos.relative(direction); + BlockState neighborState = level.getBlockState(neighborPos); + + if (!state.canOcclude() || !neighborState.isFaceSturdy(level, neighborPos, direction.getOpposite())) { + double x = direction.getStepX() == 0 ? random.nextDouble() : 0.5d + direction.getStepX() * 0.6d; + double y = direction.getStepY() == 0 ? random.nextDouble() : 0.5d + direction.getStepY() * 0.6d; + double z = direction.getStepZ() == 0 ? random.nextDouble() : 0.5d + direction.getStepZ() * 0.6d; + level.addParticle(ModParticleTypes.DRIPPING_ACID.get(), pos.getX() + x, pos.getY() + y, pos.getZ() + z, 0, 0, 0); + } + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/block/orifice/package-info.java b/src/main/java/com/github/elenterius/biomancy/block/orifice/package-info.java new file mode 100644 index 000000000..c1c91a051 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/block/orifice/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.block.orifice; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/block/property/BlockPropertyUtil.java b/src/main/java/com/github/elenterius/biomancy/block/property/BlockPropertyUtil.java index 4bfe6f05a..4fbfc125e 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/property/BlockPropertyUtil.java +++ b/src/main/java/com/github/elenterius/biomancy/block/property/BlockPropertyUtil.java @@ -1,9 +1,8 @@ package com.github.elenterius.biomancy.block.property; -import com.google.common.collect.ImmutableMap; +import com.github.elenterius.biomancy.mixin.IntegerPropertyAccessor; import net.minecraft.world.level.block.CropBlock; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.level.block.state.properties.Property; @@ -13,35 +12,16 @@ public final class BlockPropertyUtil { - public static final ImmutableMap maxAgeMappings; public static final String AGE_PROPERTY = "age"; - static { - //we can't know if someone might have manipulated the properties, so we search for the max value - maxAgeMappings = ImmutableMap.builder() - .put(BlockStateProperties.AGE_1, findLast(BlockStateProperties.AGE_1.getPossibleValues())) - .put(BlockStateProperties.AGE_3, findLast(BlockStateProperties.AGE_3.getPossibleValues())) - .put(BlockStateProperties.AGE_5, findLast(BlockStateProperties.AGE_5.getPossibleValues())) - .put(BlockStateProperties.AGE_7, findLast(BlockStateProperties.AGE_7.getPossibleValues())) - .put(BlockStateProperties.AGE_15, findLast(BlockStateProperties.AGE_15.getPossibleValues())) - .put(BlockStateProperties.AGE_25, findLast(BlockStateProperties.AGE_25.getPossibleValues())) - .build(); - } - private BlockPropertyUtil() {} public static int getMaxAge(IntegerProperty property) { - if (maxAgeMappings.containsKey(property)) { - Integer integer = maxAgeMappings.get(property); - return integer != null ? integer : 0; - } - else { - return findLast(property.getPossibleValues()); - } + return ((IntegerPropertyAccessor) property).biomancy$getMax(); } public static int getMinAge(IntegerProperty property) { - return property.getPossibleValues().iterator().next(); + return ((IntegerPropertyAccessor) property).biomancy$getMin(); } public static Optional getAgeProperty(BlockState state) { @@ -60,25 +40,11 @@ public static int getAge(BlockState state) { return getAgeProperty(state).map(state::getValue).orElse(0); } - public static Optional getCurrentAgeAndMaxAge(BlockState state) { - if (state.getBlock() instanceof CropBlock crop) { - return Optional.of(new int[]{state.getValue(crop.getAgeProperty()), crop.getMaxAge()}); - } - - for (Property property : state.getProperties()) { - if (property.getName().equals(AGE_PROPERTY) && property instanceof IntegerProperty ageProperty) { - return Optional.of(new int[]{state.getValue(ageProperty), getMaxAge(ageProperty)}); - } - } - - return Optional.empty(); - } - public static > T getPrevious(Property property, T value) { return findPrevious(property.getPossibleValues(), value); } - public static T findPrevious(Collection collection, T value) { + private static T findPrevious(Collection collection, T value) { Iterator iterator = collection.iterator(); T previous = null; @@ -93,7 +59,11 @@ public static T findPrevious(Collection collection, T value) { return previous != null ? previous : value; //returns the last value of the collection if possible } - public static T findLast(Collection collection) { + public static > T getLast(Property property) { + return findLast(property.getPossibleValues()); + } + + private static T findLast(Collection collection) { T value = collection.iterator().next(); for (T t : collection) value = t; return value; diff --git a/src/main/java/com/github/elenterius/biomancy/block/veins/FleshVeinsBlock.java b/src/main/java/com/github/elenterius/biomancy/block/veins/FleshVeinsBlock.java index 69f51404c..e3ffb8940 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/veins/FleshVeinsBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/block/veins/FleshVeinsBlock.java @@ -1,18 +1,25 @@ package com.github.elenterius.biomancy.block.veins; -import com.github.elenterius.biomancy.block.DirectionalSlabBlock; -import com.github.elenterius.biomancy.block.cradle.PrimordialCradleBlockEntity; -import com.github.elenterius.biomancy.block.property.DirectionalSlabType; +import com.github.elenterius.biomancy.block.cradle.PrimalEnergyHandler; +import com.github.elenterius.biomancy.block.malignantbloom.MalignantBloomBlock; import com.github.elenterius.biomancy.init.ModBlockProperties; import com.github.elenterius.biomancy.init.ModBlocks; -import com.github.elenterius.biomancy.init.ModItems; +import com.github.elenterius.biomancy.init.ModFluids; import com.github.elenterius.biomancy.init.ModSoundEvents; +import com.github.elenterius.biomancy.init.tags.ModBlockTags; import com.github.elenterius.biomancy.util.ArrayUtil; import com.github.elenterius.biomancy.util.Bit32Set; import com.github.elenterius.biomancy.util.EnhancedIntegerProperty; import com.github.elenterius.biomancy.util.LevelUtil; -import com.github.elenterius.biomancy.util.random.Noise; +import com.github.elenterius.biomancy.util.random.CellularNoise; import com.github.elenterius.biomancy.world.PrimordialEcosystem; +import com.github.elenterius.biomancy.world.mound.MoundChamber; +import com.github.elenterius.biomancy.world.mound.MoundShape; +import com.github.elenterius.biomancy.world.mound.decorator.ChamberDecorator; +import com.github.elenterius.biomancy.world.mound.decorator.ChamberSpecialDecorator; +import com.github.elenterius.biomancy.world.spatial.SpatialShapeManager; +import com.github.elenterius.biomancy.world.spatial.geometry.HasRadius; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ItemParticleOption; @@ -29,22 +36,28 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.StairsShape; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.Optional; +import java.util.function.Predicate; public class FleshVeinsBlock extends MultifaceBlock implements SimpleWaterloggedBlock { + public static final Predicate BLOCKS_TO_AVOID_PREDICATE = blockState -> blockState.is(ModBlocks.MALIGNANT_BLOOM.get()); protected static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; protected static final EnhancedIntegerProperty CHARGE = ModBlockProperties.CHARGE; private final MultifaceSpreader spreader = new MultifaceSpreader(new MalignantFleshSpreaderConfig(this)); @@ -54,97 +67,185 @@ public FleshVeinsBlock(Properties properties) { registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false).setValue(CHARGE.get(), CHARGE.getMin())); } - public static boolean convertSelf(BlockState state, ServerLevel level, BlockPos pos, int directNeighbors, boolean isCradleNearby, float nearCradlePct) { - - Bit32Set bitSet = new Bit32Set(); - bitSet.set(5, hasFace(state, Direction.DOWN)); - bitSet.set(4, hasFace(state, Direction.UP)); - bitSet.set(3, hasFace(state, Direction.NORTH)); - bitSet.set(2, hasFace(state, Direction.SOUTH)); - bitSet.set(1, hasFace(state, Direction.WEST)); - bitSet.set(0, hasFace(state, Direction.EAST)); - - int faces = bitSet.cardinality(); - - if (faces == 1) { - if (isCradleNearby) { - int bitIndex = bitSet.nextSetBit(0); - if (bitIndex < 6) { - Direction direction = Direction.from3DDataValue(5 - bitIndex); - BlockPos posBelow = pos.relative(direction); - BlockState stateBelow = level.getBlockState(posBelow); - - Block replacementBlock = ModBlocks.PRIMAL_FLESH.get(); - - if (stateBelow.getBlock() == ModBlocks.PRIMAL_FLESH.get() || stateBelow.getBlock() == ModBlocks.MALIGNANT_FLESH.get()) { - BlockPos posBelow2 = pos.relative(direction, 2); - BlockState stateBelow2 = level.getBlockState(posBelow2); - - if (direction == Direction.UP && (stateBelow2.getBlock() == ModBlocks.PRIMAL_FLESH.get() || stateBelow2.getBlock() == ModBlocks.MALIGNANT_FLESH.get())) { - if (level.getLightEngine().getRawBrightness(pos, 0) < 5) { - level.setBlock(posBelow, Blocks.SHROOMLIGHT.defaultBlockState(), Block.UPDATE_CLIENTS); - return true; - } - } + public static boolean convert(BlockState state, ServerLevel level, BlockPos pos, int directNeighbors, @Nullable MoundShape mound, float nearBoundingCenterPct, @Nullable PrimalEnergyHandler energyHandler) { - posBelow = posBelow2; - stateBelow = stateBelow2; - } - else { - replacementBlock = level.random.nextFloat() < nearCradlePct ? ModBlocks.PRIMAL_FLESH.get() : ModBlocks.MALIGNANT_FLESH.get(); - } + Bit32Set facesSet = new Bit32Set(); + facesSet.set(5, hasFace(state, Direction.DOWN)); + facesSet.set(4, hasFace(state, Direction.UP)); + facesSet.set(3, hasFace(state, Direction.NORTH)); + facesSet.set(2, hasFace(state, Direction.SOUTH)); + facesSet.set(1, hasFace(state, Direction.WEST)); + facesSet.set(0, hasFace(state, Direction.EAST)); - return PrimordialEcosystem.tryToReplaceBlock(level, posBelow, stateBelow, replacementBlock.defaultBlockState()); - } + if (mound != null) { + MoundChamber chamber = mound.getChamberAt(pos); + if (chamber != null && chamber.contains(pos)) { + return convertInsideChamber(level, pos, directNeighbors, mound, chamber, nearBoundingCenterPct, facesSet, energyHandler); + } + } + + return convertNormal(level, pos, mound, nearBoundingCenterPct, directNeighbors, facesSet); + } + + protected static boolean convertNormal(ServerLevel level, BlockPos pos, @Nullable MoundShape mound, float nearBoundingCenterPct, int directNeighbors, Bit32Set facesSet) { + int numFaces = facesSet.cardinality(); + + boolean hasAnyBlockToAvoidNearby = LevelUtil.isBlockNearby(level, pos, 2, BLOCKS_TO_AVOID_PREDICATE); + + if (numFaces == 1) { + CellularNoise cellularNoise = PrimordialEcosystem.getCellularNoise(level); + float noiseValue = cellularNoise.getValueAtCenter(pos); + + //vein face points inwards and the direction is in reference to itself and not in reference to the block it's attached to + Direction axisDirection = Direction.from3DDataValue(5 - facesSet.nextSetBit(0)); + + boolean hasConvertedAnyOtherBlocks = convertDirectNeighborBlock(level, pos, axisDirection, directNeighbors, cellularNoise, noiseValue); - return false; + if (!hasConvertedAnyOtherBlocks && !hasAnyBlockToAvoidNearby && directNeighbors > 2) { + if (noiseValue >= cellularNoise.borderThreshold()) { + return convertSelfIntoSlabBlock(level, pos, axisDirection.getOpposite()); + } + else if (noiseValue < cellularNoise.coreThreshold() && (PrimordialEcosystem.getRandomWithSeed(pos).nextFloat() <= 0.3f - nearBoundingCenterPct) && (LevelUtil.getMaxBrightness(level, pos) > 5)) { + return convertSelfIntoBloom(level, pos, axisDirection); + } } - if (directNeighbors < 3) return false; + return hasConvertedAnyOtherBlocks; + } + + if (hasAnyBlockToAvoidNearby) return false; + + if (numFaces > 3) { + return convertSelfIntoFullBlock(level, pos); + } + + return convertSelfIntoStairs(level, pos, facesSet); + } + + protected static boolean convertInsideChamber(ServerLevel level, BlockPos pos, int directNeighbors, MoundShape mound, MoundChamber chamber, float nearBoundingCenterPct, Bit32Set facesSet, @Nullable PrimalEnergyHandler energyHandler) { + Direction[] axisDirections = Arrays.stream(facesSet.getIndices()).mapToObj(i -> Direction.from3DDataValue(5 - i)).toArray(Direction[]::new); + ArrayUtil.shuffle(axisDirections, level.random); - int bitIndex = bitSet.nextSetBit(0); - if (bitIndex < 6) { - //vein faces point inwards and the direction is in reference to itself and not in reference to the block it's attached to + for (Direction axisDirection : axisDirections) { + BlockPos closeOffsetPos = pos.relative(axisDirection); + BlockState closeOffsetState = level.getBlockState(closeOffsetPos); - Direction direction = Direction.from3DDataValue(5 - bitIndex); - BlockPos posBelow = pos.relative(direction); - BlockState stateBelow = level.getBlockState(posBelow); + if (PrimordialEcosystem.isReplaceable(closeOffsetState)) { + return destroyBlockAndConvertIntoEnergy(level, closeOffsetPos, energyHandler, 15); + } + + if (PrimordialEcosystem.FULL_FLESH_BLOCKS.contains(closeOffsetState.getBlock())) { + ChamberDecorator chamberDecorator = chamber.getDecorator(); - Block replacementBlock; + boolean chamberContainsCloseOffsetPos = chamber.contains(closeOffsetPos); - if (stateBelow.getBlock() == ModBlocks.PRIMAL_FLESH.get() || stateBelow.getBlock() == ModBlocks.MALIGNANT_FLESH.get()) { - posBelow = pos.relative(direction, 2); - stateBelow = level.getBlockState(posBelow); - replacementBlock = ModBlocks.PRIMAL_FLESH.get(); + if (chamberContainsCloseOffsetPos && !closeOffsetState.is(ModBlocks.BLOOMLIGHT.get())) { + ChamberDecorator.PartOfDecorationResult result = chamberDecorator.isBlockPartOfDecoration(chamber, level, closeOffsetPos, closeOffsetState); + if (result.positionIsValid && !result.materialIsValid) { + return destroyBlockAndConvertIntoEnergy(level, closeOffsetPos, energyHandler, 30); + } } - else { - replacementBlock = ModBlocks.MALIGNANT_FLESH.get(); + + if (chamberDecorator.canPlace(chamber, level, pos, axisDirection)) { + return chamberDecorator.place(chamber, level, pos, axisDirection); } - if (!PrimordialEcosystem.tryToReplaceBlock(level, posBelow, stateBelow, replacementBlock.defaultBlockState())) { - Noise noise = PrimordialEcosystem.getCellularNoise(level); - float borderThreshold = 0.15f; - float n = noise.getValue(pos.getX(), pos.getY(), pos.getZ()); - if (n >= borderThreshold) { - BlockState slabState = ModBlocks.MALIGNANT_FLESH_SLAB.get() - .defaultBlockState() - .setValue(DirectionalSlabBlock.TYPE, DirectionalSlabType.getHalfFrom(pos, Vec3.atCenterOf(pos), direction.getOpposite())); - level.setBlock(pos, slabState, Block.UPDATE_CLIENTS); + BlockPos farOffsetPos = pos.relative(axisDirection, 2); + BlockState farOffsetState = level.getBlockState(farOffsetPos); + + if (!chamberContainsCloseOffsetPos) { + // create "Door" between two adjacent chambers that are separated by a one block thick wall + if (!mound.hasChamberAt(closeOffsetPos)) { + MoundChamber farChamber = mound.getChamberAt(farOffsetPos); + if (farChamber != null && farChamber != chamber) { + return level.setBlock(closeOffsetPos, ModBlocks.PRIMAL_PERMEABLE_MEMBRANE.get().defaultBlockState(), Block.UPDATE_CLIENTS); + } } } + + // create light source in dark areas + if (ChamberSpecialDecorator.BLOOMLIGHT.canDecorate(chamber, level, pos, axisDirection, closeOffsetPos, closeOffsetState, farOffsetPos, farOffsetState)) { + return ChamberSpecialDecorator.BLOOMLIGHT.decorate(chamber, level, pos, axisDirection, closeOffsetPos, closeOffsetState, farOffsetPos, farOffsetState); + } + + if (PrimordialEcosystem.isReplaceable(farOffsetState) && farOffsetState.isCollisionShapeFullBlock(level, farOffsetPos)) { + BlockState replacementState = level.random.nextFloat() < nearBoundingCenterPct ? ModBlocks.PRIMAL_FLESH.get().defaultBlockState() : ModBlocks.MALIGNANT_FLESH.get().defaultBlockState(); + return level.setBlock(farOffsetPos, replacementState, Block.UPDATE_CLIENTS); + } } + } + + return false; + } + protected static boolean destroyBlockAndConvertIntoEnergy(ServerLevel level, BlockPos pos, @Nullable PrimalEnergyHandler energyHandler, int amount) { + if (level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS)) { + if (energyHandler != null) energyHandler.fillPrimalEnergy(amount); return true; } + return false; + } - if (isCradleNearby) return false; + protected static boolean convertDirectNeighborBlock(ServerLevel level, BlockPos pos, Direction axisDirection, int directNeighbors, CellularNoise cellularNoise, float noiseValue) { + BlockPos posRelative = pos.relative(axisDirection); + BlockState stateRelative = level.getBlockState(posRelative); + BlockState replacementState = null; - if (faces > 3) { - level.setBlock(pos, ModBlocks.MALIGNANT_FLESH.get().defaultBlockState(), Block.UPDATE_CLIENTS); - return true; + if (directNeighbors > 2) { + if (stateRelative.getBlock() == ModBlocks.PRIMAL_FLESH.get() || stateRelative.getBlock() == ModBlocks.MALIGNANT_FLESH.get()) { + posRelative = pos.relative(axisDirection, 2); + stateRelative = level.getBlockState(posRelative); + return PrimordialEcosystem.tryToReplaceBlock(level, posRelative, stateRelative, ModBlocks.PRIMAL_FLESH.get().defaultBlockState()); + } + else { + replacementState = ModBlocks.MALIGNANT_FLESH.get().defaultBlockState(); + } + } + + if (PrimordialEcosystem.isReplaceableLog(stateRelative)) { + if (noiseValue < cellularNoise.coreThreshold()) { + if (stateRelative.hasProperty(RotatedPillarBlock.AXIS)) { + Direction.Axis axis = stateRelative.getValue(RotatedPillarBlock.AXIS); + replacementState = Blocks.BONE_BLOCK.defaultBlockState().setValue(RotatedPillarBlock.AXIS, axis); + } + else replacementState = ModBlocks.PRIMAL_FLESH_WALL.get().defaultBlockState(); + } + else replacementState = ModBlocks.PRIMAL_FLESH.get().defaultBlockState(); + } + + if (replacementState != null) { + return PrimordialEcosystem.tryToReplaceBlock(level, posRelative, stateRelative, replacementState); } - int mask = bitSet.getBits(); + return false; + } + + protected static boolean convertSelfIntoBloom(ServerLevel level, BlockPos pos, Direction direction) { + MalignantBloomBlock bloomBlock = ModBlocks.MALIGNANT_BLOOM.get(); + + BlockPos posBelow = pos.relative(direction); + BlockState stateBelow = level.getBlockState(posBelow); + boolean mayPlace = bloomBlock.mayPlaceOn(level, posBelow, stateBelow); + + if (mayPlace && !LevelUtil.isBlockNearby(level, pos, 4, blockState -> blockState.is(bloomBlock)) && bloomBlock.hasUnobstructedAim(level, pos, direction.getOpposite())) { + BlockState stateForPlacement = bloomBlock.getStateForPlacement(level, pos, direction.getOpposite()); + return level.setBlock(pos, stateForPlacement, Block.UPDATE_CLIENTS); + } + + return false; + } + + protected static boolean convertSelfIntoSlabBlock(ServerLevel level, BlockPos pos, Direction direction) { + BlockState stateForPlacement = ModBlocks.MALIGNANT_FLESH_SLAB.get().getStateForPlacement(level, pos, direction); + return level.setBlock(pos, stateForPlacement, Block.UPDATE_CLIENTS); + } + + protected static boolean convertSelfIntoFullBlock(ServerLevel level, BlockPos pos) { + return level.setBlock(pos, ModBlocks.MALIGNANT_FLESH.get().defaultBlockState(), Block.UPDATE_CLIENTS); + } + + protected static boolean convertSelfIntoStairs(ServerLevel level, BlockPos pos, Bit32Set facesSet) { + int mask = facesSet.getBits(); if (mask == 0b10_10_00) { //down & north BlockState blockState = ModBlocks.MALIGNANT_FLESH_STAIRS.get().defaultBlockState() @@ -277,6 +378,17 @@ public static boolean convertSelf(BlockState state, ServerLevel level, BlockPos return false; } + protected static BlockState removeFace(BlockState state, BooleanProperty face) { + BlockState blockstate = state.setValue(face, Boolean.FALSE); + return hasAnyFace(blockstate) ? blockstate : Blocks.AIR.defaultBlockState(); + } + + public static boolean canVeinsAttachTo(BlockGetter level, Direction direction, BlockPos pos, BlockState state) { + if (state.is(ModBlockTags.DISALLOW_VEINS_TO_ATTACH)) return false; + if (state.is(ModBlockTags.ALLOW_VEINS_TO_ATTACH)) return true; + return Block.isFaceFull(state.getBlockSupportShape(level, pos), direction.getOpposite()) || Block.isFaceFull(state.getCollisionShape(level, pos), direction.getOpposite()); + } + @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { super.createBlockStateDefinition(builder); @@ -306,12 +418,52 @@ public BlockState updateShape(BlockState state, Direction direction, BlockState if (Boolean.TRUE.equals(state.getValue(WATERLOGGED))) { level.scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickDelay(level)); } - return super.updateShape(state, direction, neighborState, level, currentPos, neighborPos); + + if (!hasAnyFace(state)) { + return Blocks.AIR.defaultBlockState(); + } + + if (hasFace(state, direction) && !canVeinsAttachTo(level, direction, neighborPos, neighborState)) { + return removeFace(state, getFaceProperty(direction)); + } + + return state; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) { + boolean flag = false; + + for (Direction direction : DIRECTIONS) { + if (hasFace(state, direction)) { + BlockPos blockpos = pos.relative(direction); + if (!canVeinsAttachTo(level, direction, blockpos, level.getBlockState(blockpos))) { + return false; + } + flag = true; + } + } + + return flag; + } + + @Override + public boolean isValidStateForPlacement(BlockGetter level, BlockState state, BlockPos pos, Direction direction) { + if (isFaceSupported(direction) && (!state.is(this) || !hasFace(state, direction))) { + BlockPos blockPos = pos.relative(direction); + return canVeinsAttachTo(level, direction, blockPos, level.getBlockState(blockPos)); + } + return false; } @Override public boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) { - return useContext.getItemInHand().is(asItem()) && super.canBeReplaced(state, useContext); + return !useContext.getItemInHand().is(asItem()) || super.canBeReplaced(state, useContext); + } + + @Override + public boolean canBeReplaced(BlockState state, Fluid fluid) { + return fluid.getFluidType() == ModFluids.ACID_TYPE.get() || material.isReplaceable() || !material.isSolid(); } @Override @@ -334,17 +486,7 @@ public void entityInside(BlockState state, Level level, BlockPos pos, Entity ent ItemStack stack = itemEntity.getItem(); - if (stack.is(ModItems.LIVING_FLESH.get())) { - charge = CHARGE.getMax(); - - Vec3 motion = new Vec3((level.random.nextFloat() - 0.5d) * 0.1d, level.random.nextFloat() * 0.1d + 0.15d, (level.random.nextFloat() - 0.5d) * 0.1d); - ((ServerLevel) level).sendParticles(new ItemParticleOption(ParticleTypes.ITEM, stack), itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(), 8, motion.x, motion.y, motion.z, 0.05f); - - stack.shrink(1); - setCharge(level, pos, state, charge); - level.playSound(null, pos, ModSoundEvents.DECOMPOSER_EAT.get(), SoundSource.BLOCKS, 1f, 0.15f + level.random.nextFloat() * 0.5f); - } - else if (stack.isEdible()) { + if (stack.isEdible()) { int nutrition = Optional.ofNullable(stack.getFoodProperties(null)) .filter(FoodProperties::isMeat) .map(FoodProperties::getNutrition).orElse(0); @@ -358,7 +500,7 @@ else if (stack.isEdible()) { stack.shrink(amount); charge += amount * nutrition; setCharge(level, pos, state, charge); - level.playSound(null, pos, ModSoundEvents.DECOMPOSER_EAT.get(), SoundSource.BLOCKS, 1f, 0.15f + level.random.nextFloat() * 0.5f); + level.playSound(null, pos, ModSoundEvents.DECOMPOSER_EAT.get(), SoundSource.BLOCKS, 0.6f, 0.15f + level.random.nextFloat() * 0.5f); } } } @@ -368,21 +510,36 @@ public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource if (level.random.nextFloat() >= 0.5f) return; if (!level.isAreaLoaded(pos, 2)) return; - int cradleCoreRadius = 8; - int maxCradleDist = cradleCoreRadius * 4; - PrimordialCradleBlockEntity cradle = LevelUtil.findNearestBlockEntity(level, pos, maxCradleDist, PrimordialCradleBlockEntity.class); + PrimalEnergyHandler energyHandler = null; + MoundShape mound = null; + float nearBoundingCenterPct = 0; + + if (SpatialShapeManager.getClosestShape(level, pos, MoundShape.class::isInstance) instanceof MoundShape moundShape) { + mound = moundShape; + + BlockPos origin = mound.getOrigin(); + BlockEntity existingBlockEntity = level.getExistingBlockEntity(origin); + if (existingBlockEntity instanceof PrimalEnergyHandler peh) { + energyHandler = peh; + } + + Shape boundingShape = mound.getBoundingShapeAt(pos); + if (boundingShape != null) { + double radius = boundingShape instanceof HasRadius sphere ? sphere.getRadius() : boundingShape.getAABB().getSize() / 2; + double radiusSqr = radius * radius; + double distSqr = boundingShape.distanceToSqr(pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d); + nearBoundingCenterPct = Mth.clamp((float) (1 - distSqr / radiusSqr), 0f, 1f); + } + } int charge = getCharge(state); if (charge < 2) { - if (cradle != null && cradle.consumePrimalSpreadCharge(level, 1)) { + if (energyHandler != null && energyHandler.drainPrimalEnergy(1) > 0) { setCharge(level, pos, state, charge + 1); } return; } - double cradleDistance = cradle != null ? Math.sqrt(cradle.getBlockPos().distSqr(pos)) : maxCradleDist + 1; - float nearCradlePct = Mth.clamp((float) (1d - cradleDistance / maxCradleDist), 0f, 1f); - int directNeighbors = 0; for (Direction direction : Direction.values()) { BlockState neighborState = level.getBlockState(pos.relative(direction)); @@ -392,9 +549,9 @@ public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource float populationPct = directNeighbors / (float) Direction.values().length; float conversionChance = charge / (CHARGE.getMax() + 5f) + populationPct * 0.5f; - if (random.nextFloat() < conversionChance && convertSelf(state, level, pos, directNeighbors, cradleDistance <= cradleCoreRadius, nearCradlePct)) { - level.playSound(null, pos, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundSource.BLOCKS, 1.2f, 0.15f + random.nextFloat() * 0.5f); - return; + if (random.nextFloat() < conversionChance && convert(state, level, pos, directNeighbors, mound, nearBoundingCenterPct, energyHandler)) { + level.playSound(null, pos, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundSource.BLOCKS, 0.8f, 0.15f + random.nextFloat() * 0.5f); + //return; //TODO: exiting early hampers growth to a very extreme degree. reevaluate which conversions should return true or be ignored } if (charge > 4) { @@ -402,21 +559,21 @@ public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource if (growthAmount > 0) { charge -= growthAmount * 2; state = level.getBlockState(pos); - level.playSound(null, pos, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundSource.BLOCKS, 1f, 0.15f + random.nextFloat() * 0.5f); + level.playSound(null, pos, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundSource.BLOCKS, 0.6f, 0.15f + random.nextFloat() * 0.5f); } } else { if (getSpreader().spreadFromRandomFaceTowardRandomDirection(state, level, pos, random).isPresent()) { charge -= 2; state = level.getBlockState(pos); - level.playSound(null, pos, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundSource.BLOCKS, 1f, 0.15f + random.nextFloat() * 0.5f); + level.playSound(null, pos, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundSource.BLOCKS, 0.6f, 0.15f + random.nextFloat() * 0.5f); } } - if (cradleDistance <= maxCradleDist) { - if (cradleDistance <= cradleCoreRadius || (cradle != null && cradle.consumePrimalSpreadCharge(level, charge))) { - charge = Math.max(charge, Math.round(CHARGE.getMax() * nearCradlePct)); - increaseChargeAroundPos(level, pos, random, charge * 2); + if (energyHandler != null) { + int primalEnergy = Math.max(charge, Math.round(CHARGE.getMax() * nearBoundingCenterPct) / 2); + if (energyHandler.getPrimalEnergy() > primalEnergy && energyHandler.drainPrimalEnergy(primalEnergy) >= primalEnergy) { + increaseChargeAroundPos(level, pos, random, primalEnergy * 2); } else if (charge > 1) { int usedCharge = increaseChargeAroundPos(level, pos, random, charge); diff --git a/src/main/java/com/github/elenterius/biomancy/block/veins/MalignantFleshSpreaderConfig.java b/src/main/java/com/github/elenterius/biomancy/block/veins/MalignantFleshSpreaderConfig.java index 405468810..33d01147e 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/veins/MalignantFleshSpreaderConfig.java +++ b/src/main/java/com/github/elenterius/biomancy/block/veins/MalignantFleshSpreaderConfig.java @@ -2,7 +2,8 @@ import com.github.elenterius.biomancy.block.cradle.PrimordialCradleBlock; import com.github.elenterius.biomancy.init.ModBlocks; -import com.github.elenterius.biomancy.util.random.Noise; +import com.github.elenterius.biomancy.util.LevelUtil; +import com.github.elenterius.biomancy.util.random.CellularNoise; import com.github.elenterius.biomancy.world.PrimordialEcosystem; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -23,7 +24,6 @@ class MalignantFleshSpreaderConfig extends MultifaceSpreader.DefaultSpreaderConfig { protected static final Set VALID_SOURCES = Set.of(ModBlocks.MALIGNANT_FLESH_SLAB.get(), ModBlocks.MALIGNANT_FLESH_STAIRS.get(), ModBlocks.MALIGNANT_FLESH.get()); - protected static final Set VALID_UPGRADE_TARGETS = Set.of(ModBlocks.MALIGNANT_FLESH_SLAB.get(), ModBlocks.MALIGNANT_FLESH_STAIRS.get()); public MalignantFleshSpreaderConfig(MultifaceBlock block) { super(block); @@ -54,12 +54,12 @@ protected boolean stateCanBeReplaced(BlockGetter level, BlockPos posA, BlockPos @Override public boolean canSpreadInto(BlockGetter level, BlockPos pos, MultifaceSpreader.SpreadPos spreadPos) { BlockState state = level.getBlockState(spreadPos.pos()); - if (VALID_UPGRADE_TARGETS.contains(state.getBlock())) { + if (PrimordialEcosystem.VALID_UPGRADE_TARGETS.contains(state.getBlock())) { if (level instanceof ServerLevel serverLevel) { - Noise noise = PrimordialEcosystem.getCellularNoise(serverLevel); - float borderThreshold = 0.145f; - float n = noise.getValue(pos.getX(), pos.getY(), pos.getZ()); - return n >= borderThreshold; + CellularNoise cellularNoise = PrimordialEcosystem.getCellularNoise(serverLevel); + float borderThreshold = cellularNoise.borderThreshold() - 0.005f; + float n = cellularNoise.getValueAtCenter(pos); + return n >= borderThreshold && !LevelUtil.isBlockNearby(serverLevel, spreadPos.pos(), 4, blockState -> blockState.is(ModBlocks.MALIGNANT_BLOOM.get())); } return true; } @@ -68,7 +68,7 @@ public boolean canSpreadInto(BlockGetter level, BlockPos pos, MultifaceSpreader. @Override public boolean placeBlock(LevelAccessor level, MultifaceSpreader.SpreadPos spreadPos, BlockState state, boolean markForPostprocessing) { - if (VALID_UPGRADE_TARGETS.contains(state.getBlock())) { + if (PrimordialEcosystem.VALID_UPGRADE_TARGETS.contains(state.getBlock())) { if (level.getRandom().nextFloat() < 0.25f) { return level.setBlock(spreadPos.pos(), ModBlocks.MALIGNANT_FLESH.get().defaultBlockState(), Block.UPDATE_CLIENTS); } diff --git a/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreen.java b/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreen.java index b67d7422f..0abe1ba63 100644 --- a/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreen.java +++ b/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreen.java @@ -1,16 +1,16 @@ package com.github.elenterius.biomancy.client.gui; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.gui.component.CustomEditBox; import com.github.elenterius.biomancy.client.util.ClientSoundUtil; import com.github.elenterius.biomancy.client.util.GuiRenderUtil; import com.github.elenterius.biomancy.client.util.GuiUtil; +import com.github.elenterius.biomancy.crafting.recipe.BioForgeRecipe; +import com.github.elenterius.biomancy.crafting.recipe.IngredientStack; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.menu.BioForgeMenu; -import com.github.elenterius.biomancy.recipe.BioForgeRecipe; -import com.github.elenterius.biomancy.recipe.IngredientStack; import com.github.elenterius.biomancy.styles.ColorStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; diff --git a/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreenController.java b/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreenController.java index f131e81ac..7c43b8cb3 100644 --- a/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreenController.java +++ b/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreenController.java @@ -1,13 +1,15 @@ package com.github.elenterius.biomancy.client.gui; +import com.github.elenterius.biomancy.BiomancyConfig; +import com.github.elenterius.biomancy.crafting.recipe.BioForgeRecipe; import com.github.elenterius.biomancy.init.ModBioForgeTabs; import com.github.elenterius.biomancy.init.ModRecipeBookTypes; import com.github.elenterius.biomancy.init.client.ModRecipeBookCategories; +import com.github.elenterius.biomancy.integration.BioForgeCompat; import com.github.elenterius.biomancy.menu.BioForgeMenu; import com.github.elenterius.biomancy.menu.BioForgeTab; import com.github.elenterius.biomancy.mixin.client.RecipeCollectionAccessor; import com.github.elenterius.biomancy.network.ModNetworkHandler; -import com.github.elenterius.biomancy.recipe.BioForgeRecipe; import com.google.common.collect.Lists; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; @@ -267,7 +269,7 @@ private static void canCraftRecipe(RecipeCollection recipeCollection, StackedCon private void updateAndSearchRecipes() { LocalPlayer player = getPlayer(); - boolean isCreativePlayer = player.isCreative(); + boolean isCreativePlayer = player.isCreative() || !BiomancyConfig.SERVER.doBioForgeRecipeProgression.get() || BioForgeCompat.isRecipeCollectionOverwriteEnabled(); ClientRecipeBook recipeBook = player.getRecipeBook(); List recipesForCategory = recipeBook.getCollection(ModRecipeBookCategories.getRecipeBookCategories(tabs.get(activeTab))); diff --git a/src/main/java/com/github/elenterius/biomancy/client/gui/DevCannonScreen.java b/src/main/java/com/github/elenterius/biomancy/client/gui/DevCannonScreen.java index d1c9ad809..1790593c2 100644 --- a/src/main/java/com/github/elenterius/biomancy/client/gui/DevCannonScreen.java +++ b/src/main/java/com/github/elenterius/biomancy/client/gui/DevCannonScreen.java @@ -1,12 +1,12 @@ package com.github.elenterius.biomancy.client.gui; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.GuiRenderUtil; import com.github.elenterius.biomancy.entity.projectile.BaseProjectile; import com.github.elenterius.biomancy.init.ModProjectiles; import com.github.elenterius.biomancy.item.weapon.DevArmCannonItem; import com.github.elenterius.biomancy.network.ModNetworkHandler; import com.github.elenterius.biomancy.styles.ColorStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; diff --git a/src/main/java/com/github/elenterius/biomancy/client/gui/DigesterScreen.java b/src/main/java/com/github/elenterius/biomancy/client/gui/DigesterScreen.java index 5f4c19d98..9e81009d6 100644 --- a/src/main/java/com/github/elenterius/biomancy/client/gui/DigesterScreen.java +++ b/src/main/java/com/github/elenterius/biomancy/client/gui/DigesterScreen.java @@ -72,7 +72,7 @@ protected void renderTooltip(PoseStack poseStack, int mouseX, int mouseY) { } private void drawFuelTooltip(PoseStack poseStack, int mouseX, int mouseY) { - int maxFuel = menu.getMAxFuelAmount(); + int maxFuel = menu.getMaxFuelAmount(); int fuelAmount = menu.getFuelAmount(); int totalFuelCost = menu.getFuelCost(); GuiRenderUtil.drawFuelTooltip(this, poseStack, mouseX, mouseY, maxFuel, fuelAmount, totalFuelCost); diff --git a/src/main/java/com/github/elenterius/biomancy/client/gui/InjectorScreen.java b/src/main/java/com/github/elenterius/biomancy/client/gui/InjectorScreen.java index 64f1192d4..cd4d55cd5 100644 --- a/src/main/java/com/github/elenterius/biomancy/client/gui/InjectorScreen.java +++ b/src/main/java/com/github/elenterius/biomancy/client/gui/InjectorScreen.java @@ -2,12 +2,12 @@ import com.github.elenterius.biomancy.api.serum.Serum; import com.github.elenterius.biomancy.api.serum.SerumContainer; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.GuiRenderUtil; import com.github.elenterius.biomancy.item.injector.InjectorItem; import com.github.elenterius.biomancy.network.ModNetworkHandler; import com.github.elenterius.biomancy.styles.ColorStyles; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; diff --git a/src/main/java/com/github/elenterius/biomancy/client/particle/ParticleProviders.java b/src/main/java/com/github/elenterius/biomancy/client/particle/ParticleProviders.java new file mode 100644 index 000000000..8cf12ee5e --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/particle/ParticleProviders.java @@ -0,0 +1,65 @@ +package com.github.elenterius.biomancy.client.particle; + +import com.github.elenterius.biomancy.init.ModFluids; +import com.github.elenterius.biomancy.init.ModMobEffects; +import com.github.elenterius.biomancy.init.ModParticleTypes; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.particle.SpriteSet; +import net.minecraft.core.particles.SimpleParticleType; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +public final class ParticleProviders { + private ParticleProviders() {} + + @OnlyIn(Dist.CLIENT) + public static class AcidLandProvider implements ParticleProvider { + protected final SpriteSet sprite; + + public AcidLandProvider(SpriteSet pSprites) { + this.sprite = pSprites; + } + + public Particle createParticle(SimpleParticleType type, ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + VanillaDripParticle particle = new VanillaDripParticle.DripLandParticle(level, x, y, z, ModFluids.ACID.get()); + particle.setColorRGB(ModMobEffects.CORROSIVE.get().getColor()); + particle.pickSprite(sprite); + return particle; + } + } + + @OnlyIn(Dist.CLIENT) + public static class AcidHangProvider implements ParticleProvider { + protected final SpriteSet sprite; + + public AcidHangProvider(SpriteSet pSprites) { + this.sprite = pSprites; + } + + public Particle createParticle(SimpleParticleType type, ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + VanillaDripParticle particle = new VanillaDripParticle.DripHangParticle(level, x, y, z, ModFluids.ACID.get(), ModParticleTypes.FALLING_ACID.get()); + particle.setColorRGB(ModMobEffects.CORROSIVE.get().getColor()); + particle.pickSprite(sprite); + return particle; + } + } + + @OnlyIn(Dist.CLIENT) + public static class AcidFallProvider implements ParticleProvider { + protected final SpriteSet sprite; + + public AcidFallProvider(SpriteSet pSprites) { + this.sprite = pSprites; + } + + public Particle createParticle(SimpleParticleType type, ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { + VanillaDripParticle particle = new VanillaDripParticle.AcidFallAndLandParticle(level, x, y, z, ModFluids.ACID.get(), ModParticleTypes.LANDING_ACID.get()); + particle.setColorRGB(ModMobEffects.CORROSIVE.get().getColor()); + particle.pickSprite(sprite); + return particle; + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/client/particle/VanillaDripParticle.java b/src/main/java/com/github/elenterius/biomancy/client/particle/VanillaDripParticle.java new file mode 100644 index 000000000..42ac6f3ec --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/particle/VanillaDripParticle.java @@ -0,0 +1,191 @@ +package com.github.elenterius.biomancy.client.particle; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.FastColor; +import net.minecraft.util.Mth; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +/** + * Almost exact copy of vanilla code because DripParticle class can't be extended + */ +@OnlyIn(Dist.CLIENT) +public class VanillaDripParticle extends TextureSheetParticle { + + protected final Fluid type; + private boolean isGlowing; + + protected VanillaDripParticle(ClientLevel level, double x, double y, double z, Fluid fluid) { + super(level, x, y, z); + this.setSize(0.01F, 0.01F); + this.gravity = 0.06F; + this.type = fluid; + } + + public ParticleRenderType getRenderType() { + return ParticleRenderType.PARTICLE_SHEET_OPAQUE; + } + + protected void setGlowing(boolean glowing) { + isGlowing = glowing; + } + + @Override + public int getLightColor(float partialTick) { + return isGlowing ? 0xf0 : super.getLightColor(partialTick); + } + + @Override + public void tick() { + xo = x; + yo = y; + zo = z; + + preMoveUpdate(); + + if (!removed) { + yd -= gravity; + move(xd, yd, zd); + + postMoveUpdate(); + + if (!removed) { + xd *= 0.9800000190734863; + yd *= 0.9800000190734863; + zd *= 0.9800000190734863; + BlockPos blockPos = new BlockPos(x, y, z); + FluidState fluidState = level.getFluidState(blockPos); + if (fluidState.getType() == type && y < (blockPos.getY() + fluidState.getHeight(level, blockPos))) { + remove(); + } + } + } + } + + protected void preMoveUpdate() { + if (lifetime-- <= 0) { + remove(); + } + } + + protected void postMoveUpdate() { /* placeholder */ } + + protected void setColorRGB(int color) { + rCol = FastColor.ARGB32.red(color) / 255f; + gCol = FastColor.ARGB32.green(color) / 255f; + bCol = FastColor.ARGB32.blue(color) / 255f; + } + + protected void setColorARGB(int color) { + rCol = FastColor.ARGB32.red(color) / 255f; + gCol = FastColor.ARGB32.green(color) / 255f; + bCol = FastColor.ARGB32.blue(color) / 255f; + alpha = FastColor.ARGB32.alpha(color) / 255f; + } + + @OnlyIn(Dist.CLIENT) + protected static class DripLandParticle extends VanillaDripParticle { + protected DripLandParticle(ClientLevel level, double x, double y, double z, Fluid fluid) { + super(level, x, y, z, fluid); + this.lifetime = (int) (16.0f / (Math.random() * 0.8f + 0.2f)); + } + } + + @OnlyIn(Dist.CLIENT) + protected static class DripHangParticle extends VanillaDripParticle { + protected final ParticleOptions fallingParticle; + + protected DripHangParticle(ClientLevel level, double x, double y, double z, Fluid fluid, ParticleOptions fallingParticle) { + super(level, x, y, z, fluid); + this.fallingParticle = fallingParticle; + this.gravity *= 0.02F; + this.lifetime = 40; + } + + @Override + protected void preMoveUpdate() { + if (lifetime-- <= 0) { + remove(); + level.addParticle(fallingParticle, x, y, z, xd, yd, zd); + } + } + + @Override + protected void postMoveUpdate() { + xd *= 0.02; + yd *= 0.02; + zd *= 0.02; + } + } + + @OnlyIn(Dist.CLIENT) + protected static class FallingParticle extends VanillaDripParticle { + protected FallingParticle(ClientLevel level, double x, double y, double z, Fluid fluid) { + this(level, x, y, z, fluid, (int) (64.0f / (Math.random() * 0.8f + 0.2f))); + } + + protected FallingParticle(ClientLevel level, double x, double y, double z, Fluid fluid, int lifetime) { + super(level, x, y, z, fluid); + this.lifetime = lifetime; + } + + @Override + protected void postMoveUpdate() { + if (onGround) { + remove(); + } + } + } + + @OnlyIn(Dist.CLIENT) + protected static class FallAndLandParticle extends FallingParticle { + protected final ParticleOptions landParticle; + + protected FallAndLandParticle(ClientLevel level, double x, double y, double z, Fluid fluid, ParticleOptions landParticle) { + super(level, x, y, z, fluid); + this.landParticle = landParticle; + } + + @Override + protected void postMoveUpdate() { + if (onGround) { + remove(); + level.addParticle(landParticle, x, y, z, 0, 0, 0); + } + } + } + + @OnlyIn(Dist.CLIENT) + protected static class AcidFallAndLandParticle extends FallAndLandParticle { + protected AcidFallAndLandParticle(ClientLevel level, double x, double y, double z, Fluid fluid, ParticleOptions landParticle) { + super(level, x, y, z, fluid, landParticle); + } + + @Override + protected void postMoveUpdate() { + if (onGround) { + remove(); + level.addParticle(landParticle, x, y, z, 0, 0, 0); + + if (random.nextInt(4) == 0) { + float volume = Mth.randomBetween(random, 0.4f, 0.6f); + float pitch = Mth.randomBetween(random, 1.8f, 3.4f); + level.playLocalSound(x, y, z, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, volume, pitch, false); + for (int i = 0; i < 4; i++) { + level.addParticle(ParticleTypes.LARGE_SMOKE, x + random.nextDouble(), y + 0.2f, z + random.nextDouble(), 0, 0, 0); + } + } + } + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/client/render/entity/CustomGeoProjectileRenderer.java b/src/main/java/com/github/elenterius/biomancy/client/render/entity/CustomGeoProjectileRenderer.java new file mode 100644 index 000000000..b1233b34d --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/render/entity/CustomGeoProjectileRenderer.java @@ -0,0 +1,165 @@ +package com.github.elenterius.biomancy.client.render.entity; + +import com.github.elenterius.biomancy.entity.projectile.BaseProjectile; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Matrix4f; +import com.mojang.math.Vector3f; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.Nullable; +import software.bernie.geckolib3.core.IAnimatable; +import software.bernie.geckolib3.core.IAnimatableModel; +import software.bernie.geckolib3.core.controller.AnimationController; +import software.bernie.geckolib3.core.event.predicate.AnimationEvent; +import software.bernie.geckolib3.core.util.Color; +import software.bernie.geckolib3.geo.render.built.GeoBone; +import software.bernie.geckolib3.geo.render.built.GeoModel; +import software.bernie.geckolib3.model.AnimatedGeoModel; +import software.bernie.geckolib3.model.provider.GeoModelProvider; +import software.bernie.geckolib3.model.provider.data.EntityModelData; +import software.bernie.geckolib3.renderers.geo.IGeoRenderer; +import software.bernie.geckolib3.util.AnimationUtils; +import software.bernie.geckolib3.util.EModelRenderCycle; +import software.bernie.geckolib3.util.IRenderCycle; +import software.bernie.geckolib3.util.RenderUtils; + +import javax.annotation.Nonnull; +import java.util.Collections; + +public class CustomGeoProjectileRenderer extends EntityRenderer implements IGeoRenderer { + + static { + AnimationController.addModelFetcher(animatable -> animatable instanceof Entity entity ? (IAnimatableModel) AnimationUtils.getGeoModelForEntity(entity) : null); + } + + protected final AnimatedGeoModel modelProvider; + protected float widthScale = 1; + protected float heightScale = 1; + protected Matrix4f dispatchedMat = new Matrix4f(); + protected Matrix4f renderEarlyMat = new Matrix4f(); + protected T animatable; + protected MultiBufferSource rtb = null; + private IRenderCycle currentModelRenderCycle = EModelRenderCycle.INITIAL; + + public CustomGeoProjectileRenderer(EntityRendererProvider.Context renderManager, AnimatedGeoModel modelProvider) { + super(renderManager); + this.modelProvider = modelProvider; + } + + @Override + public void render(T projectile, float yaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { + GeoModel model = modelProvider.getModel(modelProvider.getModelResource(projectile)); + dispatchedMat = poseStack.last().pose().copy(); + + setCurrentModelRenderCycle(EModelRenderCycle.INITIAL); + + poseStack.pushPose(); + + poseStack.translate(0, projectile.getBbHeight() / 2f, 0); + + poseStack.mulPose(Vector3f.YP.rotationDegrees(Mth.lerp(partialTick, projectile.yRotO, projectile.getYRot()) + 180f)); //wtf? + poseStack.mulPose(Vector3f.ZP.rotationDegrees(Mth.lerp(partialTick, projectile.xRotO, projectile.getXRot()))); + + AnimationEvent predicate = new AnimationEvent<>(projectile, 0, 0, partialTick, false, Collections.singletonList(new EntityModelData())); + modelProvider.setCustomAnimations(projectile, getInstanceId(projectile), predicate); + + if (!projectile.isInvisibleTo(Minecraft.getInstance().player)) { + ResourceLocation textureLocation = getTextureLocation(projectile); + RenderSystem.setShaderTexture(0, textureLocation); + + Color renderColor = getRenderColor(projectile, partialTick, poseStack, bufferSource, null, packedLight); + RenderType renderType = getRenderType(projectile, partialTick, poseStack, bufferSource, null, packedLight, textureLocation); + + render(model, projectile, partialTick, renderType, poseStack, bufferSource, null, packedLight, getPackedOverlay(projectile, 0), renderColor.getRed() / 255f, renderColor.getGreen() / 255f, renderColor.getBlue() / 255f, renderColor.getAlpha() / 255f); + } + + poseStack.popPose(); + + super.render(projectile, yaw, partialTick, poseStack, bufferSource, packedLight); + } + + @Override + public void renderEarly(T projectile, PoseStack poseStack, float partialTick, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + renderEarlyMat = poseStack.last().pose().copy(); + animatable = projectile; + IGeoRenderer.super.renderEarly(projectile, poseStack, partialTick, bufferSource, buffer, packedLight, packedOverlay, red, green, blue, alpha); + } + + @Override + public void renderRecursively(GeoBone bone, PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + if (bone.isTrackingXform()) { + Matrix4f poseState = poseStack.last().pose().copy(); + Matrix4f localMatrix = RenderUtils.invertAndMultiplyMatrices(poseState, dispatchedMat); + + bone.setModelSpaceXform(RenderUtils.invertAndMultiplyMatrices(poseState, renderEarlyMat)); + localMatrix.translate(new Vector3f(getRenderOffset(animatable, 1))); + bone.setLocalSpaceXform(localMatrix); + + Matrix4f worldState = localMatrix.copy(); + + worldState.translate(new Vector3f(animatable.position())); + bone.setWorldSpaceXform(worldState); + } + + IGeoRenderer.super.renderRecursively(bone, poseStack, buffer, packedLight, packedOverlay, red, green, blue, alpha); + } + + public int getPackedOverlay(T projectile, float uIn) { + return OverlayTexture.pack(OverlayTexture.u(uIn), OverlayTexture.v(false)); + } + + @Override + public GeoModelProvider getGeoModelProvider() { + return modelProvider; + } + + @Override + @Nonnull + public IRenderCycle getCurrentModelRenderCycle() { + return currentModelRenderCycle; + } + + @Override + public void setCurrentModelRenderCycle(IRenderCycle renderCycle) { + currentModelRenderCycle = renderCycle; + } + + @Override + public float getWidthScale(T projectile) { + return widthScale; + } + + @Override + public float getHeightScale(T projectile) { + return heightScale; + } + + @Override + public ResourceLocation getTextureLocation(T projectile) { + return modelProvider.getTextureResource(projectile); + } + + @Override + public int getInstanceId(T projectile) { + return projectile.getUUID().hashCode(); + } + + @Override + public MultiBufferSource getCurrentRTB() { + return rtb; + } + + @Override + public void setCurrentRTB(MultiBufferSource buffer) { + rtb = buffer; + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/AcidBlobModel.java b/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/AcidBlobModel.java new file mode 100644 index 000000000..5f0786f13 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/AcidBlobModel.java @@ -0,0 +1,29 @@ +package com.github.elenterius.biomancy.client.render.entity.acidblob; + +import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.entity.projectile.AcidBlobProjectile; +import net.minecraft.resources.ResourceLocation; +import software.bernie.geckolib3.model.AnimatedGeoModel; + +public class AcidBlobModel extends AnimatedGeoModel { + + protected static final ResourceLocation MODEL = BiomancyMod.createRL("geo/entity/acid_blob.geo.json"); + protected static final ResourceLocation TEXTURE = BiomancyMod.createRL("textures/entity/acid_blob.png"); + protected static final ResourceLocation ANIMATION = BiomancyMod.createRL("animations/entity/acid_blob.animation.json"); + + @Override + public ResourceLocation getModelResource(AcidBlobProjectile projectile) { + return MODEL; + } + + @Override + public ResourceLocation getTextureResource(AcidBlobProjectile projectile) { + return TEXTURE; + } + + @Override + public ResourceLocation getAnimationResource(AcidBlobProjectile projectile) { + return ANIMATION; + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/AcidBlobProjectileRenderer.java b/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/AcidBlobProjectileRenderer.java new file mode 100644 index 000000000..3c2348ac3 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/AcidBlobProjectileRenderer.java @@ -0,0 +1,24 @@ +package com.github.elenterius.biomancy.client.render.entity.acidblob; + +import com.github.elenterius.biomancy.client.render.entity.CustomGeoProjectileRenderer; +import com.github.elenterius.biomancy.entity.projectile.AcidBlobProjectile; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +public class AcidBlobProjectileRenderer extends CustomGeoProjectileRenderer { + + public AcidBlobProjectileRenderer(EntityRendererProvider.Context renderManager) { + super(renderManager, new AcidBlobModel()); + } + + @Override + public RenderType getRenderType(AcidBlobProjectile projectile, float partialTick, PoseStack poseStack, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, int packedLight, ResourceLocation texture) { + return RenderType.entityTranslucent(texture); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/package-info.java b/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/package-info.java new file mode 100644 index 000000000..fd9b58d8b --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/render/entity/acidblob/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.client.render.entity.acidblob; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/SapberryModel.java b/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/SapberryModel.java new file mode 100644 index 000000000..8ec448b3a --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/SapberryModel.java @@ -0,0 +1,29 @@ +package com.github.elenterius.biomancy.client.render.entity.sapberry; + +import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.entity.projectile.SapberryProjectile; +import net.minecraft.resources.ResourceLocation; +import software.bernie.geckolib3.model.AnimatedGeoModel; + +public class SapberryModel extends AnimatedGeoModel { + + protected static final ResourceLocation MODEL = BiomancyMod.createRL("geo/entity/sapberry.geo.json"); + protected static final ResourceLocation TEXTURE = BiomancyMod.createRL("textures/entity/sapberry.png"); + protected static final ResourceLocation ANIMATION = BiomancyMod.createRL("animations/entity/sapberry.animation.json"); + + @Override + public ResourceLocation getModelResource(SapberryProjectile projectile) { + return MODEL; + } + + @Override + public ResourceLocation getTextureResource(SapberryProjectile projectile) { + return TEXTURE; + } + + @Override + public ResourceLocation getAnimationResource(SapberryProjectile projectile) { + return ANIMATION; + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/SapberryProjectileRenderer.java b/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/SapberryProjectileRenderer.java new file mode 100644 index 000000000..4149ae371 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/SapberryProjectileRenderer.java @@ -0,0 +1,24 @@ +package com.github.elenterius.biomancy.client.render.entity.sapberry; + +import com.github.elenterius.biomancy.client.render.entity.CustomGeoProjectileRenderer; +import com.github.elenterius.biomancy.entity.projectile.SapberryProjectile; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +public class SapberryProjectileRenderer extends CustomGeoProjectileRenderer { + + public SapberryProjectileRenderer(EntityRendererProvider.Context renderManager) { + super(renderManager, new SapberryModel()); + } + + @Override + public RenderType getRenderType(SapberryProjectile projectile, float partialTick, PoseStack poseStack, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, int packedLight, ResourceLocation texture) { + return RenderType.entityTranslucent(texture); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/package-info.java b/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/package-info.java new file mode 100644 index 000000000..32accf036 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/client/render/entity/sapberry/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.client.render.entity.sapberry; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/client/util/ClientTextUtil.java b/src/main/java/com/github/elenterius/biomancy/client/util/ClientTextUtil.java index bf2a2277c..7b4a9768d 100644 --- a/src/main/java/com/github/elenterius/biomancy/client/util/ClientTextUtil.java +++ b/src/main/java/com/github/elenterius/biomancy/client/util/ClientTextUtil.java @@ -1,11 +1,11 @@ package com.github.elenterius.biomancy.client.util; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.init.client.ClientSetupHandler; import com.github.elenterius.biomancy.item.CustomTooltipProvider; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/github/elenterius/biomancy/client/util/GuiRenderUtil.java b/src/main/java/com/github/elenterius/biomancy/client/util/GuiRenderUtil.java index 4f7ba71d4..8926f81ec 100644 --- a/src/main/java/com/github/elenterius/biomancy/client/util/GuiRenderUtil.java +++ b/src/main/java/com/github/elenterius/biomancy/client/util/GuiRenderUtil.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.client.util; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; diff --git a/src/main/java/com/github/elenterius/biomancy/config/PrimalEnergySettings.java b/src/main/java/com/github/elenterius/biomancy/config/PrimalEnergySettings.java new file mode 100644 index 000000000..edba5d67d --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/config/PrimalEnergySettings.java @@ -0,0 +1,11 @@ +package com.github.elenterius.biomancy.config; + +public final class PrimalEnergySettings { + + private PrimalEnergySettings() {} + + public enum SupplyAmount { + NONE, LIMITED, UNLIMITED + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/config/ServerConfig.java b/src/main/java/com/github/elenterius/biomancy/config/ServerConfig.java new file mode 100644 index 000000000..9c88d0504 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/config/ServerConfig.java @@ -0,0 +1,42 @@ +package com.github.elenterius.biomancy.config; + +import net.minecraftforge.common.ForgeConfigSpec; + +public class ServerConfig { + + public final ForgeConfigSpec.BooleanValue doBioForgeRecipeProgression; + public final ForgeConfigSpec.BooleanValue addTradesToVillagers; + public final ForgeConfigSpec.BooleanValue addTradesToWanderingTrader; + public final ForgeConfigSpec.EnumValue primalEnergySupplyOfCradle; + + public ServerConfig(ForgeConfigSpec.Builder builder) { + builder.push("recipes"); + + doBioForgeRecipeProgression = builder + .comment("Determines if the BioForge recipes need to be unlocked to be able to craft them") + .define("doBioForgeRecipeProgression", true); + + builder.pop(); + + builder.push("trades"); + + addTradesToVillagers = builder + .comment("Determines if villagers will sell biomancy items") + .define("addTradesToVillagers", true); + + addTradesToWanderingTrader = builder + .comment("Determines if wandering traders will sell biomancy items") + .define("addTradesToWanderingTrader", true); + + builder.pop(); + + builder.push("flesh-growth"); + + primalEnergySupplyOfCradle = builder + .comment("Determines how much primal energy the Cradle can supply to nearby malignant flesh veins") + .defineEnum("primalEnergySupplyOfCradle", PrimalEnergySettings.SupplyAmount.LIMITED); + + builder.pop(); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/BioForgeRecipe.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioForgeRecipe.java similarity index 99% rename from src/main/java/com/github/elenterius/biomancy/recipe/BioForgeRecipe.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioForgeRecipe.java index 82b520e87..02a82126e 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/BioForgeRecipe.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioForgeRecipe.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/BioLabRecipe.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioLabRecipe.java similarity index 99% rename from src/main/java/com/github/elenterius/biomancy/recipe/BioLabRecipe.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioLabRecipe.java index 434ff98ef..a16f9bf24 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/BioLabRecipe.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioLabRecipe.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/DecomposerRecipe.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/DecomposerRecipe.java similarity index 98% rename from src/main/java/com/github/elenterius/biomancy/recipe/DecomposerRecipe.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/DecomposerRecipe.java index 6a20f6409..4dfbe2716 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/DecomposerRecipe.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/DecomposerRecipe.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/DigesterRecipe.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/DigesterRecipe.java similarity index 98% rename from src/main/java/com/github/elenterius/biomancy/recipe/DigesterRecipe.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/DigesterRecipe.java index 7eb71444a..4842a0f15 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/DigesterRecipe.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/DigesterRecipe.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/FoodNutritionIngredient.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/FoodNutritionIngredient.java similarity index 98% rename from src/main/java/com/github/elenterius/biomancy/recipe/FoodNutritionIngredient.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/FoodNutritionIngredient.java index 534d69b34..c854bcbd7 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/FoodNutritionIngredient.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/FoodNutritionIngredient.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.google.gson.JsonElement; import com.google.gson.JsonObject; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/IngredientStack.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/IngredientStack.java similarity index 93% rename from src/main/java/com/github/elenterius/biomancy/recipe/IngredientStack.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/IngredientStack.java index 39959e3c8..ed282f627 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/IngredientStack.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/IngredientStack.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -13,7 +13,7 @@ public record IngredientStack(Ingredient ingredient, int count) { - public static final String ALT_INGREDIENT_KEY = "alt"; + public static final String ALT_INGREDIENT_KEY = "alt"; //legacy support, unused by Biomancy public static final String COUNT_KEY = "count"; public boolean testItem(@Nullable ItemStack stack) { diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/ItemCountRange.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/ItemCountRange.java similarity index 99% rename from src/main/java/com/github/elenterius/biomancy/recipe/ItemCountRange.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/ItemCountRange.java index f02a45af9..0cf802631 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/ItemCountRange.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/ItemCountRange.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.google.gson.JsonObject; import net.minecraft.network.FriendlyByteBuf; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/ProcessingRecipe.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/ProcessingRecipe.java similarity index 94% rename from src/main/java/com/github/elenterius/biomancy/recipe/ProcessingRecipe.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/ProcessingRecipe.java index cb8450de2..50e43769d 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/ProcessingRecipe.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/ProcessingRecipe.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.Container; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/RecipeUtil.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/RecipeUtil.java similarity index 97% rename from src/main/java/com/github/elenterius/biomancy/recipe/RecipeUtil.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/RecipeUtil.java index 9757a7469..7d2b5bfec 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/RecipeUtil.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/RecipeUtil.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.google.gson.JsonArray; import com.google.gson.JsonObject; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/SimpleRecipeType.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/SimpleRecipeType.java similarity index 97% rename from src/main/java/com/github/elenterius/biomancy/recipe/SimpleRecipeType.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/SimpleRecipeType.java index d6e0c5683..8c723a8cf 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/SimpleRecipeType.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/SimpleRecipeType.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.Container; diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/VariableProductionOutput.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/VariableProductionOutput.java similarity index 98% rename from src/main/java/com/github/elenterius/biomancy/recipe/VariableProductionOutput.java rename to src/main/java/com/github/elenterius/biomancy/crafting/recipe/VariableProductionOutput.java index e639535a2..eafec41dc 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/VariableProductionOutput.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/VariableProductionOutput.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.crafting.recipe; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; diff --git a/src/main/java/com/github/elenterius/biomancy/crafting/recipe/package-info.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/package-info.java new file mode 100644 index 000000000..fa11be485 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.crafting.recipe; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/block/state/CraftingState.java b/src/main/java/com/github/elenterius/biomancy/crafting/state/CraftingState.java similarity index 92% rename from src/main/java/com/github/elenterius/biomancy/block/state/CraftingState.java rename to src/main/java/com/github/elenterius/biomancy/crafting/state/CraftingState.java index b19ce7faa..ba31ad255 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/state/CraftingState.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/state/CraftingState.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.block.state; +package com.github.elenterius.biomancy.crafting.state; import net.minecraft.nbt.CompoundTag; diff --git a/src/main/java/com/github/elenterius/biomancy/crafting/state/FuelConsumingRecipeCraftingStateData.java b/src/main/java/com/github/elenterius/biomancy/crafting/state/FuelConsumingRecipeCraftingStateData.java new file mode 100644 index 000000000..9f0845c76 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/crafting/state/FuelConsumingRecipeCraftingStateData.java @@ -0,0 +1,40 @@ +package com.github.elenterius.biomancy.crafting.state; + +import com.github.elenterius.biomancy.crafting.recipe.ProcessingRecipe; +import com.github.elenterius.biomancy.util.fuel.IFuelHandler; + +public abstract class FuelConsumingRecipeCraftingStateData extends RecipeCraftingStateData { + + public static final int FUEL_INDEX = 3; + + public final IFuelHandler fuelHandler; + + protected FuelConsumingRecipeCraftingStateData(IFuelHandler fuelHandler) { + this.fuelHandler = fuelHandler; + } + + @Override + public int getFuelCost() { + return fuelHandler.getFuelCost(nutrientsCost); + } + + @Override + public int get(int index) { + if (index == FUEL_INDEX) return fuelHandler.getFuelAmount(); + return super.get(index); + } + + @Override + public void set(int index, int value) { + if (index == FUEL_INDEX) { + fuelHandler.setFuelAmount(value); + return; + } + super.set(index, value); + } + + @Override + public int getCount() { + return 4; + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/block/state/RecipeCraftingStateData.java b/src/main/java/com/github/elenterius/biomancy/crafting/state/RecipeCraftingStateData.java similarity index 70% rename from src/main/java/com/github/elenterius/biomancy/block/state/RecipeCraftingStateData.java rename to src/main/java/com/github/elenterius/biomancy/crafting/state/RecipeCraftingStateData.java index 8fa0305bd..807324d79 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/state/RecipeCraftingStateData.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/state/RecipeCraftingStateData.java @@ -1,6 +1,6 @@ -package com.github.elenterius.biomancy.block.state; +package com.github.elenterius.biomancy.crafting.state; -import com.github.elenterius.biomancy.recipe.ProcessingRecipe; +import com.github.elenterius.biomancy.crafting.recipe.ProcessingRecipe; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.ContainerData; @@ -12,15 +12,18 @@ public abstract class RecipeCraftingStateData implements ContainerData { - public static final String NBT_KEY_RECIPE_ID = "RecipeId"; - public static final String NBT_KEY_TIME_ELAPSED = "TimeElapsed"; - public static final String NBT_KEY_TIME_FOR_COMPLETION = "TimeForCompletion"; + public static final String RECIPE_ID_KEY = "RecipeId"; + public static final String TIME_ELAPSED_KEY = "TimeElapsed"; + public static final String TIME_FOR_COMPLETION_KEY = "TimeForCompletion"; + public static final String NUTRIENTS_COST_KEY = "NutrientsCost"; public static final int TIME_INDEX = 0; public static final int TIME_FOR_COMPLETION_INDEX = 1; + public static final int NUTRIENTS_COST_INDEX = 2; public int timeElapsed; public int timeForCompletion; + public int nutrientsCost; private CraftingState craftingState = CraftingState.NONE; private ResourceLocation recipeId; @@ -63,6 +66,7 @@ public Optional getCraftingGoalRecipe(Level world) { public void setCraftingGoalRecipe(T recipe) { recipeId = recipe.getId(); timeForCompletion = recipe.getCraftingTimeTicks(); + nutrientsCost = recipe.getCraftingCostNutrients(); } public abstract int getFuelCost(); @@ -71,26 +75,29 @@ public void clear() { recipeId = null; timeElapsed = 0; timeForCompletion = 0; + nutrientsCost = 0; } public void serialize(CompoundTag tag) { CraftingState.toNBT(tag, craftingState); if (recipeId != null) { - tag.putString(NBT_KEY_RECIPE_ID, recipeId.toString()); + tag.putString(RECIPE_ID_KEY, recipeId.toString()); } - tag.putInt(NBT_KEY_TIME_ELAPSED, timeElapsed); - tag.putInt(NBT_KEY_TIME_FOR_COMPLETION, timeForCompletion); + tag.putInt(TIME_ELAPSED_KEY, timeElapsed); + tag.putInt(TIME_FOR_COMPLETION_KEY, timeForCompletion); + tag.putInt(NUTRIENTS_COST_KEY, nutrientsCost); } public void deserialize(CompoundTag tag) { craftingState = CraftingState.fromNBT(tag); - if (tag.contains(NBT_KEY_RECIPE_ID)) { - String id = tag.getString(NBT_KEY_RECIPE_ID); + if (tag.contains(RECIPE_ID_KEY)) { + String id = tag.getString(RECIPE_ID_KEY); recipeId = ResourceLocation.tryParse(id); } else recipeId = null; - timeElapsed = tag.getInt(NBT_KEY_TIME_ELAPSED); - timeForCompletion = tag.getInt(NBT_KEY_TIME_FOR_COMPLETION); + timeElapsed = tag.getInt(TIME_ELAPSED_KEY); + timeForCompletion = tag.getInt(TIME_FOR_COMPLETION_KEY); + nutrientsCost = tag.getInt(NUTRIENTS_COST_KEY); } protected void validateIndex(int index) { @@ -102,6 +109,7 @@ public int get(int index) { validateIndex(index); if (index == TIME_INDEX) return timeElapsed; else if (index == TIME_FOR_COMPLETION_INDEX) return timeForCompletion; + else if (index == NUTRIENTS_COST_INDEX) return nutrientsCost; return 0; } @@ -110,11 +118,12 @@ public void set(int index, int value) { validateIndex(index); if (index == TIME_INDEX) timeElapsed = value; else if (index == TIME_FOR_COMPLETION_INDEX) timeForCompletion = value; + else if (index == NUTRIENTS_COST_INDEX) nutrientsCost = value; } @Override public int getCount() { - return 2; + return 3; } } diff --git a/src/main/java/com/github/elenterius/biomancy/crafting/state/package-info.java b/src/main/java/com/github/elenterius/biomancy/crafting/state/package-info.java new file mode 100644 index 000000000..fa6df2eba --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/crafting/state/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.crafting.state; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/entity/ownable/Fleshkin.java b/src/main/java/com/github/elenterius/biomancy/entity/ownable/Fleshkin.java index b06216f2e..8b2ae84dc 100644 --- a/src/main/java/com/github/elenterius/biomancy/entity/ownable/Fleshkin.java +++ b/src/main/java/com/github/elenterius/biomancy/entity/ownable/Fleshkin.java @@ -1,10 +1,10 @@ package com.github.elenterius.biomancy.entity.ownable; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.entity.ai.goal.controllable.FollowOwnerGoal; import com.github.elenterius.biomancy.entity.ai.goal.controllable.*; import com.github.elenterius.biomancy.ownable.IOwnableMob; import com.github.elenterius.biomancy.styles.TextComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/github/elenterius/biomancy/entity/projectile/AcidBlobProjectile.java b/src/main/java/com/github/elenterius/biomancy/entity/projectile/AcidBlobProjectile.java new file mode 100644 index 000000000..c76ceca09 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/entity/projectile/AcidBlobProjectile.java @@ -0,0 +1,76 @@ +package com.github.elenterius.biomancy.entity.projectile; + +import com.github.elenterius.biomancy.block.veins.FleshVeinsBlock; +import com.github.elenterius.biomancy.init.ModBlocks; +import com.github.elenterius.biomancy.init.ModEntityTypes; +import com.github.elenterius.biomancy.init.ModFluids; +import com.github.elenterius.biomancy.init.ModParticleTypes; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import software.bernie.geckolib3.core.IAnimatable; +import software.bernie.geckolib3.core.manager.AnimationData; +import software.bernie.geckolib3.core.manager.AnimationFactory; +import software.bernie.geckolib3.util.GeckoLibUtil; + +public class AcidBlobProjectile extends CorrosiveAcidProjectile implements IAnimatable { + + protected final AnimationFactory animationFactory = GeckoLibUtil.createFactory(this); + + public AcidBlobProjectile(EntityType entityType, Level level) { + super(entityType, level); + } + + public AcidBlobProjectile(Level level, double x, double y, double z) { + super(ModEntityTypes.ACID_BLOB_PROJECTILE.get(), level, x, y, z); + } + + @Override + protected void onHitBlock(BlockHitResult result) { + BlockPos pos = result.getBlockPos(); + BlockPos posRelative = pos.relative(result.getDirection()); + BlockState stateRelative = level.getBlockState(posRelative); + + if (stateRelative.canBeReplaced(ModFluids.ACID.get())) { + BlockState stateBelow = level.getBlockState(posRelative.below()); + if (stateBelow.is(ModBlocks.ACID_FLUID_BLOCK.get()) || stateBelow.isFaceSturdy(level, posRelative.below(), Direction.UP)) { + if (!level.isClientSide) { + level.setBlock(posRelative, ModBlocks.ACID_FLUID_BLOCK.get().defaultBlockState(), Block.UPDATE_CLIENTS); + } + playHitSound(); + return; + } + else if (stateRelative.getBlock() instanceof FleshVeinsBlock) { + if (!level.isClientSide) { + level.setBlock(posRelative, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS); + } + playHitSound(); + return; + } + } + + super.onHitBlock(result); + } + + @Override + protected ParticleOptions getParticle() { + return ModParticleTypes.FALLING_ACID.get(); + } + + @Override + public void registerControllers(AnimationData data) { + //do nothing + } + + @Override + public AnimationFactory getFactory() { + return animationFactory; + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/entity/projectile/BaseProjectile.java b/src/main/java/com/github/elenterius/biomancy/entity/projectile/BaseProjectile.java index 188e50f20..0623bc245 100644 --- a/src/main/java/com/github/elenterius/biomancy/entity/projectile/BaseProjectile.java +++ b/src/main/java/com/github/elenterius/biomancy/entity/projectile/BaseProjectile.java @@ -7,7 +7,6 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.level.Level; @@ -26,9 +25,9 @@ public abstract class BaseProjectile extends Projectile implements IEntityAdditi private float damage = 2f; private byte knockback = 0; -// private double accelerationX = 0; -// private double accelerationY = 0; -// private double accelerationZ = 0; + // private double accelerationX = 0; + // private double accelerationY = 0; + // private double accelerationZ = 0; protected BaseProjectile(EntityType entityType, Level level) { super(entityType, level); @@ -39,11 +38,6 @@ protected BaseProjectile(EntityType entityType, Level setPos(x, y, z); } - protected BaseProjectile(EntityType entityType, Level level, LivingEntity shooter) { - this(entityType, level, shooter.getX(), shooter.getEyeY() - 0.1f, shooter.getZ()); - setOwner(shooter); - } - @Override protected void defineSynchedData() {} @@ -56,18 +50,18 @@ public Packet getAddEntityPacket() { public void writeSpawnData(FriendlyByteBuf buffer) { Entity shooter = getOwner(); buffer.writeVarInt(shooter == null ? 0 : shooter.getId()); -// buffer.writeDouble(accelerationX); -// buffer.writeDouble(accelerationY); -// buffer.writeDouble(accelerationZ); + // buffer.writeDouble(accelerationX); + // buffer.writeDouble(accelerationY); + // buffer.writeDouble(accelerationZ); } @Override public void readSpawnData(FriendlyByteBuf buffer) { Entity shooter = level.getEntity(buffer.readVarInt()); setOwner(shooter); -// accelerationX = buffer.readDouble(); -// accelerationY = buffer.readDouble(); -// accelerationZ = buffer.readDouble(); + // accelerationX = buffer.readDouble(); + // accelerationY = buffer.readDouble(); + // accelerationZ = buffer.readDouble(); } @Override @@ -87,12 +81,12 @@ public void readAdditionalSaveData(CompoundTag tag) { @Override public void shoot(double x, double y, double z, float velocity, float inaccuracy) { super.shoot(x, y, z, velocity, inaccuracy); -// double magnitude = getMotion().length(); -// if (magnitude != 0.0D) { -// accelerationX = getMotion().x / magnitude * 0.1d; -// accelerationY = getMotion().y / magnitude * 0.1d; -// accelerationZ = getMotion().z / magnitude * 0.1d; -// } + // double magnitude = getMotion().length(); + // if (magnitude != 0.0D) { + // accelerationX = getMotion().x / magnitude * 0.1d; + // accelerationY = getMotion().y / magnitude * 0.1d; + // accelerationZ = getMotion().z / magnitude * 0.1d; + // } } public float getDamage() { @@ -191,12 +185,6 @@ public float getPickRadius() { return 1f; } - //TODO: Replace this code. - /*@Override - public float getBrightness() { - return 1f; - }*/ - @Override public boolean shouldRenderAtSqrDistance(double distance) { double dist = getBoundingBox().getSize() * 10d; diff --git a/src/main/java/com/github/elenterius/biomancy/entity/projectile/CorrosiveAcidProjectile.java b/src/main/java/com/github/elenterius/biomancy/entity/projectile/CorrosiveAcidProjectile.java index 29b5c677b..c4ebd7134 100644 --- a/src/main/java/com/github/elenterius/biomancy/entity/projectile/CorrosiveAcidProjectile.java +++ b/src/main/java/com/github/elenterius/biomancy/entity/projectile/CorrosiveAcidProjectile.java @@ -24,8 +24,8 @@ public CorrosiveAcidProjectile(Level level, double x, double y, double z) { super(ModEntityTypes.CORROSIVE_ACID_PROJECTILE.get(), level, x, y, z); } - public CorrosiveAcidProjectile(Level level, LivingEntity shooter) { - super(ModEntityTypes.CORROSIVE_ACID_PROJECTILE.get(), level, shooter); + public CorrosiveAcidProjectile(EntityType entityType, Level level, double x, double y, double z) { + super(entityType, level, x, y, z); } @Override @@ -59,7 +59,7 @@ public boolean hurt(DamageSource source, float amount) { @Override protected void onHitBlock(BlockHitResult result) { super.onHitBlock(result); - playSound(SoundEvents.SLIME_BLOCK_BREAK, 1, 1.2f / (random.nextFloat() * 0.2f + 0.9f)); + playHitSound(); } @Override @@ -80,6 +80,10 @@ protected void onHitEntity(EntityHitResult result) { } } + playHitSound(); + } + + protected void playHitSound() { playSound(SoundEvents.SLIME_BLOCK_BREAK, 1, 1.2f / (random.nextFloat() * 0.2f + 0.9f)); } diff --git a/src/main/java/com/github/elenterius/biomancy/entity/projectile/SapberryProjectile.java b/src/main/java/com/github/elenterius/biomancy/entity/projectile/SapberryProjectile.java new file mode 100644 index 000000000..225ab81d2 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/entity/projectile/SapberryProjectile.java @@ -0,0 +1,83 @@ +package com.github.elenterius.biomancy.entity.projectile; + +import com.github.elenterius.biomancy.init.ModEntityTypes; +import com.github.elenterius.biomancy.world.PrimordialEcosystem; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; +import software.bernie.geckolib3.core.IAnimatable; +import software.bernie.geckolib3.core.manager.AnimationData; +import software.bernie.geckolib3.core.manager.AnimationFactory; +import software.bernie.geckolib3.util.GeckoLibUtil; + +public class SapberryProjectile extends BaseProjectile implements IAnimatable { + + protected final AnimationFactory animationFactory = GeckoLibUtil.createFactory(this); + + public SapberryProjectile(EntityType entityType, Level level) { + super(entityType, level); + } + + public SapberryProjectile(Level level, double x, double y, double z) { + super(ModEntityTypes.SAPBERRY_PROJECTILE.get(), level, x, y, z); + } + + @Override + public float getGravity() { + return 0.025f; + } + + @Override + public boolean isPickable() { + return false; + } + + @Override + public boolean hurt(DamageSource source, float amount) { + return false; //explode on death? + } + + @Override + protected void onHitBlock(BlockHitResult result) { + super.onHitBlock(result); + if (level instanceof ServerLevel serverLevel) { + Direction direction = result.getDirection(); + PrimordialEcosystem.placeMalignantBloomOrBlocks(serverLevel, result.getBlockPos(), direction); + } + playSound(SoundEvents.SLIME_BLOCK_BREAK, 1, 1.2f / (random.nextFloat() * 0.2f + 0.9f)); + } + + @Override + protected void onHitEntity(EntityHitResult result) { + super.onHitEntity(result); + if (level instanceof ServerLevel serverLevel) { + Direction direction = Direction.orderedByNearest(this)[0]; + BlockPos pos = new BlockPos(result.getLocation()); + PrimordialEcosystem.placeMalignantBloomOrBlocks(serverLevel, pos, direction); + } + playSound(SoundEvents.SLIME_BLOCK_BREAK, 1, 1.2f / (random.nextFloat() * 0.2f + 0.9f)); + } + + @Override + protected ParticleOptions getParticle() { + return ParticleTypes.SPIT; + } + + @Override + public void registerControllers(AnimationData data) { + //do nothing + } + + @Override + public AnimationFactory getFactory() { + return animationFactory; + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/entity/projectile/ToothProjectile.java b/src/main/java/com/github/elenterius/biomancy/entity/projectile/ToothProjectile.java index ab5367274..7b24bcc35 100644 --- a/src/main/java/com/github/elenterius/biomancy/entity/projectile/ToothProjectile.java +++ b/src/main/java/com/github/elenterius/biomancy/entity/projectile/ToothProjectile.java @@ -33,10 +33,6 @@ public ToothProjectile(Level world, double x, double y, double z) { super(ModEntityTypes.TOOTH_PROJECTILE.get(), world, x, y, z); } - public ToothProjectile(Level world, LivingEntity shooter) { - super(ModEntityTypes.TOOTH_PROJECTILE.get(), world, shooter); - } - @Override public float getGravity() { return 0.01f; diff --git a/src/main/java/com/github/elenterius/biomancy/entity/projectile/WitherProjectile.java b/src/main/java/com/github/elenterius/biomancy/entity/projectile/WitherProjectile.java index afd972370..802789eef 100644 --- a/src/main/java/com/github/elenterius/biomancy/entity/projectile/WitherProjectile.java +++ b/src/main/java/com/github/elenterius/biomancy/entity/projectile/WitherProjectile.java @@ -25,10 +25,6 @@ public WitherProjectile(Level world, double x, double y, double z) { super(ModEntityTypes.WITHER_SKULL_PROJECTILE.get(), world, x, y, z); } - public WitherProjectile(Level world, LivingEntity shooter) { - super(ModEntityTypes.WITHER_SKULL_PROJECTILE.get(), world, shooter); - } - @Override public float getGravity() { return 0.001f; diff --git a/src/main/java/com/github/elenterius/biomancy/event/LivingEventHandler.java b/src/main/java/com/github/elenterius/biomancy/event/LivingEventHandler.java new file mode 100644 index 000000000..dca5a9819 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/event/LivingEventHandler.java @@ -0,0 +1,19 @@ +package com.github.elenterius.biomancy.event; + +import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.fluid.AcidFluid; +import net.minecraftforge.event.entity.living.LivingEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(modid = BiomancyMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE) +public final class LivingEventHandler { + + private LivingEventHandler() {} + + @SubscribeEvent + public static void onLivingTick(final LivingEvent.LivingTickEvent event) { + AcidFluid.onEntityInside(event.getEntity()); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/event/MobSpawnHandler.java b/src/main/java/com/github/elenterius/biomancy/event/MobSpawnHandler.java new file mode 100644 index 000000000..cfc1f51b3 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/event/MobSpawnHandler.java @@ -0,0 +1,49 @@ +package com.github.elenterius.biomancy.event; + +import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.world.MobSpawnFilter; +import com.github.elenterius.biomancy.world.spatial.SpatialShapeManager; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraftforge.event.entity.living.LivingSpawnEvent; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import java.util.function.Predicate; + +@Mod.EventBusSubscriber(modid = BiomancyMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE) +public final class MobSpawnHandler { + + private MobSpawnHandler() {} + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void onCheckSpawn(LivingSpawnEvent.CheckSpawn event) { + if (event.isCanceled()) return; + + if (event.getLevel() instanceof ServerLevel serverLevel) { + MobSpawnType spawnReason = event.getSpawnReason(); + + //TODO: check unnatural spawns as well?? + if (MobSpawnFilter.isNaturalSpawn(spawnReason)) { + Mob mob = event.getEntity(); + double x = event.getX(); + double y = event.getY(); + double z = event.getZ(); + + Predicate denySpawnPredicate = shape -> shape instanceof MobSpawnFilter filter && !filter.isMobAllowedToSpawn(mob, spawnReason, serverLevel, x, y, z); + boolean denySpawn = SpatialShapeManager.getAnyShape(serverLevel, mob, SpatialShapeManager.QueryStrategy.INTERSECTION, denySpawnPredicate) != null; + + if (denySpawn) { + //TODO: chamber specific mob spawn filters? --> e.g. chamber only allows creepers spawns + //MoundChamber chamber = moundShape.getChamberAt(pos.getX(), pos.getY(), pos.getZ()); + event.setResult(Event.Result.DENY); + } + } + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/fluid/AcidFluid.java b/src/main/java/com/github/elenterius/biomancy/fluid/AcidFluid.java new file mode 100644 index 000000000..e2334aedf --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/fluid/AcidFluid.java @@ -0,0 +1,218 @@ +package com.github.elenterius.biomancy.fluid; + +import com.github.elenterius.biomancy.block.veins.FleshVeinsBlock; +import com.github.elenterius.biomancy.init.ModFluids; +import com.github.elenterius.biomancy.init.tags.ModBlockTags; +import com.github.elenterius.biomancy.util.CombatUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.fluids.ForgeFlowingFluid; + +import java.util.Map; + +public abstract class AcidFluid extends ForgeFlowingFluid { + + protected static final Map NORMAL_TO_ERODED_BLOCK_CONVERSION = Map.of( + Blocks.GRASS_BLOCK, Blocks.DIRT.defaultBlockState(), + Blocks.COBBLESTONE, Blocks.GRAVEL.defaultBlockState(), + Blocks.STONE_BRICKS, Blocks.CRACKED_STONE_BRICKS.defaultBlockState(), + Blocks.DEEPSLATE_BRICKS, Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState(), + Blocks.DEEPSLATE_TILES, Blocks.CRACKED_DEEPSLATE_TILES.defaultBlockState(), + Blocks.POLISHED_BLACKSTONE_BRICKS, Blocks.CRACKED_POLISHED_BLACKSTONE_BRICKS.defaultBlockState(), + Blocks.NETHER_BRICKS, Blocks.CRACKED_NETHER_BRICKS.defaultBlockState() + ); + + protected AcidFluid(Properties properties) { + super(properties); + } + + public static void onEntityInside(LivingEntity livingEntity) { + if (livingEntity.tickCount % 5 == 0 && livingEntity.isInFluidType(ModFluids.ACID_TYPE.get())) { + if (!livingEntity.level.isClientSide) { + CombatUtil.applyAcidEffect(livingEntity, 4); + } + else if (livingEntity.tickCount % 10 == 0 && livingEntity.getRandom().nextFloat() < 0.4f) { + Level level = livingEntity.level; + RandomSource random = livingEntity.getRandom(); + Vec3 pos = livingEntity.position(); + double height = livingEntity.getBoundingBox().getYsize() * 0.5f; + + level.playLocalSound(pos.x, pos.y, pos.z, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.5f, 2.6f + (random.nextFloat() - random.nextFloat()) * 0.8f, false); + for (int i = 0; i < 4; i++) { + level.addParticle(ParticleTypes.LARGE_SMOKE, pos.x + random.nextDouble(), pos.y + random.nextDouble() * height, pos.z + random.nextDouble(), 0, 0.1d, 0); + } + } + } + } + + @Override + protected boolean canPassThrough(BlockGetter level, Fluid fluid, BlockPos fromPos, BlockState fromBlockState, Direction direction, BlockPos toPos, BlockState toBlockState, FluidState toFluidState) { + return toBlockState.getBlock() instanceof FleshVeinsBlock || super.canPassThrough(level, fluid, fromPos, fromBlockState, direction, toPos, toBlockState, toFluidState); + } + + @Override + protected boolean canSpreadTo(BlockGetter level, BlockPos fromPos, BlockState fromBlockState, Direction direction, BlockPos toPos, BlockState toBlockState, FluidState toFluidState, Fluid fluid) { + return toBlockState.getBlock() instanceof FleshVeinsBlock || super.canSpreadTo(level, fromPos, fromBlockState, direction, toPos, toBlockState, toFluidState, fluid); + } + + @Override + protected void spreadTo(LevelAccessor level, BlockPos pos, BlockState state, Direction direction, FluidState fluidState) { + if (state.getBlock() instanceof FleshVeinsBlock) { + beforeDestroyingBlock(level, pos, state); + level.setBlock(pos, fluidState.createLegacyBlock(), Block.UPDATE_ALL); + return; + } + + super.spreadTo(level, pos, state, direction, fluidState); + } + + @Override + protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) { + if (state.is(ModBlockTags.ACID_DESTRUCTIBLE)) return; //don't drop any block resources i.e. "destroy" them + + super.beforeDestroyingBlock(level, pos, state); + } + + @Override + protected boolean isRandomlyTicking() { + return true; + } + + @Override + protected void randomTick(Level level, BlockPos liquidPos, FluidState fluidState, RandomSource random) { + if (level.random.nextFloat() > 0.4f) return; + + for (int i = 0; i < 3; i++) { + float p = level.random.nextFloat(); + int yOffset = 0; + if (p < 0.2f) yOffset = 1; + else if (p < 0.6f) yOffset = -1; + BlockPos blockPos = liquidPos.offset(random.nextInt(3) - 1, yOffset, random.nextInt(3) - 1); + + if (!level.isLoaded(blockPos)) return; + + BlockState blockState = level.getBlockState(blockPos); + if (blockState.isAir()) return; + + if (level.random.nextFloat() >= 0.1f) { + Block block = blockState.getBlock(); + if (corrodeCopper(level, liquidPos, block, blockState, blockPos)) continue; + if (destroyBlock(level, liquidPos, block, blockState, blockPos)) continue; + if (erodeBlock(level, liquidPos, block, blockState, blockPos)) continue; + + if (fluidState.getAmount() > 2) { + destroyFleshVeins(level, liquidPos, block, blockState, blockPos); + } + } + else if (fluidState.getAmount() > 2) { + Block block = blockState.getBlock(); + destroyFleshVeins(level, liquidPos, block, blockState, blockPos); + } + } + } + + protected boolean corrodeCopper(Level level, BlockPos liquidPos, Block block, BlockState blockState, BlockPos pos) { + if (block instanceof WeatheringCopper weatheringCopper && WeatheringCopper.getNext(block).isPresent()) { + weatheringCopper.getNext(blockState).ifPresent(state -> level.setBlockAndUpdate(pos, ForgeEventFactory.fireFluidPlaceBlockEvent(level, pos, liquidPos, state))); + level.levelEvent(LevelEvent.LAVA_FIZZ, pos, 0); + return true; + } + + return false; + } + + protected boolean destroyBlock(Level level, BlockPos liquidPos, Block block, BlockState blockState, BlockPos pos) { + if (!blockState.is(ModBlockTags.ACID_DESTRUCTIBLE)) return false; + + SoundType soundType = block.getSoundType(blockState, level, pos, null); + level.setBlockAndUpdate(pos, ForgeEventFactory.fireFluidPlaceBlockEvent(level, pos, liquidPos, Blocks.AIR.defaultBlockState())); + level.playSound(null, pos, soundType.getBreakSound(), SoundSource.BLOCKS, soundType.volume, soundType.pitch); + level.levelEvent(LevelEvent.LAVA_FIZZ, pos, 0); + return true; + } + + protected boolean erodeBlock(Level level, BlockPos liquidPos, Block block, BlockState blockState, BlockPos pos) { + if (!NORMAL_TO_ERODED_BLOCK_CONVERSION.containsKey(block)) return false; + + SoundType soundType = block.getSoundType(blockState, level, pos, null); + level.setBlockAndUpdate(pos, ForgeEventFactory.fireFluidPlaceBlockEvent(level, pos, liquidPos, NORMAL_TO_ERODED_BLOCK_CONVERSION.get(block))); + level.playSound(null, pos, soundType.getBreakSound(), SoundSource.BLOCKS, soundType.volume, soundType.pitch); + level.levelEvent(LevelEvent.LAVA_FIZZ, pos, 0); + return true; + } + + protected void destroyFleshVeins(Level level, BlockPos liquidPos, Block block, BlockState blockState, BlockPos pos) { + if (block instanceof FleshVeinsBlock) { + level.setBlockAndUpdate(pos, ForgeEventFactory.fireFluidPlaceBlockEvent(level, pos, liquidPos, Blocks.AIR.defaultBlockState())); + level.levelEvent(LevelEvent.LAVA_FIZZ, pos, 0); + } + } + + @Override + protected void animateTick(Level level, BlockPos pos, FluidState state, RandomSource random) { + if (!state.isSource() && Boolean.FALSE.equals(state.getValue(FALLING))) { + if (random.nextInt(64) == 0) { + level.playLocalSound(pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f, SoundEvents.WATER_AMBIENT, SoundSource.BLOCKS, random.nextFloat() * 0.25f + 0.75f, random.nextFloat() + 0.5f, false); + } + } + else if (random.nextInt(10) == 0) { + level.addParticle(ParticleTypes.UNDERWATER, pos.getX() + random.nextDouble(), pos.getY() + random.nextDouble(), pos.getZ() + random.nextDouble(), 0, 0, 0); + } + } + + public static class Flowing extends AcidFluid { + public Flowing(Properties properties) { + super(properties); + registerDefaultState(getStateDefinition().any().setValue(LEVEL, 7)); + } + + @Override + protected void createFluidStateDefinition(StateDefinition.Builder builder) { + super.createFluidStateDefinition(builder); + builder.add(LEVEL); + } + + @Override + public int getAmount(FluidState state) { + return state.getValue(LEVEL); + } + + @Override + public boolean isSource(FluidState state) { + return false; + } + } + + public static class Source extends AcidFluid { + + public Source(Properties properties) { + super(properties); + } + + @Override + public int getAmount(FluidState state) { + return 8; + } + + @Override + public boolean isSource(FluidState state) { + return true; + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/fluid/TintedFluidType.java b/src/main/java/com/github/elenterius/biomancy/fluid/TintedFluidType.java new file mode 100644 index 000000000..52760ccaa --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/fluid/TintedFluidType.java @@ -0,0 +1,109 @@ +package com.github.elenterius.biomancy.fluid; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import com.mojang.math.Matrix4f; +import com.mojang.math.Vector3f; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.FastColor; +import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions; +import net.minecraftforge.fluids.FluidType; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +public class TintedFluidType extends FluidType { + + protected static final ResourceLocation STILL_TEXTURE = new ResourceLocation("block/water_still"); + protected static final ResourceLocation FLOWING_TEXTURE = new ResourceLocation("block/water_flow"); + protected static final ResourceLocation OVERLAY_TEXTURE = new ResourceLocation("block/water_overlay"); + protected static final ResourceLocation INSIDE_FLUID_TEXTURE = new ResourceLocation("textures/block/water_overlay.png"); + + protected final int colorARGB; + + public TintedFluidType(Properties properties, int colorARGB) { + super(properties); + this.colorARGB = colorARGB; + } + + public static void renderTintedScreenOverlay(Minecraft minecraft, PoseStack poseStack, ResourceLocation texture, int colorARGB) { + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.enableTexture(); + RenderSystem.setShaderTexture(0, texture); + + BlockPos blockPos = new BlockPos(minecraft.player.getX(), minecraft.player.getEyeY(), minecraft.player.getZ()); + float brightness = LightTexture.getBrightness(minecraft.player.level.dimensionType(), minecraft.player.level.getMaxLocalRawBrightness(blockPos)); + float red = (FastColor.ARGB32.red(colorARGB) / 255f) * brightness; + float green = (FastColor.ARGB32.green(colorARGB) / 255f) * brightness; + float blue = (FastColor.ARGB32.blue(colorARGB) / 255f) * brightness; + + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.setShaderColor(red, green, blue, 0.5f); + + float uOffset = -minecraft.player.getYRot() / 64f; + float vOffset = minecraft.player.getXRot() / 64f; + float depth = -0.5f; + + Matrix4f matrix4f = poseStack.last().pose(); + BufferBuilder bufferbuilder = Tesselator.getInstance().getBuilder(); + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); + bufferbuilder.vertex(matrix4f, -1f, -1f, depth).uv(4f + uOffset, 4f + vOffset).endVertex(); + bufferbuilder.vertex(matrix4f, 1f, -1f, depth).uv(uOffset, 4f + vOffset).endVertex(); + bufferbuilder.vertex(matrix4f, 1f, 1f, depth).uv(uOffset, vOffset).endVertex(); + bufferbuilder.vertex(matrix4f, -1f, 1f, depth).uv(4f + uOffset, vOffset).endVertex(); + BufferUploader.drawWithShader(bufferbuilder.end()); + + RenderSystem.disableBlend(); + } + + @Override + public void initializeClient(Consumer consumer) { + consumer.accept(new IClientFluidTypeExtensions() { + @Override + public int getTintColor() { + return colorARGB; + } + + @Override + public @NotNull Vector3f modifyFogColor(Camera camera, float partialTick, ClientLevel level, int renderDistance, float darkenWorldAmount, Vector3f fluidFogColor) { + float red = (FastColor.ARGB32.red(colorARGB) / 255f) * fluidFogColor.x(); + float green = (FastColor.ARGB32.green(colorARGB) / 255f) * fluidFogColor.y(); + float blue = (FastColor.ARGB32.blue(colorARGB) / 255f) * fluidFogColor.z(); + fluidFogColor.set(red, green, blue); + return fluidFogColor; + } + + @Override + public ResourceLocation getStillTexture() { + return STILL_TEXTURE; + } + + @Override + public ResourceLocation getFlowingTexture() { + return FLOWING_TEXTURE; + } + + @Override + public ResourceLocation getOverlayTexture() { + return OVERLAY_TEXTURE; + } + + @Override + public ResourceLocation getRenderOverlayTexture(Minecraft mc) { + return INSIDE_FLUID_TEXTURE; + } + + @Override + public void renderOverlay(Minecraft mc, PoseStack poseStack) { + renderTintedScreenOverlay(mc, poseStack, INSIDE_FLUID_TEXTURE, colorARGB); + } + }); + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/recipe/package-info.java b/src/main/java/com/github/elenterius/biomancy/fluid/package-info.java similarity index 78% rename from src/main/java/com/github/elenterius/biomancy/recipe/package-info.java rename to src/main/java/com/github/elenterius/biomancy/fluid/package-info.java index 1cc0b11ea..1173de1ce 100644 --- a/src/main/java/com/github/elenterius/biomancy/recipe/package-info.java +++ b/src/main/java/com/github/elenterius/biomancy/fluid/package-info.java @@ -1,6 +1,6 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.github.elenterius.biomancy.recipe; +package com.github.elenterius.biomancy.fluid; import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/github/elenterius/biomancy/init/CommonSetupHandler.java b/src/main/java/com/github/elenterius/biomancy/init/CommonSetupHandler.java index 6de072cb9..734f82461 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/CommonSetupHandler.java +++ b/src/main/java/com/github/elenterius/biomancy/init/CommonSetupHandler.java @@ -16,6 +16,11 @@ import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegisterEvent; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.stream.Stream; + @Mod.EventBusSubscriber(modid = BiomancyMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) public final class CommonSetupHandler { @@ -29,14 +34,32 @@ public static void onSetup(final FMLCommonSetupEvent event) { // if not thread safe do it after the common setup event on a single thread event.enqueueWork(() -> { ModTriggers.register(); + ModPredicates.registerItemPredicates(); + registerDispenserBehaviors(); ModRecipes.registerComposterRecipes(); + + //dumpBiomeTemperatureAndHumidity(); }); + ModFluids.registerInteractions(); ModRecipes.registerBrewingRecipes(); ModsCompatHandler.onBiomancyCommonSetup(event); } + public static void dumpBiomeTemperatureAndHumidity() { + BiomancyMod.LOGGER.info("dumping biome default temperatures to biome_temperatures.csv..."); + try { + Stream stringStream = ForgeRegistries.BIOMES.getEntries().stream() + .map(keyEntry -> "%s,%s,%s".formatted(keyEntry.getKey().location(), keyEntry.getValue().getBaseTemperature(), keyEntry.getValue().getDownfall())); + + Files.write(Paths.get("biome_temperatures.csv"), (Iterable) stringStream::iterator); + } + catch (IOException e) { + BiomancyMod.LOGGER.error("Failed to dump biome temps!", e); + } + } + @SubscribeEvent public static void registerRecipeSerializers(RegisterEvent event) { if (event.getRegistryKey().equals(ForgeRegistries.Keys.RECIPE_SERIALIZERS)) { diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModBlockMaterials.java b/src/main/java/com/github/elenterius/biomancy/init/ModBlockMaterials.java index fcf5db5bb..0e8c14ef9 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModBlockMaterials.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModBlockMaterials.java @@ -1,12 +1,87 @@ package com.github.elenterius.biomancy.init; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.material.Material; import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.material.PushReaction; + +import java.util.function.Predicate; public final class ModBlockMaterials { - public static final Material FLESH_MATERIAL = new Material.Builder(MaterialColor.COLOR_PINK).build(); + public static final Predicate FLESH_PREDICATE = block -> { + Material material = block.defaultBlockState().getMaterial(); + return material == ModBlockMaterials.FLESH_MATERIAL || material == ModBlockMaterials.FLESH_VEINS_MATERIAL; + }; + + public static final Material FLESH_MATERIAL = Builder.create().build(); + public static final Material FLESH_VEINS_MATERIAL = Builder.create().noCollider().notSolidBlocking().nonSolid().destroyOnPush().build(); private ModBlockMaterials() {} + public static class Builder { + private PushReaction pushReaction = PushReaction.NORMAL; + private boolean blocksMotion = true; + private boolean flammable; + private boolean liquid; + private boolean replaceable; + private boolean solid = true; + private MaterialColor color = MaterialColor.COLOR_PINK; + private boolean solidBlocking = true; + + private Builder() {} + + public static Builder create() { + return new Builder(); + } + + public Builder color(MaterialColor color) { + this.color = color; + return this; + } + + public Builder liquid() { + liquid = true; + return this; + } + + public Builder nonSolid() { + solid = false; + return this; + } + + public Builder noCollider() { + blocksMotion = false; + return this; + } + + public Builder notSolidBlocking() { + solidBlocking = false; + return this; + } + + public Builder flammable() { + flammable = true; + return this; + } + + public Builder replaceable() { + replaceable = true; + return this; + } + + public Builder destroyOnPush() { + pushReaction = PushReaction.DESTROY; + return this; + } + + public Builder notPushable() { + pushReaction = PushReaction.BLOCK; + return this; + } + + public Material build() { + return new Material(color, liquid, solid, blocksMotion, solidBlocking, flammable, replaceable, pushReaction); + } + } } diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModBlockProperties.java b/src/main/java/com/github/elenterius/biomancy/init/ModBlockProperties.java index 885d55712..9fff73050 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModBlockProperties.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModBlockProperties.java @@ -4,7 +4,6 @@ import com.github.elenterius.biomancy.util.EnhancedIntegerProperty; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.EnumProperty; -import net.minecraft.world.level.block.state.properties.IntegerProperty; public final class ModBlockProperties { public static final BooleanProperty IS_CRAFTING = BooleanProperty.create("crafting"); @@ -19,7 +18,7 @@ public final class ModBlockProperties { public static final BooleanProperty VIAL_4 = BooleanProperty.create("vial_4"); public static final EnumProperty DIRECTED_CONNECTION = EnumProperty.create("connection", DirectedConnection.class); public static final EnumProperty VERTEX_TYPE = EnumProperty.create("vertex", VertexType.class); - public static final IntegerProperty SPIKES = IntegerProperty.create("spikes", 1, 3); + public static final EnhancedIntegerProperty SPIKES = EnhancedIntegerProperty.create("spikes", 1, 3); private ModBlockProperties() {} diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModBlocks.java b/src/main/java/com/github/elenterius/biomancy/init/ModBlocks.java index 41bec7ce7..67a7cce63 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModBlocks.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModBlocks.java @@ -9,8 +9,10 @@ import com.github.elenterius.biomancy.block.digester.DigesterBlock; import com.github.elenterius.biomancy.block.fleshkinchest.FleshkinChestBlock; import com.github.elenterius.biomancy.block.fleshspike.FleshSpikeBlock; +import com.github.elenterius.biomancy.block.malignantbloom.MalignantBloomBlock; import com.github.elenterius.biomancy.block.mawhopper.MawHopperBlock; import com.github.elenterius.biomancy.block.modularlarynx.VoiceBoxBlock; +import com.github.elenterius.biomancy.block.orifice.OrificeBlock; import com.github.elenterius.biomancy.block.ownable.OwnablePressurePlateBlock; import com.github.elenterius.biomancy.block.storagesac.StorageSacBlock; import com.github.elenterius.biomancy.block.tongue.TongueBlock; @@ -35,9 +37,8 @@ public final class ModBlocks { public static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, BiomancyMod.MOD_ID); - //## Crafting + //## Manual Crafting public static final RegistryObject PRIMORDIAL_CRADLE = register("primordial_cradle", PrimordialCradleBlock::new); - public static final RegistryObject BIO_FORGE = register("bio_forge", BioForgeBlock::new); //## Machine @@ -45,15 +46,11 @@ public final class ModBlocks { public static final RegistryObject BIO_LAB = register("bio_lab", BioLabBlock::new); public static final RegistryObject DIGESTER = register("digester", DigesterBlock::new); - //## Automation & Storage + //## Automation, Redstone & Storage public static final RegistryObject STORAGE_SAC = register("storage_sac", StorageSacBlock::new); public static final RegistryObject TONGUE = register("tongue", TongueBlock::new); public static final RegistryObject MAW_HOPPER = register("maw_hopper", MawHopperBlock::new); - - //## Ownable public static final RegistryObject FLESHKIN_CHEST = register("fleshkin_chest", FleshkinChestBlock::new); - // public static final RegistryObject FLESHKIN_DOOR = register("fleshkin_door", OwnableDoorBlock::new); - // public static final RegistryObject FLESHKIN_TRAPDOOR = register("fleshkin_trapdoor", OwnableTrapDoorBlock::new); public static final RegistryObject FLESHKIN_PRESSURE_PLATE = register("fleshkin_pressure_plate", OwnablePressurePlateBlock::new); //## Building Materials @@ -65,12 +62,15 @@ public final class ModBlocks { public static final RegistryObject PACKED_FLESH_STAIRS = registerStairs(PACKED_FLESH, StairBlock::new); public static final RegistryObject PACKED_FLESH_WALL = registerWall(PACKED_FLESH, WallBlock::new); public static final RegistryObject PACKED_FLESH_SLAB = registerSlab(PACKED_FLESH, DirectionalSlabBlock::new); - public static final RegistryObject FLESH_PILLAR = register("flesh_pillar", RotatedPillarBlock::new); - public static final RegistryObject FIBROUS_FLESH = register("fibrous_flesh", FleshBlock::new); - public static final RegistryObject CHISELED_FLESH = register("chiseled_flesh", FleshBlock::new); - public static final RegistryObject ORNATE_FLESH = register("ornate_flesh", RotatedPillarBlock::new); + + //## Decoration public static final RegistryObject TUBULAR_FLESH_BLOCK = register("tubular_flesh", RotatedPillarBlock::new); + public static final RegistryObject FIBROUS_FLESH = register("fibrous_flesh", FleshBlock::new); + public static final RegistryObject FLESH_PILLAR = register("flesh_pillar", () -> new RotatedPillarBlock(createBonyFleshProperties())); + public static final RegistryObject CHISELED_FLESH = register("chiseled_flesh", () -> new FleshBlock(createBonyFleshProperties())); + public static final RegistryObject ORNATE_FLESH = register("ornate_flesh", () -> new RotatedPillarBlock(createBonyFleshProperties())); + //## Primal Ecosystem public static final RegistryObject PRIMAL_FLESH = register("primal_flesh", FleshBlock::new); public static final RegistryObject PRIMAL_FLESH_STAIRS = registerStairs(PRIMAL_FLESH, StairBlock::new); public static final RegistryObject PRIMAL_FLESH_SLAB = registerSlab(PRIMAL_FLESH, DirectionalSlabBlock::new); @@ -79,26 +79,40 @@ public final class ModBlocks { public static final RegistryObject MALIGNANT_FLESH_STAIRS = registerStairs(MALIGNANT_FLESH, StairBlock::new); public static final RegistryObject MALIGNANT_FLESH_SLAB = registerSlab(MALIGNANT_FLESH, DirectionalSlabBlock::new); public static final RegistryObject MALIGNANT_FLESH_WALL = registerWall(MALIGNANT_FLESH, WallBlock::new); - public static final RegistryObject MALIGNANT_FLESH_VEINS = register("malignant_flesh_veins", properties -> new FleshVeinsBlock(properties.noCollission().noOcclusion())); + public static final RegistryObject MALIGNANT_FLESH_VEINS = register("malignant_flesh_veins", () -> new FleshVeinsBlock(createFleshVeinsProperties().noCollission().noOcclusion())); + public static final RegistryObject MALIGNANT_BLOOM = register("malignant_bloom", properties -> new MalignantBloomBlock(properties.randomTicks().noOcclusion().lightLevel(MalignantBloomBlock::getLightEmission))); + public static final RegistryObject BLOOMLIGHT = register("bloomlight", properties -> new Block(properties.sound(SoundType.SHROOMLIGHT).lightLevel(x -> 15))); + public static final RegistryObject PRIMAL_ORIFICE = register("primal_orifice", properties -> new OrificeBlock(properties.randomTicks())); - //## Misc + //## Utility public static final RegistryObject VOICE_BOX = register("voice_box", VoiceBoxBlock::new); + public static final RegistryObject FLESH_SPIKE = register("flesh_spike", () -> new FleshSpikeBlock(createBonyFleshProperties().noOcclusion())); + public static final RegistryObject VIAL_HOLDER = register("vial_holder", VialHolderBlock::new); + //public static final RegistryObject NEURAL_INTERCEPTOR = register("neural_interceptor", NeuralInterceptorBlock::new); + + //## Membranes public static final RegistryObject IMPERMEABLE_MEMBRANE = registerMembrane("impermeable_membrane", MembraneBlock.IgnoreEntityCollisionPredicate.NEVER); public static final RegistryObject BABY_PERMEABLE_MEMBRANE = registerMembrane("baby_permeable_membrane", MembraneBlock.IgnoreEntityCollisionPredicate.IS_BABY_MOB); public static final RegistryObject ADULT_PERMEABLE_MEMBRANE = registerMembrane("adult_permeable_membrane", MembraneBlock.IgnoreEntityCollisionPredicate.IS_ADULT_MOB); + public static final RegistryObject PRIMAL_PERMEABLE_MEMBRANE = registerMembrane("primal_permeable_membrane", MembraneBlock.IgnoreEntityCollisionPredicate.IS_ALIVE_MOB, SpreadingMembraneBlock::new); + public static final RegistryObject UNDEAD_PERMEABLE_MEMBRANE = registerMembrane("undead_permeable_membrane", MembraneBlock.IgnoreEntityCollisionPredicate.IS_UNDEAD_MOB); + + //## Light Sources + public static final RegistryObject PRIMORDIAL_BIO_LANTERN = register("primordial_bio_lantern", properties -> new FleshLanternBlock(properties.sound(SoundType.SHROOMLIGHT).lightLevel(x -> 15).noOcclusion())); + public static final RegistryObject YELLOW_BIO_LANTERN = register("bio_lantern_yellow", properties -> new FleshLanternBlock(properties.sound(SoundType.SHROOMLIGHT).lightLevel(x -> 15).noOcclusion())); + public static final RegistryObject BLUE_BIO_LANTERN = register("bio_lantern_blue", properties -> new FleshLanternBlock(properties.sound(SoundType.SHROOMLIGHT).lightLevel(x -> 15).noOcclusion())); - public static final RegistryObject FLESH_LADDER = register("flesh_ladder", () -> new LadderBlock(createFleshyBoneProperties().noOcclusion())); + //## Fluids + public static final RegistryObject ACID_FLUID_BLOCK = register("acid_fluid_block", () -> new LiquidBlock(ModFluids.ACID, copyProperties(Blocks.WATER))); + + //## Misc + public static final RegistryObject FLESH_LADDER = register("flesh_ladder", () -> new LadderBlock(createBonyFleshProperties().noOcclusion())); public static final RegistryObject FLESH_FENCE = register("flesh_fence", FleshFenceBlock::new); - public static final RegistryObject FLESH_FENCE_GATE = register("flesh_fence_gate", () -> new FleshFenceGateBlock(createFleshyBoneProperties().noOcclusion())); + public static final RegistryObject FLESH_FENCE_GATE = register("flesh_fence_gate", () -> new FleshFenceGateBlock(createBonyFleshProperties().noOcclusion())); public static final RegistryObject FLESH_IRIS_DOOR = register("flesh_iris_door", IrisDoorBlock::new); public static final RegistryObject FLESH_DOOR = register("flesh_door", FleshDoorBlock::new); public static final RegistryObject FULL_FLESH_DOOR = register("full_flesh_door", FullFleshDoorBlock::new); - public static final RegistryObject FLESH_SPIKE = register("flesh_spike", () -> new FleshSpikeBlock(createFleshyBoneProperties().noOcclusion())); - public static final RegistryObject YELLOW_BIO_LANTERN = register("bio_lantern_yellow", properties -> new FleshLanternBlock(properties.sound(SoundType.SHROOMLIGHT).lightLevel(x -> 15).noOcclusion())); - public static final RegistryObject BLUE_BIO_LANTERN = register("bio_lantern_blue", properties -> new FleshLanternBlock(properties.sound(SoundType.SHROOMLIGHT).lightLevel(x -> 15).noOcclusion())); - public static final RegistryObject PRIMORDIAL_BIO_LANTERN = register("primordial_bio_lantern", properties -> new FleshLanternBlock(properties.sound(SoundType.SHROOMLIGHT).lightLevel(x -> 15).noOcclusion())); public static final RegistryObject TENDON_CHAIN = register("tendon_chain", properties -> new FleshChainBlock(properties.noOcclusion())); - public static final RegistryObject VIAL_HOLDER = register("vial_holder", VialHolderBlock::new); private ModBlocks() {} @@ -135,9 +149,13 @@ private static RegistryObject registerSlab(S } private static RegistryObject registerMembrane(String name, MembraneBlock.IgnoreEntityCollisionPredicate predicate) { + return registerMembrane(name, predicate, MembraneBlock::new); + } + + private static RegistryObject registerMembrane(String name, MembraneBlock.IgnoreEntityCollisionPredicate predicate, MembraneBlockFactory factory) { return register(name, props -> { props = props.noOcclusion().isRedstoneConductor(ModBlocks::neverValid).isSuffocating(ModBlocks::neverValid).isViewBlocking(ModBlocks::neverValid); - return new MembraneBlock(props, predicate); + return factory.create(props, predicate); }); } @@ -149,12 +167,16 @@ public static BlockBehaviour.Properties createFleshProperties() { return BlockBehaviour.Properties.of(ModBlockMaterials.FLESH_MATERIAL).strength(3f, 3f).sound(ModSoundTypes.FLESH_BLOCK).isValidSpawn(ModBlocks::isValidFleshkinSpawn); } + public static BlockBehaviour.Properties createFleshVeinsProperties() { + return BlockBehaviour.Properties.of(ModBlockMaterials.FLESH_VEINS_MATERIAL).strength(3f, 3f).sound(ModSoundTypes.FLESH_BLOCK).isValidSpawn(ModBlocks::isValidFleshkinSpawn); + } + public static BlockBehaviour.Properties createToughFleshProperties() { - return createFleshProperties().strength(6f, 6f); + return createFleshProperties().strength(6f, 12f); } - public static BlockBehaviour.Properties createFleshyBoneProperties() { - return BlockBehaviour.Properties.of(ModBlockMaterials.FLESH_MATERIAL).strength(3f, 3f).sound(SoundType.BONE_BLOCK).isValidSpawn(ModBlocks::isValidFleshkinSpawn); + public static BlockBehaviour.Properties createBonyFleshProperties() { + return BlockBehaviour.Properties.of(ModBlockMaterials.FLESH_MATERIAL).strength(4f, 6f).sound(ModSoundTypes.BONY_FLESH_BLOCK).isValidSpawn(ModBlocks::isValidFleshkinSpawn); } public static boolean isValidFleshkinSpawn(BlockState state, BlockGetter level, BlockPos pos, EntityType entityType) { @@ -174,4 +196,7 @@ interface StairBlockFactory { T create(Supplier state, BlockBehaviour.Properties properties); } + interface MembraneBlockFactory { + T create(BlockBehaviour.Properties properties, MembraneBlock.IgnoreEntityCollisionPredicate predicate); + } } diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModEntityTypes.java b/src/main/java/com/github/elenterius/biomancy/init/ModEntityTypes.java index 4f6b5b2aa..6a136333e 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModEntityTypes.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModEntityTypes.java @@ -5,9 +5,7 @@ import com.github.elenterius.biomancy.entity.fleshblob.AdulteratedHangryEaterFleshBlob; import com.github.elenterius.biomancy.entity.fleshblob.PrimordialEaterFleshBlob; import com.github.elenterius.biomancy.entity.fleshblob.PrimordialHangryEaterFleshBlob; -import com.github.elenterius.biomancy.entity.projectile.CorrosiveAcidProjectile; -import com.github.elenterius.biomancy.entity.projectile.ToothProjectile; -import com.github.elenterius.biomancy.entity.projectile.WitherProjectile; +import com.github.elenterius.biomancy.entity.projectile.*; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; @@ -18,6 +16,8 @@ import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; +import java.util.function.UnaryOperator; + @Mod.EventBusSubscriber(modid = BiomancyMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) public final class ModEntityTypes { @@ -48,9 +48,11 @@ public final class ModEntityTypes { // public static final RegistryObject> THICK_WOOL_SHEEP = register("thick_wool_sheep", EntityType.Builder.of(ThickWoolSheepEntity::new, EntityClassification.CREATURE).sized(0.9f, 1.3f).clientTrackingRange(10)); //Projectiles - public static final RegistryObject> TOOTH_PROJECTILE = register("tooth_projectile", EntityType.Builder.of(ToothProjectile::new, MobCategory.MISC).sized(0.25f, 0.25f).updateInterval(10)); - public static final RegistryObject> WITHER_SKULL_PROJECTILE = register("wither_projectile", EntityType.Builder.of(WitherProjectile::new, MobCategory.MISC).sized(0.3125f, 0.3125f).updateInterval(10)); - public static final RegistryObject> CORROSIVE_ACID_PROJECTILE = register("corrosive_acid_projectile", EntityType.Builder.of(CorrosiveAcidProjectile::new, MobCategory.MISC).sized(0.25f, 0.25f).updateInterval(10)); + public static final RegistryObject> TOOTH_PROJECTILE = registerProjectile("tooth_projectile", ToothProjectile::new, builder -> builder.sized(0.25f, 0.25f)); + public static final RegistryObject> WITHER_SKULL_PROJECTILE = registerProjectile("wither_projectile", WitherProjectile::new, builder -> builder.sized(0.3125f, 0.3125f)); + public static final RegistryObject> CORROSIVE_ACID_PROJECTILE = registerProjectile("corrosive_acid_projectile", CorrosiveAcidProjectile::new, builder -> builder.sized(0.25f, 0.25f)); + public static final RegistryObject> SAPBERRY_PROJECTILE = registerProjectile("sapberry_projectile", SapberryProjectile::new, builder -> builder.sized(8f / 16f, 8f / 16f)); + public static final RegistryObject> ACID_BLOB_PROJECTILE = registerProjectile("acid_blob_projectile", AcidBlobProjectile::new, builder -> builder.sized(6f / 16f, 6f / 16f)); private ModEntityTypes() {} @@ -58,6 +60,10 @@ private static RegistryObject> register(String return ENTITIES.register(name, () -> builder.build(BiomancyMod.MOD_ID + ":" + name)); } + private static RegistryObject> registerProjectile(String name, EntityType.EntityFactory factory, UnaryOperator> builder) { + return ENTITIES.register(name, () -> builder.apply(EntityType.Builder.of(factory, MobCategory.MISC)).updateInterval(10).build(BiomancyMod.MOD_ID + ":" + name)); + } + @SubscribeEvent public static void onAttributeCreation(final EntityAttributeCreationEvent event) { event.put(FLESH_BLOB.get(), AdulteratedEaterFleshBlob.createAttributes().build()); diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModFluids.java b/src/main/java/com/github/elenterius/biomancy/init/ModFluids.java new file mode 100644 index 000000000..56744fd9e --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/init/ModFluids.java @@ -0,0 +1,92 @@ +package com.github.elenterius.biomancy.init; + +import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.fluid.AcidFluid; +import com.github.elenterius.biomancy.fluid.TintedFluidType; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions; +import net.minecraftforge.common.ForgeMod; +import net.minecraftforge.fluids.FluidInteractionRegistry; +import net.minecraftforge.fluids.FluidType; +import net.minecraftforge.fluids.ForgeFlowingFluid; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; + +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +public final class ModFluids { + + public static final DeferredRegister FLUID_TYPES = DeferredRegister.create(ForgeRegistries.Keys.FLUID_TYPES, BiomancyMod.MOD_ID); + public static final DeferredRegister FLUIDS = DeferredRegister.create(ForgeRegistries.FLUIDS, BiomancyMod.MOD_ID); + + public static final RegistryObject ACID_TYPE = registerTintedType("acid", 0xFF_39FF14, properties -> properties.density(1024).viscosity(1024)); + public static final Supplier ACID_FLUID_PROPERTIES = () -> new ForgeFlowingFluid + .Properties(ACID_TYPE, ModFluids.ACID, ModFluids.FLOWING_ACID) + .slopeFindDistance(2) + .levelDecreasePerBlock(2) + .block(ModBlocks.ACID_FLUID_BLOCK) + .bucket(ModItems.ACID_BUCKET); + public static final RegistryObject ACID = register("acid", () -> new AcidFluid.Source(ACID_FLUID_PROPERTIES.get())); + public static final RegistryObject FLOWING_ACID = register("flowing_acid", () -> new AcidFluid.Flowing(ACID_FLUID_PROPERTIES.get())); + + private ModFluids() {} + + static void registerInteractions() { + FluidInteractionRegistry.addInteraction(ACID_TYPE.get(), new FluidInteractionRegistry.InteractionInformation( + ForgeMod.WATER_TYPE.get(), + fluidState -> fluidState.isSource() ? Blocks.CALCITE.defaultBlockState() : Blocks.DIORITE.defaultBlockState() + )); + FluidInteractionRegistry.addInteraction(ACID_TYPE.get(), new FluidInteractionRegistry.InteractionInformation( + ForgeMod.LAVA_TYPE.get(), + fluidState -> fluidState.isSource() ? Blocks.OBSIDIAN.defaultBlockState() : Blocks.DIORITE.defaultBlockState() + )); + } + + private static RegistryObject register(String name, Supplier factory) { + return FLUIDS.register(name, factory); + } + + private static RegistryObject registerTintedType(String name, int colorARGB, UnaryOperator operator) { + return FLUID_TYPES.register(name, () -> new TintedFluidType(operator.apply(FluidType.Properties.create()), colorARGB)); + } + + private static RegistryObject registerType(String name, UnaryOperator operator) { + return FLUID_TYPES.register(name, () -> new FluidType(operator.apply(FluidType.Properties.create())) { + + private final ResourceLocation stillTexture = BiomancyMod.createRL("fluid/%s_still".formatted(name)); + private final ResourceLocation flowingTexture = BiomancyMod.createRL("fluid/%s_flowing".formatted(name)); + private final ResourceLocation overlayTexture = BiomancyMod.createRL("fluid/%s_overlay".formatted(name)); + + @Override + public void initializeClient(Consumer consumer) { + consumer.accept(new IClientFluidTypeExtensions() { + @Override + public ResourceLocation getStillTexture() { + return stillTexture; + } + + @Override + public ResourceLocation getFlowingTexture() { + return flowingTexture; + } + + @Override + public ResourceLocation getOverlayTexture() { + return overlayTexture; + } + + @Override + public ResourceLocation getRenderOverlayTexture(Minecraft mc) { + return overlayTexture; + } + }); + } + }); + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModItems.java b/src/main/java/com/github/elenterius/biomancy/init/ModItems.java index e51443084..ecc7285ff 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModItems.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModItems.java @@ -83,6 +83,9 @@ public final class ModItems { // public static final RegistryObject ADRENALINE_SERUM = registerSerumItem(ModSerums.ADRENALINE_SERUM); // public static final RegistryObject DECAY_AGENT = registerSerumItem(ModSerums.DECAY_AGENT); + //# Buckets + public static final RegistryObject ACID_BUCKET = registerItem("acid_bucket", properties -> new BucketItem(ModFluids.ACID, properties.craftRemainder(Items.BUCKET).stacksTo(1).rarity(Rarity.COMMON))); + //# Misc public static final RegistryObject GIFT_SAC = registerItem("gift_sac", props -> new GiftSacItem(props.stacksTo(1).rarity(ModRarities.ULTRA_RARE).tab(null))); public static final RegistryObject CREATOR_MIX = registerSimpleItem("creator_mix"); @@ -105,6 +108,7 @@ public final class ModItems { //# Food/Fuel public static final RegistryObject NUTRIENT_PASTE = registerItem("nutrient_paste", props -> new EffectCureItem(props.food(ModFoods.NUTRIENT_PASTE))); public static final RegistryObject NUTRIENT_BAR = registerItem("nutrient_bar", props -> new EffectCureItem(props.food(ModFoods.NUTRIENT_BAR))); + public static final RegistryObject BLOOMBERRY = registerItem("bloomberry", props -> new SapberryItem(props.food(ModFoods.NUTRIENT_PASTE))); //# Block Items @@ -150,12 +154,20 @@ public final class ModItems { public static final RegistryObject MALIGNANT_FLESH_STAIRS = registerBlockItem(ModBlocks.MALIGNANT_FLESH_STAIRS, ObfuscatedTooltipBlockItem::new); public static final RegistryObject MALIGNANT_FLESH_WALL = registerBlockItem(ModBlocks.MALIGNANT_FLESH_WALL, ObfuscatedTooltipBlockItem::new); public static final RegistryObject MALIGNANT_FLESH_VEINS = registerBlockItem(ModBlocks.MALIGNANT_FLESH_VEINS, ObfuscatedTooltipBlockItem::new); + public static final RegistryObject MALIGNANT_BLOOM = registerBlockItem(ModBlocks.MALIGNANT_BLOOM, ObfuscatedTooltipBlockItem::new); + public static final RegistryObject PRIMAL_ORIFICE = registerBlockItem(ModBlocks.PRIMAL_ORIFICE, ObfuscatedTooltipBlockItem::new); - //## Misc + //## Utility public static final RegistryObject VOICE_BOX = registerSimpleBlockItem(ModBlocks.VOICE_BOX, () -> createProperties().tab(null)); public static final RegistryObject IMPERMEABLE_MEMBRANE = registerSimpleBlockItem(ModBlocks.IMPERMEABLE_MEMBRANE); public static final RegistryObject BABY_PERMEABLE_MEMBRANE = registerSimpleBlockItem(ModBlocks.BABY_PERMEABLE_MEMBRANE); public static final RegistryObject ADULT_PERMEABLE_MEMBRANE = registerSimpleBlockItem(ModBlocks.ADULT_PERMEABLE_MEMBRANE); + public static final RegistryObject PRIMAL_PERMEABLE_MEMBRANE = registerSimpleBlockItem(ModBlocks.PRIMAL_PERMEABLE_MEMBRANE); + public static final RegistryObject UNDEAD_PERMEABLE_MEMBRANE = registerSimpleBlockItem(ModBlocks.UNDEAD_PERMEABLE_MEMBRANE); + + //public static final RegistryObject NEURAL_INTERCEPTOR = registerSimpleBlockItem(ModBlocks.NEURAL_INTERCEPTOR, ModRarities.VERY_RARE); + + //## Misc public static final RegistryObject FLESH_LADDER = registerSimpleBlockItem(ModBlocks.FLESH_LADDER); public static final RegistryObject FLESH_FENCE = registerSimpleBlockItem(ModBlocks.FLESH_FENCE); public static final RegistryObject FLESH_FENCE_GATE = registerSimpleBlockItem(ModBlocks.FLESH_FENCE_GATE); @@ -166,6 +178,7 @@ public final class ModItems { public static final RegistryObject YELLOW_BIO_LANTERN = registerSimpleBlockItem(ModBlocks.YELLOW_BIO_LANTERN); public static final RegistryObject BLUE_BIO_LANTERN = registerSimpleBlockItem(ModBlocks.BLUE_BIO_LANTERN); public static final RegistryObject PRIMORDIAL_BIO_LANTERN = registerBlockItem(ModBlocks.PRIMORDIAL_BIO_LANTERN, ObfuscatedTooltipBlockItem::new); + public static final RegistryObject BLOOMLIGHT = registerBlockItem(ModBlocks.BLOOMLIGHT, ObfuscatedTooltipBlockItem::new); public static final RegistryObject TENDON_CHAIN = registerBlockItem(ModBlocks.TENDON_CHAIN, FleshChainBlockItem::new); public static final RegistryObject VIAL_HOLDER = registerSimpleBlockItem(ModBlocks.VIAL_HOLDER); diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModMobEffects.java b/src/main/java/com/github/elenterius/biomancy/init/ModMobEffects.java index 25b8ce5a5..ae03ded7c 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModMobEffects.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModMobEffects.java @@ -14,7 +14,7 @@ public final class ModMobEffects { public static final DeferredRegister EFFECTS = DeferredRegister.create(ForgeRegistries.MOB_EFFECTS, BiomancyMod.MOD_ID); - public static final RegistryObject CORROSIVE = EFFECTS.register("corrosive", () -> new CorrosiveEffect(MobEffectCategory.HARMFUL, 0xbee040)); + public static final RegistryObject CORROSIVE = EFFECTS.register("corrosive", () -> new CorrosiveEffect(MobEffectCategory.HARMFUL, 0x39FF14)); public static final RegistryObject ARMOR_SHRED = EFFECTS.register("armor_shred", () -> new ArmorShredEffect(MobEffectCategory.HARMFUL, 20, 0x909090) .addModifier(Attributes.ARMOR, "a15ed03e-c5db-4cf8-a0f5-4eb4657bb731", -1f, AttributeModifier.Operation.ADDITION)); public static final RegistryObject BLEED = EFFECTS.register("bleed", () -> new BleedEffect(MobEffectCategory.HARMFUL, 0x8a0303, 2)); diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModParticleTypes.java b/src/main/java/com/github/elenterius/biomancy/init/ModParticleTypes.java index a3529f521..bc2de4cf9 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModParticleTypes.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModParticleTypes.java @@ -14,6 +14,9 @@ public final class ModParticleTypes { public static final RegistryObject FALLING_BLOOD = register("falling_blood", false); public static final RegistryObject LANDING_BLOOD = register("landing_blood", false); public static final RegistryObject CORROSIVE_SWIPE_ATTACK = register("corrosive_swipe", true); + public static final RegistryObject DRIPPING_ACID = register("dripping_acid", false); + public static final RegistryObject FALLING_ACID = register("falling_acid", false); + public static final RegistryObject LANDING_ACID = register("landing_acid", false); private ModParticleTypes() {} diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModPredicates.java b/src/main/java/com/github/elenterius/biomancy/init/ModPredicates.java new file mode 100644 index 000000000..801580a55 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/init/ModPredicates.java @@ -0,0 +1,14 @@ +package com.github.elenterius.biomancy.init; + +import com.github.elenterius.biomancy.advancements.predicate.FoodItemPredicate; +import net.minecraft.advancements.critereon.ItemPredicate; + +public final class ModPredicates { + + private ModPredicates() {} + + public static void registerItemPredicates() { + ItemPredicate.register(FoodItemPredicate.ID, FoodItemPredicate::deserializeFromJson); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModProjectiles.java b/src/main/java/com/github/elenterius/biomancy/init/ModProjectiles.java index 2a36cda42..d8b91ec1f 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModProjectiles.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModProjectiles.java @@ -1,12 +1,10 @@ package com.github.elenterius.biomancy.init; -import com.github.elenterius.biomancy.entity.projectile.BaseProjectile; -import com.github.elenterius.biomancy.entity.projectile.CorrosiveAcidProjectile; -import com.github.elenterius.biomancy.entity.projectile.ToothProjectile; -import com.github.elenterius.biomancy.entity.projectile.WitherProjectile; +import com.github.elenterius.biomancy.entity.projectile.*; import com.github.elenterius.biomancy.item.weapon.Gun; -import it.unimi.dsi.fastutil.floats.FloatUnaryOperator; -import it.unimi.dsi.fastutil.ints.IntUnaryOperator; +import com.github.elenterius.biomancy.util.function.FloatOperator; +import com.github.elenterius.biomancy.util.function.IntOperator; +import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.entity.LivingEntity; @@ -22,19 +20,30 @@ public final class ModProjectiles { public static final ConfiguredProjectile TOOTH = build("Sharp Tooth", 1.75f, 5f, 0, convertToInaccuracy(0.92f), ToothProjectile::new); public static final ConfiguredProjectile WITHER = build("Withershot", 0.8f, 8f, 0, convertToInaccuracy(0.9f), WitherProjectile::new); public static final ConfiguredProjectile CORROSIVE = build("Corrosive", 1.5f, 4, 0, convertToInaccuracy(0.9f), CorrosiveAcidProjectile::new); + public static final ConfiguredProjectile ACID_BLOB = build("Acid Blob", 1.2f, 2, 0, convertToInaccuracy(0.9f), SoundEvents.SLIME_JUMP_SMALL, AcidBlobProjectile::new); + public static final ConfiguredProjectile FALLING_ACID_BLOB = build("Falling Acid Blob", 0.1f, 2, 0, convertToInaccuracy(0.9f), SoundEvents.SLIME_SQUISH_SMALL, AcidBlobProjectile::new); + public static final ConfiguredProjectile SAPBERRY = build("Sapberry", 1.25f, 2, 0, convertToInaccuracy(0.9f), SapberryProjectile::new); private static float convertToInaccuracy(float accuracy) { return -Gun.MAX_INACCURACY * accuracy + Gun.MAX_INACCURACY; } private static ConfiguredProjectile build(String name, float velocity, float damage, int knockback, float accuracy, ProjectileFactory factory) { - ConfiguredProjectile configuredProjectile = new ConfiguredProjectile<>(name, velocity, damage, knockback, convertToInaccuracy(accuracy), factory); + ConfiguredProjectile configuredProjectile = new ConfiguredProjectile<>(name, velocity, damage, knockback, convertToInaccuracy(accuracy), SoundEvents.CROSSBOW_SHOOT, factory); PRECONFIGURED_PROJECTILES.add(configuredProjectile); return configuredProjectile; } - public static boolean shootProjectile(Level level, LivingEntity shooter, float velocity, float damage, int knockback, float inaccuracy, ProjectileFactory factory) { - BaseProjectile projectile = factory.create(level, shooter); + private static ConfiguredProjectile build(String name, float velocity, float damage, int knockback, float accuracy, SoundEvent shootSound, ProjectileFactory factory) { + ConfiguredProjectile configuredProjectile = new ConfiguredProjectile<>(name, velocity, damage, knockback, convertToInaccuracy(accuracy), shootSound, factory); + PRECONFIGURED_PROJECTILES.add(configuredProjectile); + return configuredProjectile; + } + + public static boolean shootProjectile(Level level, LivingEntity shooter, float velocity, float damage, int knockback, float inaccuracy, SoundEvent shootSound, ProjectileFactory factory) { + BaseProjectile projectile = factory.create(level, shooter.getX(), shooter.getEyeY() - 0.1f, shooter.getZ()); + projectile.setOwner(shooter); + projectile.setDamage(damage); if (knockback > 0) { projectile.setKnockback((byte) knockback); @@ -44,7 +53,26 @@ public static boolean shootProjectile(Level level, Li projectile.shoot(direction.x(), direction.y(), direction.z(), velocity, inaccuracy); if (level.addFreshEntity(projectile)) { - level.playSound(null, shooter.getX(), shooter.getY(), shooter.getZ(), SoundEvents.CROSSBOW_SHOOT, SoundSource.PLAYERS, 0.8f, 0.4f); + level.playSound(null, shooter.getX(), shooter.getY(), shooter.getZ(), shootSound, SoundSource.PLAYERS, 0.8f, 0.4f); + return true; + } + + return false; + } + + public static boolean shootProjectile(Level level, Vec3 origin, Vec3 target, float velocity, float damage, int knockback, float inaccuracy, SoundEvent shootSound, ProjectileFactory factory) { + BaseProjectile projectile = factory.create(level, origin.x, origin.y, origin.z); + + projectile.setDamage(damage); + if (knockback > 0) { + projectile.setKnockback((byte) knockback); + } + + Vec3 direction = target.subtract(origin).normalize(); + projectile.shoot(direction.x(), direction.y(), direction.z(), velocity, inaccuracy); + + if (level.addFreshEntity(projectile)) { + level.playSound(null, origin.x, origin.y, origin.z, shootSound, SoundSource.PLAYERS, 0.8f, 0.4f); return true; } @@ -52,17 +80,25 @@ public static boolean shootProjectile(Level level, Li } public interface ProjectileFactory { - T create(Level level, LivingEntity shooter); + T create(Level level, double x, double v, double z); } - public record ConfiguredProjectile(String name, float velocity, float damage, int knockback, float inaccuracy, ProjectileFactory factory) { + public record ConfiguredProjectile(String name, float velocity, float damage, int knockback, float inaccuracy, SoundEvent shootSound, ProjectileFactory factory) { + + public boolean shoot(Level level, Vec3 origin, Vec3 target) { + return shootProjectile(level, origin, target, velocity, damage, knockback, inaccuracy, shootSound, factory); + } + + public boolean shoot(Level level, Vec3 origin, Vec3 target, FloatOperator velocityModifier, FloatOperator damageModifier, IntOperator knockbackModifier, FloatOperator inaccuracyModifier) { + return shootProjectile(level, origin, target, velocityModifier.apply(velocity), damageModifier.apply(damage), knockbackModifier.apply(knockback), inaccuracyModifier.apply(inaccuracy), shootSound, factory); + } public boolean shoot(Level level, LivingEntity shooter) { - return shootProjectile(level, shooter, velocity, damage, knockback, inaccuracy, factory); + return shootProjectile(level, shooter, velocity, damage, knockback, inaccuracy, shootSound, factory); } - public boolean shoot(Level level, LivingEntity shooter, FloatUnaryOperator velocityFunc, FloatUnaryOperator damageFunc, IntUnaryOperator knockbackFunc, FloatUnaryOperator inaccuracyFunc) { - return shootProjectile(level, shooter, velocityFunc.apply(velocity), damageFunc.apply(damage), knockbackFunc.apply(knockback), inaccuracyFunc.apply(inaccuracy), factory); + public boolean shoot(Level level, LivingEntity shooter, FloatOperator velocityModifier, FloatOperator damageModifier, IntOperator knockbackModifier, FloatOperator inaccuracyModifier) { + return shootProjectile(level, shooter, velocityModifier.apply(velocity), damageModifier.apply(damage), knockbackModifier.apply(knockback), inaccuracyModifier.apply(inaccuracy), shootSound, factory); } } diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModRecipes.java b/src/main/java/com/github/elenterius/biomancy/init/ModRecipes.java index 7f2a47c15..fb6a95e2d 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModRecipes.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModRecipes.java @@ -1,8 +1,8 @@ package com.github.elenterius.biomancy.init; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.recipe.*; -import com.github.elenterius.biomancy.recipe.SimpleRecipeType.ItemStackRecipeType; +import com.github.elenterius.biomancy.crafting.recipe.*; +import com.github.elenterius.biomancy.crafting.recipe.SimpleRecipeType.ItemStackRecipeType; import net.minecraft.core.Registry; import net.minecraft.world.Container; import net.minecraft.world.item.Item; diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModSoundEvents.java b/src/main/java/com/github/elenterius/biomancy/init/ModSoundEvents.java index e7b68ca2a..58957f3af 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModSoundEvents.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModSoundEvents.java @@ -25,6 +25,13 @@ public final class ModSoundEvents { public static final RegistryObject FLESH_BLOCK_BREAK = registerSoundEvent("flesh_block.break"); public static final RegistryObject FLESH_BLOCK_STEP = registerSoundEvent("flesh_block.step"); public static final RegistryObject FLESH_BLOCK_FALL = registerSoundEvent("flesh_block.fall"); + + public static final RegistryObject BONY_FLESH_BLOCK_PLACE = registerSoundEvent("bony_flesh_block.place"); + public static final RegistryObject BONY_FLESH_BLOCK_HIT = registerSoundEvent("bony_flesh_block.hit"); + public static final RegistryObject BONY_FLESH_BLOCK_BREAK = registerSoundEvent("bony_flesh_block.break"); + public static final RegistryObject BONY_FLESH_BLOCK_STEP = registerSoundEvent("bony_flesh_block.step"); + public static final RegistryObject BONY_FLESH_BLOCK_FALL = registerSoundEvent("bony_flesh_block.fall"); + public static final RegistryObject FLESH_DOOR_OPEN = registerSoundEvent("flesh_door.open"); public static final RegistryObject FLESH_DOOR_CLOSE = registerSoundEvent("flesh_door.close"); diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModSoundTypes.java b/src/main/java/com/github/elenterius/biomancy/init/ModSoundTypes.java index e1a9c5787..67b6b1cfb 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModSoundTypes.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModSoundTypes.java @@ -4,10 +4,13 @@ public final class ModSoundTypes { - private ModSoundTypes() {} - public static final ForgeSoundType FLESH_BLOCK = new ForgeSoundType(1f, 1f, ModSoundEvents.FLESH_BLOCK_BREAK, ModSoundEvents.FLESH_BLOCK_STEP, ModSoundEvents.FLESH_BLOCK_PLACE, ModSoundEvents.FLESH_BLOCK_HIT, ModSoundEvents.FLESH_BLOCK_FALL); + public static final ForgeSoundType BONY_FLESH_BLOCK = new ForgeSoundType(1f, 1f, + ModSoundEvents.BONY_FLESH_BLOCK_BREAK, ModSoundEvents.BONY_FLESH_BLOCK_STEP, + ModSoundEvents.BONY_FLESH_BLOCK_PLACE, ModSoundEvents.BONY_FLESH_BLOCK_HIT, ModSoundEvents.BONY_FLESH_BLOCK_FALL); + + private ModSoundTypes() {} } diff --git a/src/main/java/com/github/elenterius/biomancy/init/ModVillagerTrades.java b/src/main/java/com/github/elenterius/biomancy/init/ModVillagerTrades.java index 62c6ec444..8d905359d 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/ModVillagerTrades.java +++ b/src/main/java/com/github/elenterius/biomancy/init/ModVillagerTrades.java @@ -1,5 +1,6 @@ package com.github.elenterius.biomancy.init; +import com.github.elenterius.biomancy.BiomancyConfig; import com.github.elenterius.biomancy.BiomancyMod; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.world.entity.npc.VillagerProfession; @@ -21,6 +22,8 @@ public class ModVillagerTrades { @SubscribeEvent public static void onVillagerTrades(final VillagerTradesEvent event) { + if (Boolean.FALSE.equals(BiomancyConfig.SERVER.addTradesToVillagers.get())) return; + if (event.getType() == VillagerProfession.BUTCHER) { addButcherTrades(event.getTrades()); } @@ -31,14 +34,16 @@ else if (event.getType() == VillagerProfession.CLERIC) { @SubscribeEvent public static void onWandererTrades(final WandererTradesEvent event) { + if (Boolean.FALSE.equals(BiomancyConfig.SERVER.addTradesToWanderingTrader.get())) return; + List genericTrades = event.getGenericTrades(); genericTrades.add(sellToPlayer(ModItems.EXOTIC_DUST.get(), 4, 2, 16, 5)); genericTrades.add(sellToPlayer(ModItems.FLESH_SPIKE.get(), 2, 16, 5)); genericTrades.add(buyFromPlayer(ModItems.NUTRIENT_BAR.get(), 2, 8, 5)); List rareTrades = event.getRareTrades(); - rareTrades.add(sellToPlayer(ModItems.INSOMNIA_CURE.get(), 20, 8, 20)); - rareTrades.add(sellToPlayer(ModItems.CREATOR_MIX.get(), 20, 5, 20)); + rareTrades.add(sellToPlayer(ModItems.INSOMNIA_CURE.get(), 10, 8, 20)); + rareTrades.add(sellToPlayer(ModItems.CREATOR_MIX.get(), 10, 5, 20)); } private static BasicItemListing buyFromPlayer(Item item, int emeralds, int maxTrades, int xp) { diff --git a/src/main/java/com/github/elenterius/biomancy/init/client/ClientSetupHandler.java b/src/main/java/com/github/elenterius/biomancy/init/client/ClientSetupHandler.java index a96f04811..096cc1158 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/client/ClientSetupHandler.java +++ b/src/main/java/com/github/elenterius/biomancy/init/client/ClientSetupHandler.java @@ -7,6 +7,7 @@ import com.github.elenterius.biomancy.client.gui.tooltip.HrTooltipClientComponent; import com.github.elenterius.biomancy.client.gui.tooltip.StorageSacTooltipClientComponent; import com.github.elenterius.biomancy.client.particle.BloodDripParticle; +import com.github.elenterius.biomancy.client.particle.ParticleProviders; import com.github.elenterius.biomancy.client.render.block.bioforge.BioForgeRenderer; import com.github.elenterius.biomancy.client.render.block.biolab.BioLabRenderer; import com.github.elenterius.biomancy.client.render.block.cradle.PrimordialCradleRenderer; @@ -18,9 +19,11 @@ import com.github.elenterius.biomancy.client.render.block.tongue.TongueRenderer; import com.github.elenterius.biomancy.client.render.entity.AcidProjectileRenderer; import com.github.elenterius.biomancy.client.render.entity.WitherProjectileRenderer; +import com.github.elenterius.biomancy.client.render.entity.acidblob.AcidBlobProjectileRenderer; import com.github.elenterius.biomancy.client.render.entity.fleshblob.FleshBlobRenderer; import com.github.elenterius.biomancy.client.render.entity.fleshblob.LegacyFleshBlobRenderer; import com.github.elenterius.biomancy.client.render.entity.fleshblob.PrimordialFleshBlobRenderer; +import com.github.elenterius.biomancy.client.render.entity.sapberry.SapberryProjectileRenderer; import com.github.elenterius.biomancy.init.*; import com.github.elenterius.biomancy.integration.ModsCompatHandler; import com.github.elenterius.biomancy.tooltip.EmptyLineTooltipComponent; @@ -33,8 +36,10 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.ThrownItemRenderer; import net.minecraft.client.renderer.entity.WitherSkullRenderer; +import net.minecraft.world.item.BucketItem; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.*; +import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions; import net.minecraftforge.client.settings.KeyConflictContext; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @@ -95,6 +100,8 @@ public static void registerRenderers(final EntityRenderersEvent.RegisterRenderer event.registerEntityRenderer(ModEntityTypes.CORROSIVE_ACID_PROJECTILE.get(), AcidProjectileRenderer::new); event.registerEntityRenderer(ModEntityTypes.TOOTH_PROJECTILE.get(), ThrownItemRenderer::new); event.registerEntityRenderer(ModEntityTypes.WITHER_SKULL_PROJECTILE.get(), WitherProjectileRenderer::new); + event.registerEntityRenderer(ModEntityTypes.SAPBERRY_PROJECTILE.get(), SapberryProjectileRenderer::new); + event.registerEntityRenderer(ModEntityTypes.ACID_BLOB_PROJECTILE.get(), AcidBlobProjectileRenderer::new); } @SuppressWarnings("removal") @@ -102,7 +109,7 @@ private static void setBlockRenderLayers() { ItemBlockRenderTypes.setRenderLayer(ModBlocks.DIGESTER.get(), RenderType.cutout()); ItemBlockRenderTypes.setRenderLayer(ModBlocks.VOICE_BOX.get(), RenderType.translucent()); -// ItemBlockRenderTypes.setRenderLayer(ModBlocks.STORAGE_SAC.get(), RenderType.translucent()); + //ItemBlockRenderTypes.setRenderLayer(ModBlocks.STORAGE_SAC.get(), RenderType.translucent()); ItemBlockRenderTypes.setRenderLayer(ModBlocks.FLESH_IRIS_DOOR.get(), RenderType.cutout()); ItemBlockRenderTypes.setRenderLayer(ModBlocks.FLESH_FENCE.get(), RenderType.cutout()); @@ -115,6 +122,9 @@ private static void setBlockRenderLayers() { ItemBlockRenderTypes.setRenderLayer(ModBlocks.TENDON_CHAIN.get(), RenderType.cutout()); ItemBlockRenderTypes.setRenderLayer(ModBlocks.VIAL_HOLDER.get(), RenderType.cutout()); + ItemBlockRenderTypes.setRenderLayer(ModFluids.ACID.get(), RenderType.translucent()); + ItemBlockRenderTypes.setRenderLayer(ModFluids.FLOWING_ACID.get(), RenderType.translucent()); + //block with "glowing" overlay texture, also needs a overlay model see onModelBakeEvent() in ClientSetupHandler //ItemBlockRenderTypes.setRenderLayer(ModBlocks.FOOBAR.get(), renderType -> renderType == RenderType.getCutout() || renderType == RenderType.getTranslucent()); } @@ -125,6 +135,9 @@ public static void registerParticles(final RegisterParticleProvidersEvent event) event.register(ModParticleTypes.FALLING_BLOOD.get(), BloodDripParticle.FallingBloodFactory::new); event.register(ModParticleTypes.LANDING_BLOOD.get(), BloodDripParticle.LandingBloodFactory::new); event.register(ModParticleTypes.CORROSIVE_SWIPE_ATTACK.get(), AttackSweepParticle.Provider::new); + event.register(ModParticleTypes.DRIPPING_ACID.get(), ParticleProviders.AcidHangProvider::new); + event.register(ModParticleTypes.FALLING_ACID.get(), ParticleProviders.AcidFallProvider::new); + event.register(ModParticleTypes.LANDING_ACID.get(), ParticleProviders.AcidLandProvider::new); } @SubscribeEvent @@ -151,6 +164,8 @@ public static void onItemColorRegistry(final RegisterColorHandlersEvent.Item eve event.register((stack, tintIndex) -> ModItems.ESSENCE.get().getColor(stack, tintIndex), ModItems.ESSENCE.get()); event.register((stack, tintIndex) -> tintIndex == 1 ? 0xFF_09DF5B : 0xFF_FFFFFF, ModBlocks.BABY_PERMEABLE_MEMBRANE.get()); event.register((stack, tintIndex) -> tintIndex == 1 ? 0xFF_ACBF60 : 0xFF_FFFFFF, ModBlocks.ADULT_PERMEABLE_MEMBRANE.get()); + event.register((stack, tintIndex) -> tintIndex == 1 ? 0xFF_F740FD : 0xFF_FFFFFF, ModBlocks.PRIMAL_PERMEABLE_MEMBRANE.get()); + event.register((stack, index) -> index == 1 ? IClientFluidTypeExtensions.of(((BucketItem) stack.getItem()).getFluid()).getTintColor() : 0xFF_FFFFFF, ModItems.ACID_BUCKET.get()); } @SubscribeEvent @@ -158,11 +173,12 @@ public static void onBlockColorRegistry(final RegisterColorHandlersEvent.Block e event.register(VialHolderBlock::getTintColor, ModBlocks.VIAL_HOLDER.get()); event.register((state, level, pos, tintIndex) -> tintIndex == 1 ? 0xFF_09DF5B : 0xFF_FFFFFF, ModBlocks.BABY_PERMEABLE_MEMBRANE.get()); event.register((state, level, pos, tintIndex) -> tintIndex == 1 ? 0xFF_ACBF60 : 0xFF_FFFFFF, ModBlocks.ADULT_PERMEABLE_MEMBRANE.get()); + event.register((state, level, pos, tintIndex) -> tintIndex == 1 ? 0xFF_F740FD : 0xFF_FFFFFF, ModBlocks.PRIMAL_PERMEABLE_MEMBRANE.get()); } @SubscribeEvent public static void registerGameOverlays(RegisterGuiOverlaysEvent event) { -// event.registerAboveAll("biomancy_gun", IngameOverlays.GUN_OVERLAY); + // event.registerAboveAll("biomancy_gun", IngameOverlays.GUN_OVERLAY); event.registerAboveAll("biomancy_injector", IngameOverlays.INJECTOR_OVERLAY); event.registerAboveAll("biomancy_charge_bar", IngameOverlays.CHARGE_BAR_OVERLAY); event.registerAboveAll("biomancy_attack_reach", IngameOverlays.ATTACK_REACH_OVERLAY); diff --git a/src/main/java/com/github/elenterius/biomancy/init/client/ModRecipeBookCategories.java b/src/main/java/com/github/elenterius/biomancy/init/client/ModRecipeBookCategories.java index ec6937659..436f2add2 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/client/ModRecipeBookCategories.java +++ b/src/main/java/com/github/elenterius/biomancy/init/client/ModRecipeBookCategories.java @@ -1,11 +1,11 @@ package com.github.elenterius.biomancy.init.client; import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.crafting.recipe.BioForgeRecipe; import com.github.elenterius.biomancy.init.ModBioForgeTabs; import com.github.elenterius.biomancy.init.ModRecipeBookTypes; import com.github.elenterius.biomancy.init.ModRecipes; import com.github.elenterius.biomancy.menu.BioForgeTab; -import com.github.elenterius.biomancy.recipe.BioForgeRecipe; import net.minecraft.client.RecipeBookCategories; import net.minecraft.world.item.crafting.Recipe; import net.minecraftforge.api.distmarker.Dist; diff --git a/src/main/java/com/github/elenterius/biomancy/init/tags/ModBlockTags.java b/src/main/java/com/github/elenterius/biomancy/init/tags/ModBlockTags.java index 05fc0fdb4..61f493945 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/tags/ModBlockTags.java +++ b/src/main/java/com/github/elenterius/biomancy/init/tags/ModBlockTags.java @@ -8,9 +8,10 @@ public final class ModBlockTags { public static final TagKey FLESHY_FENCES = tag("fleshy_fences"); - //CONVERTABLE_TO_PRIMAL_FLESH - //CONVERTABLE_TO_MALIGNANT_FLESH - public static final TagKey PRIMORDIAL_ECO_SYSTEM_REPLACEABLE = tag("primordial_ecosystem_replaceable"); + public static final TagKey FLESH_REPLACEABLE = tag("flesh_replaceable"); + public static final TagKey ALLOW_VEINS_TO_ATTACH = tag("allow_veins_to_attach"); + public static final TagKey DISALLOW_VEINS_TO_ATTACH = tag("disallow_veins_to_attach"); + public static final TagKey ACID_DESTRUCTIBLE = tag("acid_destructible"); private ModBlockTags() {} diff --git a/src/main/java/com/github/elenterius/biomancy/init/tags/ModEntityTags.java b/src/main/java/com/github/elenterius/biomancy/init/tags/ModEntityTags.java index 635d20cf3..7d71091d0 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/tags/ModEntityTags.java +++ b/src/main/java/com/github/elenterius/biomancy/init/tags/ModEntityTags.java @@ -5,10 +5,12 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; import net.minecraft.world.entity.EntityType; +import net.minecraftforge.common.Tags; public final class ModEntityTags { - public static final TagKey> FORGE_BOSSES = forgeTag("bosses"); + public static final TagKey> FORGE_BOSSES = Tags.EntityTypes.BOSSES; + public static final TagKey> FORGE_GOLEMS = forgeTag("golems"); public static final TagKey> NOT_CLONEABLE = tag("not_cloneable"); public static final TagKey> FLESHKIN = tag("fleshkin"); diff --git a/src/main/java/com/github/elenterius/biomancy/init/tags/ModItemTags.java b/src/main/java/com/github/elenterius/biomancy/init/tags/ModItemTags.java index d2389b0c5..b7013fd42 100644 --- a/src/main/java/com/github/elenterius/biomancy/init/tags/ModItemTags.java +++ b/src/main/java/com/github/elenterius/biomancy/init/tags/ModItemTags.java @@ -9,24 +9,16 @@ public final class ModItemTags { public static final TagKey FORGE_TOOLS_KNIVES = forgeTag("tools/knives"); - // public static final TagKey FORGE_RAW_BACON = forgeTag("raw_bacon"); - // public static final TagKey FORGE_RAW_BEEF = forgeTag("raw_beef"); - // public static final TagKey FORGE_RAW_CHICKEN = forgeTag("raw_chicken"); - // public static final TagKey FORGE_RAW_PORK = forgeTag("raw_pork"); - // public static final TagKey FORGE_RAW_MUTTON = forgeTag("raw_mutton"); - // public static final TagKey FORGE_RAW_FISHES = forgeTag("raw_fishes"); public static final TagKey FANGS = tag("fangs"); public static final TagKey CLAWS = tag("claws"); public static final TagKey RAW_MEATS = tag("raw_meats"); public static final TagKey COOKED_MEATS = tag("cooked_meats"); - public static final TagKey SUGARS = tag("sugars"); - public static final TagKey BIOMASS = tag("biomass"); - public static final TagKey POOR_BIOMASS = tag("poor_biomass"); - public static final TagKey AVERAGE_BIOMASS = tag("average_biomass"); - public static final TagKey GOOD_BIOMASS = tag("good_biomass"); - public static final TagKey SUPERB_BIOMASS = tag("superb_biomass"); + public static final TagKey CANNOT_BE_EATEN_BY_CRADLE = tag("cannot_be_eaten_by_cradle"); + + @Deprecated + public static final TagKey SUGARS = tag("sugars"); private ModItemTags() {} diff --git a/src/main/java/com/github/elenterius/biomancy/integration/BioForgeCompat.java b/src/main/java/com/github/elenterius/biomancy/integration/BioForgeCompat.java new file mode 100644 index 000000000..5a05d70a4 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/integration/BioForgeCompat.java @@ -0,0 +1,17 @@ +package com.github.elenterius.biomancy.integration; + +import net.minecraftforge.fml.ModList; + +public final class BioForgeCompat { + + private BioForgeCompat() {} + + public static boolean isRecipeCollectionOverwriteEnabled() { + return isNerbLoaded(); + } + + private static boolean isNerbLoaded() { + return ModList.get().isLoaded("nerb"); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/integration/create/CreateCompat.java b/src/main/java/com/github/elenterius/biomancy/integration/create/CreateCompat.java index fc99e7b53..a4dacaa73 100644 --- a/src/main/java/com/github/elenterius/biomancy/integration/create/CreateCompat.java +++ b/src/main/java/com/github/elenterius/biomancy/integration/create/CreateCompat.java @@ -1,11 +1,55 @@ package com.github.elenterius.biomancy.integration.create; +import com.github.elenterius.biomancy.init.ModFluids; +import com.github.elenterius.biomancy.util.CombatUtil; +import com.simibubi.create.content.fluids.OpenEndedPipe; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.WeatheringCopper; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.fluids.FluidStack; + +import java.util.List; + public final class CreateCompat { private CreateCompat() {} public static void onPostSetup() { InteractionBehaviors.register(); + OpenEndedPipe.registerEffectHandler(new AcidEffectHandler()); } + private static class AcidEffectHandler implements OpenEndedPipe.IEffectHandler { + + @Override + public boolean canApplyEffects(OpenEndedPipe openEndedPipe, FluidStack fluidStack) { + return fluidStack.getFluid().getFluidType() == ModFluids.ACID_TYPE.get(); + } + + @Override + public void applyEffects(OpenEndedPipe openEndedPipe, FluidStack fluidStack) { + Level level = openEndedPipe.getWorld(); + if (level.getGameTime() % 5 == 0) { + List mobs = level.getEntitiesOfClass(LivingEntity.class, openEndedPipe.getAOE(), livingEntity -> !CombatUtil.hasAcidEffect(livingEntity)); + for (LivingEntity mob : mobs) { + CombatUtil.applyAcidEffect(mob, 4); + } + + BlockPos.betweenClosedStream(openEndedPipe.getAOE()).forEach(pos -> corrodeCopper(level, pos)); + } + } + + private void corrodeCopper(Level level, BlockPos pos) { + if (level.random.nextFloat() >= 0.057f) return; + + BlockState blockState = level.getBlockState(pos); + Block block = blockState.getBlock(); + if (block instanceof WeatheringCopper weatheringCopper && WeatheringCopper.getNext(block).isPresent()) { + weatheringCopper.getNext(blockState).ifPresent(state -> level.setBlockAndUpdate(pos, state)); + } + } + } } diff --git a/src/main/java/com/github/elenterius/biomancy/integration/jei/BioForgeRecipeCategory.java b/src/main/java/com/github/elenterius/biomancy/integration/jei/BioForgeRecipeCategory.java index 90772d423..c8ca5dfd0 100644 --- a/src/main/java/com/github/elenterius/biomancy/integration/jei/BioForgeRecipeCategory.java +++ b/src/main/java/com/github/elenterius/biomancy/integration/jei/BioForgeRecipeCategory.java @@ -1,11 +1,11 @@ package com.github.elenterius.biomancy.integration.jei; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.chat.ComponentUtil; +import com.github.elenterius.biomancy.crafting.recipe.BioForgeRecipe; +import com.github.elenterius.biomancy.crafting.recipe.IngredientStack; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; -import com.github.elenterius.biomancy.recipe.BioForgeRecipe; -import com.github.elenterius.biomancy.recipe.IngredientStack; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.mojang.blaze3d.vertex.PoseStack; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; diff --git a/src/main/java/com/github/elenterius/biomancy/integration/jei/BioLabRecipeCategory.java b/src/main/java/com/github/elenterius/biomancy/integration/jei/BioLabRecipeCategory.java index db54ddd2b..f9ec70690 100644 --- a/src/main/java/com/github/elenterius/biomancy/integration/jei/BioLabRecipeCategory.java +++ b/src/main/java/com/github/elenterius/biomancy/integration/jei/BioLabRecipeCategory.java @@ -1,11 +1,11 @@ package com.github.elenterius.biomancy.integration.jei; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.chat.ComponentUtil; +import com.github.elenterius.biomancy.crafting.recipe.BioLabRecipe; +import com.github.elenterius.biomancy.crafting.recipe.IngredientStack; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; -import com.github.elenterius.biomancy.recipe.BioLabRecipe; -import com.github.elenterius.biomancy.recipe.IngredientStack; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.mojang.blaze3d.vertex.PoseStack; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; diff --git a/src/main/java/com/github/elenterius/biomancy/integration/jei/DecomposerRecipeCategory.java b/src/main/java/com/github/elenterius/biomancy/integration/jei/DecomposerRecipeCategory.java index a6f3364ee..f78eaa8bc 100644 --- a/src/main/java/com/github/elenterius/biomancy/integration/jei/DecomposerRecipeCategory.java +++ b/src/main/java/com/github/elenterius/biomancy/integration/jei/DecomposerRecipeCategory.java @@ -1,12 +1,12 @@ package com.github.elenterius.biomancy.integration.jei; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.chat.ComponentUtil; +import com.github.elenterius.biomancy.crafting.recipe.DecomposerRecipe; +import com.github.elenterius.biomancy.crafting.recipe.ItemCountRange; +import com.github.elenterius.biomancy.crafting.recipe.VariableProductionOutput; import com.github.elenterius.biomancy.init.ModBlocks; import com.github.elenterius.biomancy.init.ModRecipes; -import com.github.elenterius.biomancy.recipe.DecomposerRecipe; -import com.github.elenterius.biomancy.recipe.ItemCountRange; -import com.github.elenterius.biomancy.recipe.VariableProductionOutput; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.mojang.blaze3d.vertex.PoseStack; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; diff --git a/src/main/java/com/github/elenterius/biomancy/integration/jei/DigesterRecipeCategory.java b/src/main/java/com/github/elenterius/biomancy/integration/jei/DigesterRecipeCategory.java index 00772c37e..009196687 100644 --- a/src/main/java/com/github/elenterius/biomancy/integration/jei/DigesterRecipeCategory.java +++ b/src/main/java/com/github/elenterius/biomancy/integration/jei/DigesterRecipeCategory.java @@ -1,10 +1,10 @@ package com.github.elenterius.biomancy.integration.jei; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.chat.ComponentUtil; +import com.github.elenterius.biomancy.crafting.recipe.DigesterRecipe; import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; -import com.github.elenterius.biomancy.recipe.DigesterRecipe; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.mojang.blaze3d.vertex.PoseStack; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; diff --git a/src/main/java/com/github/elenterius/biomancy/integration/tetra/TetraCompat.java b/src/main/java/com/github/elenterius/biomancy/integration/tetra/TetraCompat.java index a72bba673..c696e5a0d 100644 --- a/src/main/java/com/github/elenterius/biomancy/integration/tetra/TetraCompat.java +++ b/src/main/java/com/github/elenterius/biomancy/integration/tetra/TetraCompat.java @@ -1,6 +1,12 @@ package com.github.elenterius.biomancy.integration.tetra; +import com.github.elenterius.biomancy.tribute.TributeImpl; +import com.github.elenterius.biomancy.tribute.Tributes; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; +import se.mickelus.tetra.TetraMod; +import se.mickelus.tetra.TetraRegistries; +import se.mickelus.tetra.items.loot.DragonSinewItem; import se.mickelus.tetra.items.modular.IModularItem; import se.mickelus.tetra.properties.IToolProvider; @@ -14,7 +20,15 @@ public static void init(Consumer helperSetter) { } public static void onPostSetup() { - //TODO: register Dragon Sinew as Primordial Cradle tribute + registerDragonSinewAsTribute(); + } + + private static void registerDragonSinewAsTribute() { + ResourceLocation dragonSinewId = new ResourceLocation(TetraMod.MOD_ID, DragonSinewItem.identifier); + TetraRegistries.items.getEntries().stream() + .filter(registryObject -> registryObject.getId().equals(dragonSinewId)) + .findAny() + .ifPresent(registryObject -> Tributes.register(registryObject.get(), TributeImpl.builder().biomass(20).lifeEnergy(25).successModifier(20).hostileModifier(20).anomalyModifier(30).create())); } static final class TetraHelperImpl implements TetraHelper { diff --git a/src/main/java/com/github/elenterius/biomancy/item/BEWLBlockItem.java b/src/main/java/com/github/elenterius/biomancy/item/BEWLBlockItem.java index 9bf1d181d..547ca6df0 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/BEWLBlockItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/BEWLBlockItem.java @@ -1,8 +1,8 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.render.item.BEWLItemRenderer; import com.github.elenterius.biomancy.client.util.ClientTextUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/github/elenterius/biomancy/item/BioExtractorItem.java b/src/main/java/com/github/elenterius/biomancy/item/BioExtractorItem.java index bb433034a..4044382bb 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/BioExtractorItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/BioExtractorItem.java @@ -1,6 +1,5 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModEnchantments; import com.github.elenterius.biomancy.init.ModItems; @@ -9,6 +8,7 @@ import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.tooltip.HrTooltipComponent; import com.github.elenterius.biomancy.util.CombatUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.MobSoundUtil; import com.github.elenterius.biomancy.util.SoundUtil; import net.minecraft.ChatFormatting; diff --git a/src/main/java/com/github/elenterius/biomancy/item/CustomTooltipProvider.java b/src/main/java/com/github/elenterius/biomancy/item/CustomTooltipProvider.java index 076e7043c..db0770bb3 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/CustomTooltipProvider.java +++ b/src/main/java/com/github/elenterius/biomancy/item/CustomTooltipProvider.java @@ -1,9 +1,9 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.init.ModRarities; import com.github.elenterius.biomancy.styles.ColorStyles; import com.github.elenterius.biomancy.styles.TextComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.network.chat.MutableComponent; import net.minecraft.world.item.ItemStack; diff --git a/src/main/java/com/github/elenterius/biomancy/item/EffectCureItem.java b/src/main/java/com/github/elenterius/biomancy/item/EffectCureItem.java index 2804dbf3c..344f3a827 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/EffectCureItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/EffectCureItem.java @@ -1,20 +1,10 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.client.util.ClientTextUtil; -import com.github.elenterius.biomancy.tooltip.HrTooltipComponent; -import net.minecraft.network.chat.Component; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.inventory.tooltip.TooltipComponent; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; -import javax.annotation.Nullable; -import java.util.List; -import java.util.Optional; - -public class EffectCureItem extends Item implements CustomTooltipProvider { +public class EffectCureItem extends SimpleItem { public EffectCureItem(Properties properties) { super(properties); @@ -26,14 +16,4 @@ public ItemStack finishUsingItem(ItemStack stack, Level level, LivingEntity enti return entity.eat(level, stack); } - @Override - public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag isAdvanced) { - tooltip.add(ClientTextUtil.getItemInfoTooltip(stack.getItem())); - } - - @Override - public Optional getTooltipImage(ItemStack stack) { - return Optional.of(new HrTooltipComponent()); - } - } diff --git a/src/main/java/com/github/elenterius/biomancy/item/EssenceItem.java b/src/main/java/com/github/elenterius/biomancy/item/EssenceItem.java index 97b1d8db8..bbb39a011 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/EssenceItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/EssenceItem.java @@ -1,9 +1,9 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.tooltip.HrTooltipComponent; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/github/elenterius/biomancy/item/FleshChainBlockItem.java b/src/main/java/com/github/elenterius/biomancy/item/FleshChainBlockItem.java index 13681327e..b4f8bfe3f 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/FleshChainBlockItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/FleshChainBlockItem.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.item; import com.github.elenterius.biomancy.block.FleshChainBlock; -import com.github.elenterius.biomancy.chat.ComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; diff --git a/src/main/java/com/github/elenterius/biomancy/item/MobLootItem.java b/src/main/java/com/github/elenterius/biomancy/item/MobLootItem.java index 7d9278904..03decc655 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/MobLootItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/MobLootItem.java @@ -1,9 +1,9 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -35,21 +35,23 @@ public void appendHoverText(ItemStack stack, @Nullable Level level, List> mobs = taggedEntities.stream().limit(12).toList(); - int mobCount = mobs.size(); - if (mobCount > 0) { - MutableComponent component = ComponentUtil.mutable().withStyle(TextStyles.ITALIC_GRAY); - tooltip.add(component); - - for (int i = 0; i < mobCount; i++) { - component.append(mobs.get(i).getDescription()); - if (mobCount > 1 && i < mobCount - 1) component.append(", "); - } - if (mobCount < taggedEntities.size()) component.append(" ").append(TextComponentUtil.getTooltipText("and_more")); + MutableComponent component = ComponentUtil.mutable().withStyle(TextStyles.ITALIC_GRAY); + tooltip.add(component); + + int mobCount = 0; + for (EntityType entityType : taggedEntities) { + if (mobCount >= 12) break; + if (mobCount > 0) component.append(ComponentUtil.textSeparator()); + component.append(entityType.getDescription()); + mobCount++; + } + + if (mobCount < taggedEntities.size()) { + component.append(ComponentUtil.textSeparator()).append(ComponentUtil.ellipsis()); } } } diff --git a/src/main/java/com/github/elenterius/biomancy/item/ObfuscatedTooltipBlockItem.java b/src/main/java/com/github/elenterius/biomancy/item/ObfuscatedTooltipBlockItem.java index 8448d8f59..98dd8ef11 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/ObfuscatedTooltipBlockItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/ObfuscatedTooltipBlockItem.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.network.chat.Component; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; diff --git a/src/main/java/com/github/elenterius/biomancy/item/SapberryItem.java b/src/main/java/com/github/elenterius/biomancy/item/SapberryItem.java new file mode 100644 index 000000000..d43acffdf --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/item/SapberryItem.java @@ -0,0 +1,40 @@ +package com.github.elenterius.biomancy.item; + +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.alchemy.Potion; +import net.minecraft.world.level.Level; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Collection; + +public class SapberryItem extends SimpleItem { + + public SapberryItem(Properties properties) { + super(properties); + } + + private static void applyPotion(LivingEntity livingEntity, Potion potion) { + for (MobEffectInstance effectInstance : potion.getEffects()) { + if (effectInstance.getEffect().isInstantenous()) { + effectInstance.getEffect().applyInstantenousEffect(livingEntity, livingEntity, livingEntity, effectInstance.getAmplifier(), 1); + } + else { + livingEntity.addEffect(new MobEffectInstance(effectInstance)); + } + } + } + + @Override + public ItemStack finishUsingItem(ItemStack stack, Level level, LivingEntity livingEntity) { + ItemStack result = livingEntity.eat(level, stack); + + if (!level.isClientSide) { + Collection potions = ForgeRegistries.POTIONS.getValues(); + potions.stream().skip(level.random.nextInt(potions.size())).findFirst().ifPresent(potion -> applyPotion(livingEntity, potion)); + } + + return result; + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/item/SerumItem.java b/src/main/java/com/github/elenterius/biomancy/item/SerumItem.java index bd5d397ae..f2008106d 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/SerumItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/SerumItem.java @@ -2,8 +2,8 @@ import com.github.elenterius.biomancy.api.serum.Serum; import com.github.elenterius.biomancy.api.serum.SerumContainer; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; diff --git a/src/main/java/com/github/elenterius/biomancy/item/SimpleBlockItem.java b/src/main/java/com/github/elenterius/biomancy/item/SimpleBlockItem.java index aebc1d242..783cd85b7 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/SimpleBlockItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/SimpleBlockItem.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.network.chat.Component; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; diff --git a/src/main/java/com/github/elenterius/biomancy/item/SimpleItem.java b/src/main/java/com/github/elenterius/biomancy/item/SimpleItem.java index 26f258389..1d895bf96 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/SimpleItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/SimpleItem.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.item; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; diff --git a/src/main/java/com/github/elenterius/biomancy/item/StorageSacBlockItem.java b/src/main/java/com/github/elenterius/biomancy/item/StorageSacBlockItem.java index 181340079..a0db8f314 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/StorageSacBlockItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/StorageSacBlockItem.java @@ -1,13 +1,13 @@ package com.github.elenterius.biomancy.item; import com.github.elenterius.biomancy.block.storagesac.StorageSacBlockEntity; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModCapabilities; import com.github.elenterius.biomancy.inventory.ItemStackInventory; import com.github.elenterius.biomancy.inventory.itemhandler.EnhancedItemHandler; import com.github.elenterius.biomancy.tooltip.HrTooltipComponent; import com.github.elenterius.biomancy.tooltip.StorageSacTooltipComponent; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/github/elenterius/biomancy/item/injector/InjectorItem.java b/src/main/java/com/github/elenterius/biomancy/item/injector/InjectorItem.java index 13cc4bfa4..26388c933 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/injector/InjectorItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/injector/InjectorItem.java @@ -3,7 +3,6 @@ import com.github.elenterius.biomancy.api.serum.Serum; import com.github.elenterius.biomancy.api.serum.SerumContainer; import com.github.elenterius.biomancy.api.serum.SerumInjector; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.gui.InjectorScreen; import com.github.elenterius.biomancy.client.render.item.injector.InjectorRenderer; import com.github.elenterius.biomancy.client.util.ClientTextUtil; @@ -16,6 +15,7 @@ import com.github.elenterius.biomancy.item.KeyPressListener; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.util.CombatUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.SoundUtil; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; @@ -168,7 +168,8 @@ public void onServerReceiveKeyPress(ItemStack stack, ServerLevel level, Player p * replace contents of injector inventory with new item from player inventory */ private void setInventory(ItemStack injector, Player player, int slotIndex) { - ItemStack foundStack = player.getInventory().getItem(slotIndex); + final Inventory playerInventory = player.getInventory(); + final ItemStack foundStack = playerInventory.getItem(slotIndex); Item item = foundStack.getItem(); if (!(item instanceof SerumContainer)) return; @@ -180,16 +181,15 @@ private void setInventory(ItemStack injector, Player player, int slotIndex) { } ItemStack remainder = handler.insertItem(foundStack, false); - player.getInventory().setItem(slotIndex, remainder); + playerInventory.setItem(slotIndex, remainder); if (remainder.isEmpty() && !handler.isFull()) { - Inventory inventory = player.getInventory(); - int slots = inventory.getContainerSize(); + int slots = playerInventory.getContainerSize(); for (int idx = 0; idx < slots; idx++) { - ItemStack stack = inventory.getItem(idx); + ItemStack stack = playerInventory.getItem(idx); if (ItemHandlerHelper.canItemStacksStack(stack, handler.getStack())) { - remainder = handler.insertItem(foundStack, false); - player.getInventory().setItem(idx, remainder); + remainder = handler.insertItem(stack, false); + playerInventory.setItem(idx, remainder); if (!remainder.isEmpty()) break; } } @@ -197,7 +197,7 @@ private void setInventory(ItemStack injector, Player player, int slotIndex) { //eject old stuff if (!oldStack.isEmpty()) { - player.getInventory().placeItemBackInInventory(oldStack); + playerInventory.placeItemBackInInventory(oldStack); } }); } diff --git a/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingClawsItem.java b/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingClawsItem.java index 2d1dbe1c8..f405c8e2b 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingClawsItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingClawsItem.java @@ -1,6 +1,5 @@ package com.github.elenterius.biomancy.item.livingtool; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.entity.MobUtil; import com.github.elenterius.biomancy.init.ModSoundEvents; @@ -8,6 +7,7 @@ import com.github.elenterius.biomancy.item.weapon.SimpleClawsItem; import com.github.elenterius.biomancy.styles.ColorStyles; import com.github.elenterius.biomancy.styles.TextComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; diff --git a/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingSwordItem.java b/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingSwordItem.java index b678eee93..2ad2a7948 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingSwordItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingSwordItem.java @@ -1,12 +1,12 @@ package com.github.elenterius.biomancy.item.livingtool; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.entity.MobUtil; import com.github.elenterius.biomancy.item.CustomTooltipProvider; import com.github.elenterius.biomancy.item.KeyPressListener; import com.github.elenterius.biomancy.styles.ColorStyles; import com.github.elenterius.biomancy.styles.TextComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; diff --git a/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingTool.java b/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingTool.java index 2cb75d48e..e230f6eee 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingTool.java +++ b/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingTool.java @@ -1,10 +1,10 @@ package com.github.elenterius.biomancy.item.livingtool; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.item.NutrientsContainerItem; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.SoundUtil; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingToolState.java b/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingToolState.java index 69eed8bb3..e3b261bc7 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingToolState.java +++ b/src/main/java/com/github/elenterius/biomancy/item/livingtool/LivingToolState.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.item.livingtool; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.chat.ComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.MutableComponent; diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/BaseGunItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/BaseGunItem.java index 941357eaf..6637f967a 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/BaseGunItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/BaseGunItem.java @@ -1,10 +1,10 @@ package com.github.elenterius.biomancy.item.weapon; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.item.KeyPressListener; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/ControlStaffItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/ControlStaffItem.java index ec255d08c..8a701bb32 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/ControlStaffItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/ControlStaffItem.java @@ -1,6 +1,5 @@ package com.github.elenterius.biomancy.item.weapon; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.entity.ownable.Fleshkin; import com.github.elenterius.biomancy.entity.ownable.IControllableMob; @@ -8,6 +7,7 @@ import com.github.elenterius.biomancy.item.KeyPressListener; import com.github.elenterius.biomancy.ownable.IOwnableMob; import com.github.elenterius.biomancy.styles.TextComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/DevArmCannonItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/DevArmCannonItem.java index a8971c6fe..d2b349fac 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/DevArmCannonItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/DevArmCannonItem.java @@ -1,6 +1,5 @@ package com.github.elenterius.biomancy.item.weapon; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.gui.DevCannonScreen; import com.github.elenterius.biomancy.client.render.item.dev.DevArmCannonRenderer; import com.github.elenterius.biomancy.client.util.ClientTextUtil; @@ -11,7 +10,8 @@ import com.github.elenterius.biomancy.item.KeyPressListener; import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.styles.TextStyles; -import it.unimi.dsi.fastutil.floats.FloatUnaryOperator; +import com.github.elenterius.biomancy.util.ComponentUtil; +import com.github.elenterius.biomancy.util.function.FloatOperator; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.model.HumanoidModel; @@ -37,6 +37,7 @@ import software.bernie.geckolib3.core.IAnimatable; import software.bernie.geckolib3.core.manager.AnimationData; import software.bernie.geckolib3.core.manager.AnimationFactory; +import software.bernie.geckolib3.util.GeckoLibUtil; import javax.annotation.Nullable; import java.util.List; @@ -47,7 +48,7 @@ public class DevArmCannonItem extends Item implements IAnimatable, IArmPoseProvider, CustomTooltipProvider, KeyPressListener { public static final Set VALID_ENCHANTMENTS = Set.of(Enchantments.PUNCH_ARROWS, Enchantments.POWER_ARROWS); - private final AnimationFactory animationFactory = new AnimationFactory(this); + private final AnimationFactory animationFactory = GeckoLibUtil.createFactory(this); public DevArmCannonItem(Properties properties) { super(properties); @@ -110,7 +111,7 @@ public InteractionResultHolder use(Level level, Player player, Intera index = 0; stack.getOrCreateTag().putByte("ProjectileIndex", (byte) 0); } - ModProjectiles.PRECONFIGURED_PROJECTILES.get(index).shoot(level, player, FloatUnaryOperator.identity(), d -> d + getBonusDamage(stack), k -> k + getBonusKnockBack(stack), FloatUnaryOperator.identity()); + ModProjectiles.PRECONFIGURED_PROJECTILES.get(index).shoot(level, player, FloatOperator.IDENTITY, d -> d + getBonusDamage(stack), k -> k + getBonusKnockBack(stack), FloatOperator.IDENTITY); } return InteractionResultHolder.consume(player.getItemInHand(usedHand)); } diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/RavenousClawsItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/RavenousClawsItem.java index fd3a2c5ef..d956b4920 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/RavenousClawsItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/RavenousClawsItem.java @@ -1,6 +1,5 @@ package com.github.elenterius.biomancy.item.weapon; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.render.item.ravenousclaws.RavenousClawsRenderer; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.entity.MobUtil; @@ -13,6 +12,7 @@ import com.github.elenterius.biomancy.styles.TextComponentUtil; import com.github.elenterius.biomancy.styles.TextStyles; import com.github.elenterius.biomancy.util.CombatUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import com.github.elenterius.biomancy.util.SoundUtil; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/SimpleClawsItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/SimpleClawsItem.java index 4a29ad5d7..dc9877c05 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/SimpleClawsItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/SimpleClawsItem.java @@ -1,8 +1,8 @@ package com.github.elenterius.biomancy.item.weapon; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.item.CustomTooltipProvider; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Tier; diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/SimpleSwordItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/SimpleSwordItem.java index e1cb0e6d4..66ee95234 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/SimpleSwordItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/SimpleSwordItem.java @@ -1,8 +1,8 @@ package com.github.elenterius.biomancy.item.weapon; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.item.CustomTooltipProvider; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.SwordItem; diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/ToothGunItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/ToothGunItem.java index 7f0659921..3e19d1f9c 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/ToothGunItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/ToothGunItem.java @@ -28,7 +28,9 @@ public ToothGunItem(Properties properties) { } public static void fireProjectile(ServerLevel serverLevel, LivingEntity shooter, InteractionHand usedHand, ItemStack stack, ProjectileProperties properties) { - ToothProjectile projectile = new ToothProjectile(serverLevel, shooter); + ToothProjectile projectile = new ToothProjectile(serverLevel, shooter.getX(), shooter.getEyeY() - 0.1f, shooter.getZ()); + projectile.setOwner(shooter); + projectile.setDamage(properties.damage()); if (properties.knockBack() > 0) { projectile.setKnockback((byte) properties.knockBack()); diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/ToxicusItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/ToxicusItem.java index dc7f5b553..f8820732f 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/ToxicusItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/ToxicusItem.java @@ -1,6 +1,5 @@ package com.github.elenterius.biomancy.item.weapon; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.init.ModDamageSources; import com.github.elenterius.biomancy.init.ModMobEffects; import com.github.elenterius.biomancy.init.ModParticleTypes; @@ -9,6 +8,7 @@ import com.github.elenterius.biomancy.item.SweepAttackListener; import com.github.elenterius.biomancy.mixin.DamageSourceAccessor; import com.github.elenterius.biomancy.styles.TextComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; diff --git a/src/main/java/com/github/elenterius/biomancy/item/weapon/WithershotItem.java b/src/main/java/com/github/elenterius/biomancy/item/weapon/WithershotItem.java index 937e317c3..ec06f2430 100644 --- a/src/main/java/com/github/elenterius/biomancy/item/weapon/WithershotItem.java +++ b/src/main/java/com/github/elenterius/biomancy/item/weapon/WithershotItem.java @@ -29,7 +29,9 @@ public WithershotItem(Properties properties) { } public static void fireProjectile(ServerLevel serverLevel, LivingEntity shooter, InteractionHand hand, ItemStack projectileWeapon, ProjectileProperties properties) { - WitherProjectile projectile = new WitherProjectile(serverLevel, shooter); + WitherProjectile projectile = new WitherProjectile(serverLevel, shooter.getX(), shooter.getEyeY() - 0.1f, shooter.getZ()); + projectile.setOwner(shooter); + projectile.setDamage(properties.damage()); if (properties.knockBack() > 0) { projectile.setKnockback((byte) properties.knockBack()); diff --git a/src/main/java/com/github/elenterius/biomancy/menu/BioForgeMenu.java b/src/main/java/com/github/elenterius/biomancy/menu/BioForgeMenu.java index d6edecde6..44011de69 100644 --- a/src/main/java/com/github/elenterius/biomancy/menu/BioForgeMenu.java +++ b/src/main/java/com/github/elenterius/biomancy/menu/BioForgeMenu.java @@ -3,14 +3,14 @@ import com.github.elenterius.biomancy.BiomancyMod; import com.github.elenterius.biomancy.block.bioforge.BioForgeBlockEntity; import com.github.elenterius.biomancy.block.bioforge.BioForgeStateData; +import com.github.elenterius.biomancy.crafting.recipe.BioForgeRecipe; +import com.github.elenterius.biomancy.crafting.recipe.IngredientStack; import com.github.elenterius.biomancy.init.ModMenuTypes; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.inventory.BehavioralInventory; import com.github.elenterius.biomancy.menu.slot.FuelSlot; import com.github.elenterius.biomancy.menu.slot.ISlotZone; import com.github.elenterius.biomancy.menu.slot.OutputSlot; -import com.github.elenterius.biomancy.recipe.BioForgeRecipe; -import com.github.elenterius.biomancy.recipe.IngredientStack; import com.github.elenterius.biomancy.util.SoundUtil; import com.github.elenterius.biomancy.util.fuel.NutrientFuelUtil; import net.minecraft.network.FriendlyByteBuf; diff --git a/src/main/java/com/github/elenterius/biomancy/menu/DigesterMenu.java b/src/main/java/com/github/elenterius/biomancy/menu/DigesterMenu.java index 5bad7c3d7..7aa3f07dd 100644 --- a/src/main/java/com/github/elenterius/biomancy/menu/DigesterMenu.java +++ b/src/main/java/com/github/elenterius/biomancy/menu/DigesterMenu.java @@ -74,7 +74,7 @@ public int getFuelAmount() { return stateData.fuelHandler.getFuelAmount(); } - public int getMAxFuelAmount() { + public int getMaxFuelAmount() { return stateData.fuelHandler.getMaxFuelAmount(); } diff --git a/src/main/java/com/github/elenterius/biomancy/mixin/BiomeAccessor.java b/src/main/java/com/github/elenterius/biomancy/mixin/BiomeAccessor.java new file mode 100644 index 000000000..6b62af04c --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/mixin/BiomeAccessor.java @@ -0,0 +1,12 @@ +package com.github.elenterius.biomancy.mixin; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(Biome.class) +public interface BiomeAccessor { + @Invoker("getTemperature") + float biomancy$getTemperature(BlockPos pos); +} diff --git a/src/main/java/com/github/elenterius/biomancy/mixin/LivingEntityMixin.java b/src/main/java/com/github/elenterius/biomancy/mixin/LivingEntityMixin.java new file mode 100644 index 000000000..3f4664822 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/mixin/LivingEntityMixin.java @@ -0,0 +1,21 @@ +package com.github.elenterius.biomancy.mixin; + +import com.github.elenterius.biomancy.init.ModMobEffects; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(LivingEntity.class) +public abstract class LivingEntityMixin { + + @Inject(method = "isSensitiveToWater", at = @At(value = "HEAD"), cancellable = true) + private void onIsSensitiveToWater(CallbackInfoReturnable cir) { + LivingEntity livingEntity = (LivingEntity) (Object) this; + if (livingEntity.hasEffect(ModMobEffects.CORROSIVE.get())) { + cir.setReturnValue(true); + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/mixin/client/ClientRecipeBookMixin.java b/src/main/java/com/github/elenterius/biomancy/mixin/client/ClientRecipeBookMixin.java new file mode 100644 index 000000000..e7e54ccb4 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/mixin/client/ClientRecipeBookMixin.java @@ -0,0 +1,89 @@ +package com.github.elenterius.biomancy.mixin.client; + +import com.github.elenterius.biomancy.init.ModRecipes; +import com.github.elenterius.biomancy.integration.BioForgeCompat; +import com.google.common.collect.*; +import net.minecraft.client.ClientRecipeBook; +import net.minecraft.client.RecipeBookCategories; +import net.minecraft.client.gui.screens.recipebook.RecipeCollection; +import net.minecraft.world.item.crafting.Recipe; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Mixin(value = ClientRecipeBook.class, priority = 999) +public abstract class ClientRecipeBookMixin { + + @Shadow + private static RecipeBookCategories getCategory(Recipe pRecipe) { + return null; + } + + @Shadow + private List allCollections; + + @Shadow + private Map> collectionsByTab; + + @Inject(method = "setupCollections", at = @At("HEAD")) + private void onSetupCollections(Iterable> recipes, CallbackInfo ci) { + if (BioForgeCompat.isRecipeCollectionOverwriteEnabled()) { + biomancy$overwriteRecipeCollections(recipes); + } + } + + @Unique + private void biomancy$overwriteRecipeCollections(Iterable> allRecipes) { + Map>>> categorizedRecipes = biomancy$categorizeBioForgeRecipes(allRecipes); + Map> recipeCategories = new HashMap<>(); //we can't use EnumMap because of Forge modifying the enum at runtime + + ImmutableList.Builder builder = ImmutableList.builder(); + + categorizedRecipes.forEach((category, groupedRecipes) -> recipeCategories.put(category, groupedRecipes.stream().map(recipes -> { + RecipeCollection collection = new RecipeCollection(recipes); + builder.add(collection); + return collection; + }).toList())); + + RecipeBookCategories.AGGREGATE_CATEGORIES.forEach((mainCategory, subCategories) -> recipeCategories.put(mainCategory, subCategories.stream() + .flatMap(category -> recipeCategories.getOrDefault(category, List.of()).stream()).toList()) + ); + + collectionsByTab = Map.copyOf(recipeCategories); + allCollections = builder.build(); + } + + @Unique + private static Map>>> biomancy$categorizeBioForgeRecipes(Iterable> recipes) { + Map>>> map = Maps.newHashMap(); + Table>> table = HashBasedTable.create(); + + for (Recipe recipe : recipes) { + if (recipe.getType() != ModRecipes.BIO_FORGING_RECIPE_TYPE.get() || recipe.isSpecial() || recipe.isIncomplete()) continue; + + RecipeBookCategories category = getCategory(recipe); + String group = recipe.getGroup().isEmpty() ? recipe.getId().toString() : recipe.getGroup(); + if (group.isEmpty()) { + map.computeIfAbsent(category, categories -> Lists.newArrayList()).add(List.of(recipe)); + } + else { + List> list = table.get(category, group); + if (list == null) { + list = Lists.newArrayList(); + table.put(category, group, list); + map.computeIfAbsent(category, categories -> Lists.newArrayList()).add(list); + } + list.add(recipe); + } + } + + return map; + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/network/BioForgeRecipeMessage.java b/src/main/java/com/github/elenterius/biomancy/network/BioForgeRecipeMessage.java index ac4a29a3c..cee64ce6e 100644 --- a/src/main/java/com/github/elenterius/biomancy/network/BioForgeRecipeMessage.java +++ b/src/main/java/com/github/elenterius/biomancy/network/BioForgeRecipeMessage.java @@ -1,8 +1,8 @@ package com.github.elenterius.biomancy.network; +import com.github.elenterius.biomancy.crafting.recipe.BioForgeRecipe; import com.github.elenterius.biomancy.init.ModRecipes; import com.github.elenterius.biomancy.menu.BioForgeMenu; -import com.github.elenterius.biomancy.recipe.BioForgeRecipe; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; diff --git a/src/main/java/com/github/elenterius/biomancy/network/ModNetworkHandler.java b/src/main/java/com/github/elenterius/biomancy/network/ModNetworkHandler.java index fff1ab1c1..3597b4607 100644 --- a/src/main/java/com/github/elenterius/biomancy/network/ModNetworkHandler.java +++ b/src/main/java/com/github/elenterius/biomancy/network/ModNetworkHandler.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.network; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.recipe.BioForgeRecipe; +import com.github.elenterius.biomancy.crafting.recipe.BioForgeRecipe; import net.minecraft.core.BlockPos; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.EquipmentSlot; diff --git a/src/main/java/com/github/elenterius/biomancy/ownable/IOwnableEntityBlock.java b/src/main/java/com/github/elenterius/biomancy/ownable/IOwnableEntityBlock.java index ab0ab4f0d..5a752fed2 100644 --- a/src/main/java/com/github/elenterius/biomancy/ownable/IOwnableEntityBlock.java +++ b/src/main/java/com/github/elenterius/biomancy/ownable/IOwnableEntityBlock.java @@ -1,10 +1,10 @@ package com.github.elenterius.biomancy.ownable; import com.github.elenterius.biomancy.block.ownable.OwnableBlockEntity; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.permission.UserType; import com.github.elenterius.biomancy.styles.TextComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.screens.Screen; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/github/elenterius/biomancy/serum/AdrenalineSerum.java b/src/main/java/com/github/elenterius/biomancy/serum/AdrenalineSerum.java index d6dc567cd..4454bf2a5 100644 --- a/src/main/java/com/github/elenterius/biomancy/serum/AdrenalineSerum.java +++ b/src/main/java/com/github/elenterius/biomancy/serum/AdrenalineSerum.java @@ -1,9 +1,9 @@ package com.github.elenterius.biomancy.serum; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModMobEffects; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/github/elenterius/biomancy/serum/BasicSerum.java b/src/main/java/com/github/elenterius/biomancy/serum/BasicSerum.java index eaa3a6709..e69a24d73 100644 --- a/src/main/java/com/github/elenterius/biomancy/serum/BasicSerum.java +++ b/src/main/java/com/github/elenterius/biomancy/serum/BasicSerum.java @@ -1,10 +1,10 @@ package com.github.elenterius.biomancy.serum; import com.github.elenterius.biomancy.api.serum.Serum; -import com.github.elenterius.biomancy.chat.ComponentUtil; import com.github.elenterius.biomancy.client.util.ClientTextUtil; import com.github.elenterius.biomancy.init.ModSerums; import com.github.elenterius.biomancy.styles.TextStyles; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; diff --git a/src/main/java/com/github/elenterius/biomancy/statuseffect/CorrosiveEffect.java b/src/main/java/com/github/elenterius/biomancy/statuseffect/CorrosiveEffect.java index c154bd554..e19e52426 100644 --- a/src/main/java/com/github/elenterius/biomancy/statuseffect/CorrosiveEffect.java +++ b/src/main/java/com/github/elenterius/biomancy/statuseffect/CorrosiveEffect.java @@ -1,6 +1,7 @@ package com.github.elenterius.biomancy.statuseffect; import com.github.elenterius.biomancy.init.ModDamageSources; +import com.github.elenterius.biomancy.init.ModParticleTypes; import com.github.elenterius.biomancy.util.CombatUtil; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; @@ -25,7 +26,8 @@ public CorrosiveEffect(MobEffectCategory category, int color) { @Override public void applyEffectTick(LivingEntity livingEntity, int amplifier) { int effectLevel = amplifier + 1; - float conversionProbability = 0.1f + 0.05f * effectLevel; + boolean isWet = livingEntity.isInWaterRainOrBubble(); + float conversionProbability = 0.1f + 0.05f * effectLevel + (isWet ? 0.15f : 0); if (livingEntity.level instanceof ServerLevel serverLevel && livingEntity.level.random.nextFloat() < conversionProbability) { DamageSource lastDamageSource = livingEntity.getLastDamageSource(); @@ -36,20 +38,26 @@ public void applyEffectTick(LivingEntity livingEntity, int amplifier) { if (!livingEntity.isAlive()) return; - int damage = 2 * effectLevel; + float damage = 2 * effectLevel + (isWet ? 0.5f : 0); CombatUtil.hurtWithAcid(livingEntity, damage); + + if (livingEntity.level.random.nextFloat() < 0.85f && livingEntity.level instanceof ServerLevel serverLevel) { + float xz = livingEntity.getBbWidth() * 0.25f; + float y = livingEntity.getBbHeight() * 0.25f; + serverLevel.sendParticles(ModParticleTypes.FALLING_ACID.get(), livingEntity.getX(), livingEntity.getY(0.5f), livingEntity.getZ(), 4, xz, y, xz, 0); + } } @Override public boolean isDurationEffectTick(int duration, int amplifier) { - return duration % 7 == 0; + return (duration + 1) % 8 == 0; } private boolean convertZombieToSkeleton(ServerLevel level, LivingEntity livingEntity) { if (livingEntity instanceof Zombie zombie && ForgeEventFactory.canLivingConvert(zombie, EntityType.SKELETON, timer -> {})) { Skeleton skeleton = zombie.convertTo(EntityType.SKELETON, true); // create new entity with same settings & equipment and remove old entity if (skeleton != null) { - // skeleton.finalizeSpawn(level, level.getCurrentDifficultyAt(zombie.blockPosition()), MobSpawnType.CONVERSION, null, null); + //skeleton.finalizeSpawn(level, level.getCurrentDifficultyAt(zombie.blockPosition()), MobSpawnType.CONVERSION, null, null); skeleton.invulnerableTime = 60; ForgeEventFactory.onLivingConvert(zombie, skeleton); if (!zombie.isSilent()) { @@ -61,7 +69,7 @@ private boolean convertZombieToSkeleton(ServerLevel level, LivingEntity livingEn else if (livingEntity instanceof ZombieHorse zombieHorse && ForgeEventFactory.canLivingConvert(zombieHorse, EntityType.SKELETON_HORSE, timer -> {})) { SkeletonHorse horse = zombieHorse.convertTo(EntityType.SKELETON_HORSE, true); // create new entity with same settings & equipment and remove old entity if (horse != null) { - // horse.finalizeSpawn(level, level.getCurrentDifficultyAt(zombieHorse.blockPosition()), MobSpawnType.CONVERSION, null, null); + //horse.finalizeSpawn(level, level.getCurrentDifficultyAt(zombieHorse.blockPosition()), MobSpawnType.CONVERSION, null, null); horse.invulnerableTime = 60; UUID owner = zombieHorse.getOwnerUUID(); if (owner != null) { diff --git a/src/main/java/com/github/elenterius/biomancy/styles/TextComponentUtil.java b/src/main/java/com/github/elenterius/biomancy/styles/TextComponentUtil.java index 37f2bbced..19da31bd4 100644 --- a/src/main/java/com/github/elenterius/biomancy/styles/TextComponentUtil.java +++ b/src/main/java/com/github/elenterius/biomancy/styles/TextComponentUtil.java @@ -1,7 +1,7 @@ package com.github.elenterius.biomancy.styles; import com.github.elenterius.biomancy.BiomancyMod; -import com.github.elenterius.biomancy.chat.ComponentUtil; +import com.github.elenterius.biomancy.util.ComponentUtil; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.world.item.Item; diff --git a/src/main/java/com/github/elenterius/biomancy/styles/TextStyles.java b/src/main/java/com/github/elenterius/biomancy/styles/TextStyles.java index 2c9592db3..e14a862b7 100644 --- a/src/main/java/com/github/elenterius/biomancy/styles/TextStyles.java +++ b/src/main/java/com/github/elenterius/biomancy/styles/TextStyles.java @@ -6,8 +6,10 @@ public final class TextStyles { public static final Style PRIMORDIAL_RUNES_GRAY = Style.EMPTY.withFont(Fonts.CARO_INVITICA).withColor(ChatFormatting.GRAY); + public static final Style PRIMORDIAL_RUNES_LIGHT_GRAY = Style.EMPTY.withFont(Fonts.CARO_INVITICA).withColor(0xff_e5e4e2); public static final Style PRIMORDIAL_RUNES_RED = Style.EMPTY.withFont(Fonts.CARO_INVITICA).withColor(0xff_bb0b3f); + public static final Style GRAY = Style.EMPTY.withColor(ChatFormatting.GRAY); public static final Style LORE = Style.EMPTY.withColor(ChatFormatting.DARK_GRAY).withItalic(true); public static final Style ITALIC_GRAY = Style.EMPTY.withColor(ChatFormatting.GRAY).withItalic(true); public static final Style ERROR = Style.EMPTY.withColor(ColorStyles.TEXT_ERROR); diff --git a/src/main/java/com/github/elenterius/biomancy/block/cradle/MobEffectTribute.java b/src/main/java/com/github/elenterius/biomancy/tribute/MobEffectTribute.java similarity index 78% rename from src/main/java/com/github/elenterius/biomancy/block/cradle/MobEffectTribute.java rename to src/main/java/com/github/elenterius/biomancy/tribute/MobEffectTribute.java index f5df57faf..3c8efc244 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/cradle/MobEffectTribute.java +++ b/src/main/java/com/github/elenterius/biomancy/tribute/MobEffectTribute.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.block.cradle; +package com.github.elenterius.biomancy.tribute; import com.github.elenterius.biomancy.init.ModMobEffects; import com.mojang.datafixers.util.Pair; @@ -13,7 +13,7 @@ import java.util.List; import java.util.Set; -class MobEffectTribute implements ITribute { +class MobEffectTribute implements Tribute { //TODO: Replace with TagKeys? // @@ -29,24 +29,29 @@ class MobEffectTribute implements ITribute { private static final Set LIFE_ENERGY_MODIFIER = Set.of(MobEffects.HEAL, MobEffects.REGENERATION, MobEffects.HEALTH_BOOST); private static final Set DISEASE_MODIFIER = Set.of(MobEffects.HUNGER, MobEffects.WITHER, MobEffects.POISON, MobEffects.CONFUSION, ModMobEffects.CORROSIVE.get()); - private static final Set HOSTILE_MODIFIER = Set.of(MobEffects.DAMAGE_BOOST, MobEffects.JUMP, MobEffects.DAMAGE_RESISTANCE, MobEffects.FIRE_RESISTANCE, MobEffects.MOVEMENT_SPEED); + private float lifeEnergy = 0; private int diseaseModifier = 0; - private int hostileModifier = 0; private int successModifier = 0; - static MobEffectTribute from(ItemStack stack) { + static Tribute from(ItemStack stack) { + boolean isPotionItem = stack.getItem() instanceof PotionItem; //we don't check if the potion has no effects because it should contain an effect in 99% of cases + + FoodProperties food = stack.getFoodProperties(null); + boolean isFoodItem = food != null && !food.getEffects().isEmpty(); //we check if the food has any effects because they are optional + + if (!isPotionItem && !isFoodItem) return Tribute.EMPTY; //avoid creation of new empty objects + MobEffectTribute mobEffectTribute = new MobEffectTribute(); - if (stack.getItem() instanceof PotionItem) { + if (isPotionItem) { List effectInstances = PotionUtils.getMobEffects(stack); for (MobEffectInstance instance : effectInstances) { mobEffectTribute.apply(instance, 1f); } } - FoodProperties food = stack.getFoodProperties(null); - if (food != null) { + if (isFoodItem) { for (Pair pair : food.getEffects()) { MobEffectInstance instance = pair.getFirst(); float chance = pair.getSecond(); @@ -62,27 +67,17 @@ void apply(MobEffectInstance instance, float chance) { if (LIFE_ENERGY_MODIFIER.contains(effect)) { int level = instance.getAmplifier() + 1; - lifeEnergy += level * 50 * chance; + lifeEnergy += level * 60 * chance; } else if (DISEASE_MODIFIER.contains(effect)) { int level = instance.getAmplifier() + 1; diseaseModifier += Math.round(level * 15 * chance); } - else if (HOSTILE_MODIFIER.contains(effect)) { - int level = instance.getAmplifier() + 1; - hostileModifier += Math.round(level * 10 * chance); - } else if (effect == MobEffects.LUCK) { int level = instance.getAmplifier() + 1; successModifier += Math.round(level * 50 * chance); } - if (effect.isBeneficial()) { - hostileModifier -= 10; - } - else { - hostileModifier += 10; - } } @Override @@ -107,7 +102,7 @@ public int diseaseModifier() { @Override public int hostileModifier() { - return hostileModifier; + return 0; } @Override diff --git a/src/main/java/com/github/elenterius/biomancy/block/cradle/ITribute.java b/src/main/java/com/github/elenterius/biomancy/tribute/Tribute.java similarity index 81% rename from src/main/java/com/github/elenterius/biomancy/block/cradle/ITribute.java rename to src/main/java/com/github/elenterius/biomancy/tribute/Tribute.java index e1b311cd3..2e8dee623 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/cradle/ITribute.java +++ b/src/main/java/com/github/elenterius/biomancy/tribute/Tribute.java @@ -1,7 +1,10 @@ -package com.github.elenterius.biomancy.block.cradle; +package com.github.elenterius.biomancy.tribute; -public interface ITribute { - ITribute EMPTY = new ITribute() { +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public interface Tribute { + Tribute EMPTY = new Tribute() { @Override public int biomass() { return 0; diff --git a/src/main/java/com/github/elenterius/biomancy/block/cradle/Tribute.java b/src/main/java/com/github/elenterius/biomancy/tribute/TributeImpl.java similarity index 71% rename from src/main/java/com/github/elenterius/biomancy/block/cradle/Tribute.java rename to src/main/java/com/github/elenterius/biomancy/tribute/TributeImpl.java index 4f8ccdc5d..6b8d67775 100644 --- a/src/main/java/com/github/elenterius/biomancy/block/cradle/Tribute.java +++ b/src/main/java/com/github/elenterius/biomancy/tribute/TributeImpl.java @@ -1,7 +1,7 @@ -package com.github.elenterius.biomancy.block.cradle; +package com.github.elenterius.biomancy.tribute; -public record Tribute(int biomass, int lifeEnergy, int successModifier, int diseaseModifier, int hostileModifier, int anomalyModifier) implements ITribute { - public Tribute(ITribute a, ITribute b) { +public record TributeImpl(int biomass, int lifeEnergy, int successModifier, int diseaseModifier, int hostileModifier, int anomalyModifier) implements Tribute { + public TributeImpl(Tribute a, Tribute b) { this( a.biomass() + b.biomass(), a.lifeEnergy() + b.lifeEnergy(), @@ -12,8 +12,8 @@ public Tribute(ITribute a, ITribute b) { ); } - public static Tribute.Builder builder() { - return new Tribute.Builder(); + public static TributeImpl.Builder builder() { + return new TributeImpl.Builder(); } public static class Builder { @@ -54,8 +54,8 @@ public Builder anomalyModifier(int anomalyModifier) { return this; } - public ITribute create() { - return new Tribute(biomass, lifeEnergy, successModifier, diseaseModifier, hostileModifier, anomalyModifier); + public Tribute create() { + return new TributeImpl(biomass, lifeEnergy, successModifier, diseaseModifier, hostileModifier, anomalyModifier); } } } diff --git a/src/main/java/com/github/elenterius/biomancy/tribute/Tributes.java b/src/main/java/com/github/elenterius/biomancy/tribute/Tributes.java new file mode 100644 index 000000000..0af5b72b5 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/tribute/Tributes.java @@ -0,0 +1,103 @@ +package com.github.elenterius.biomancy.tribute; + +import com.github.elenterius.biomancy.init.ModItems; +import com.github.elenterius.biomancy.init.tags.ModItemTags; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraftforge.common.Tags; +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +@ApiStatus.Experimental +public final class Tributes { + + private static final Map TRIBUTES = new HashMap<>(); + private static final List FUZZY_TRIBUTES = new ArrayList<>(); + + public static final Tribute INVALID_ITEM = TributeImpl.builder().successModifier(-99).diseaseModifier(5).hostileModifier(20).create(); + + public static final Tribute EXOTIC_MEAL = register(ModItems.CREATOR_MIX.get(), TributeImpl.builder().biomass(20).lifeEnergy(20).successModifier(19).diseaseModifier(6).hostileModifier(-12).create()); + public static final Tribute PRIMORDIAL_CORE = register(ModItems.PRIMORDIAL_CORE.get(), TributeImpl.builder().biomass(80).successModifier(64).anomalyModifier(100).diseaseModifier(50).create()); + + public static final Tribute LIVING_FLESH = register(ModItems.LIVING_FLESH.get(), TributeImpl.builder().biomass(10).lifeEnergy(10).successModifier(40).anomalyModifier(55).create()); + public static final Tribute BLOOMBERRY = register(ModItems.BLOOMBERRY.get(), TributeImpl.builder().successModifier(10).lifeEnergy(20).anomalyModifier(20).create()); + public static final Tribute GOLDEN_APPLE = register(Items.GOLDEN_APPLE, TributeImpl.builder().successModifier(10).hostileModifier(-100).create()); + public static final Tribute ENCHANTED_GOLDEN_APPLE = register(Items.ENCHANTED_GOLDEN_APPLE, TributeImpl.builder().lifeEnergy(15).successModifier(40).hostileModifier(-200).create()); + public static final Tribute CAKE = register(Items.CAKE, TributeImpl.builder().hostileModifier(-80).diseaseModifier(10).create()); + + public static final Tribute HEALING_ADDITIVE = register(ModItems.HEALING_ADDITIVE.get(), TributeImpl.builder().lifeEnergy(55).successModifier(1).diseaseModifier(-5).hostileModifier(-10).create()); + public static final Tribute REGENERATIVE_FLUID = register(ModItems.REGENERATIVE_FLUID.get(), TributeImpl.builder().lifeEnergy(5).hostileModifier(-1).create()); + + public static final Tribute ROTTEN_FLESH = register(Items.ROTTEN_FLESH, TributeImpl.builder().biomass(10).successModifier(-10).diseaseModifier(20).create()); + public static final Tribute MOB_SINEW = register(ModItems.MOB_SINEW.get(), TributeImpl.builder().biomass(5).successModifier(2).hostileModifier(-2).create()); + public static final Tribute FLESH_BITS = register(ModItems.FLESH_BITS.get(), TributeImpl.builder().biomass(5).successModifier(2).hostileModifier(-2).create()); + + public static final Tribute RABBIT_FOOT = register(Items.RABBIT_FOOT, TributeImpl.builder().successModifier(1000).hostileModifier(-50).anomalyModifier(50).create()); + public static final Tribute SPIDER_EYE = register(Items.SPIDER_EYE, TributeImpl.builder().successModifier(10).diseaseModifier(10).hostileModifier(-5).create()); + public static final Tribute FERMENTED_SPIDER_EYE = register(Items.FERMENTED_SPIDER_EYE, TributeImpl.builder().successModifier(-10).hostileModifier(-10).create()); + public static final Tribute TOXIN_GLAND = register(ModItems.TOXIN_GLAND.get(), TributeImpl.builder().successModifier(-5).diseaseModifier(50).create()); + public static final Tribute VOLATILE_GLAND = register(ModItems.VOLATILE_GLAND.get(), TributeImpl.builder().successModifier(-5).diseaseModifier(20).create()); + public static final Tribute GENERIC_MOB_GLAND = register(ModItems.GENERIC_MOB_GLAND.get(), TributeImpl.builder().diseaseModifier(-5).hostileModifier(-20).create()); + public static final Tribute BONE = register(Items.BONE, TributeImpl.builder().successModifier(3).diseaseModifier(-10).create()); + public static final Tribute BONE_MEAL = register(Items.BONE_MEAL, TributeImpl.builder().successModifier(1).diseaseModifier(-1).create()); + public static final Tribute MOB_MARROW = register(ModItems.MOB_MARROW.get(), TributeImpl.builder().successModifier(5).diseaseModifier(-20).hostileModifier(-10).create()); + public static final Tribute WITHERED_MOB_MARROW = register(ModItems.WITHERED_MOB_MARROW.get(), TributeImpl.builder().successModifier(-30).diseaseModifier(-40).create()); + public static final Tribute MOB_FANG = register(ModItems.MOB_FANG.get(), TributeImpl.builder().successModifier(5).hostileModifier(5).create()); + public static final Tribute MOB_CLAW = register(ModItems.MOB_CLAW.get(), TributeImpl.builder().successModifier(5).hostileModifier(5).create()); + public static final Tribute ENDER_PEARL = register(Items.ENDER_PEARL, TributeImpl.builder().hostileModifier(50).anomalyModifier(50).create()); + public static final Tribute NETHER_STAR = register(Items.NETHER_STAR, TributeImpl.builder().lifeEnergy(15_000).hostileModifier(100).diseaseModifier(100).create()); + public static final Tribute TOTEM_OF_UNDYING = register(Items.TOTEM_OF_UNDYING, TributeImpl.builder().lifeEnergy(2_500).successModifier(100).hostileModifier(-500).create()); + + public static final Tribute ELASTIC_FIBERS = register(ModItems.ELASTIC_FIBERS.get(), TributeImpl.builder().diseaseModifier(1).anomalyModifier(1).create()); + public static final Tribute TOUGH_FIBERS = register(ModItems.TOUGH_FIBERS.get(), TributeImpl.builder().diseaseModifier(1).anomalyModifier(1).create()); + + // tributes which are matched when no tribute was found beforehand + public static final Tribute RAW_MEATS = registerFuzzy(stack -> stack.is(ModItemTags.RAW_MEATS), TributeImpl.builder().biomass(20).successModifier(16).diseaseModifier(5).hostileModifier(-5).create()); + public static final Tribute COOKED_MEATS = registerFuzzy(stack -> stack.is(ModItemTags.COOKED_MEATS), TributeImpl.builder().successModifier(-999).create()); + public static final Tribute BONES = registerFuzzy(stack -> stack.is(Tags.Items.BONES), TributeImpl.builder().successModifier(5).diseaseModifier(-5).create()); + + private Tributes() {} + + public static Tribute register(Item item, Tribute tribute) { + TRIBUTES.put(item, tribute); + return tribute; + } + + public static Tribute registerFuzzy(Predicate predicate, Tribute tribute) { + FUZZY_TRIBUTES.add(new FuzzyTribute(predicate, tribute)); + return tribute; + } + + public static Tribute from(ItemStack stack) { + + Tribute mobEffectTribute = MobEffectTribute.from(stack); + Tribute tribute = findExistingTribute(stack); + + if (mobEffectTribute.isEmpty() && tribute.isEmpty()) { + return INVALID_ITEM; + } + + return new TributeImpl(tribute, mobEffectTribute); + } + + private static Tribute findExistingTribute(ItemStack stack) { + if (stack.isEmpty()) return Tribute.EMPTY; + + Tribute foundTribute = TRIBUTES.get(stack.getItem()); + if (foundTribute != null) return foundTribute; + + for (FuzzyTribute fuzzyTribute : FUZZY_TRIBUTES) { + if (fuzzyTribute.predicate().test(stack)) return fuzzyTribute.tribute(); + } + + return Tribute.EMPTY; + } + + record FuzzyTribute(Predicate predicate, Tribute tribute) {} +} diff --git a/src/main/java/com/github/elenterius/biomancy/util/Bit32Set.java b/src/main/java/com/github/elenterius/biomancy/util/Bit32Set.java index e89bf5be6..00d8183e1 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/Bit32Set.java +++ b/src/main/java/com/github/elenterius/biomancy/util/Bit32Set.java @@ -1,5 +1,7 @@ package com.github.elenterius.biomancy.util; +import com.github.elenterius.biomancy.util.serialization.IntegerSerializable; + public class Bit32Set implements IntegerSerializable { private int bits; @@ -58,6 +60,23 @@ public int nextSetBit(int fromIndex) { return 32; } + /** + * @return indices of all bits set to true + */ + public int[] getIndices() { + if (bits == 0) return new int[]{}; + int[] indices = new int[Integer.bitCount(bits)]; + + int n = 0; + for (int i = 0; i < 32; i++) { + if ((bits & (1 << i)) != 0) { + indices[n++] = i; + } + } + + return indices; + } + public int getBits() { return bits; } diff --git a/src/main/java/com/github/elenterius/biomancy/util/CombatUtil.java b/src/main/java/com/github/elenterius/biomancy/util/CombatUtil.java index 7d3541baa..0ed66dae3 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/CombatUtil.java +++ b/src/main/java/com/github/elenterius/biomancy/util/CombatUtil.java @@ -53,6 +53,7 @@ public static void applyAcidEffect(LivingEntity livingEntity, int seconds) { public static void hurtWithAcid(LivingEntity livingEntity, float damage) { livingEntity.invulnerableTime = 0; //bypass invulnerable ticks livingEntity.hurt(ModDamageSources.CORROSIVE_ACID, damage); + livingEntity.invulnerableTime = 0; //leave open for next damage } public static void hurtWithBleed(LivingEntity livingEntity, float damage) { diff --git a/src/main/java/com/github/elenterius/biomancy/chat/ComponentUtil.java b/src/main/java/com/github/elenterius/biomancy/util/ComponentUtil.java similarity index 86% rename from src/main/java/com/github/elenterius/biomancy/chat/ComponentUtil.java rename to src/main/java/com/github/elenterius/biomancy/util/ComponentUtil.java index eebb32782..02a8c76fe 100644 --- a/src/main/java/com/github/elenterius/biomancy/chat/ComponentUtil.java +++ b/src/main/java/com/github/elenterius/biomancy/util/ComponentUtil.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.chat; +package com.github.elenterius.biomancy.util; import com.github.elenterius.biomancy.tooltip.EmptyLineTooltipComponent; import com.github.elenterius.biomancy.tooltip.HrTooltipComponent; @@ -15,9 +15,11 @@ import java.util.Optional; -@SuppressWarnings("deprecation") public final class ComponentUtil { + private static final Component SPACE = Component.literal(" "); + private static final Component TEXT_SEPARATOR = Component.literal(", "); + private ComponentUtil() {} public static Component nullToEmpty(@Nullable String text) { @@ -45,12 +47,31 @@ public static Component empty() { } /** - * force empty line iin item tooltips + * whitespace + */ + public static Component space() { + return SPACE; + } + + /** + * force empty line in item tooltips */ public static Component emptyLine() { return TooltipHacks.EMPTY_LINE_COMPONENT; } + public static Component newLine() { + return CommonComponents.NEW_LINE; + } + + public static Component ellipsis() { + return CommonComponents.ELLIPSIS; + } + + public static Component textSeparator() { + return TEXT_SEPARATOR; + } + /** * horizontal line in item tooltips */ diff --git a/src/main/java/com/github/elenterius/biomancy/util/EnhancedIntegerProperty.java b/src/main/java/com/github/elenterius/biomancy/util/EnhancedIntegerProperty.java index 814bc5e9d..2bccec381 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/EnhancedIntegerProperty.java +++ b/src/main/java/com/github/elenterius/biomancy/util/EnhancedIntegerProperty.java @@ -35,6 +35,10 @@ public BlockState setValue(BlockState state, int value) { return state.setValue(property, Mth.clamp(value, min, max)); } + public BlockState addValue(BlockState state, int value) { + return setValue(state, state.getValue(property) + value); + } + public int getMin() { return ((IntegerPropertyAccessor) property).biomancy$getMin(); } diff --git a/src/main/java/com/github/elenterius/biomancy/util/LevelUtil.java b/src/main/java/com/github/elenterius/biomancy/util/LevelUtil.java index 61aed474f..71246c2c4 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/LevelUtil.java +++ b/src/main/java/com/github/elenterius/biomancy/util/LevelUtil.java @@ -7,6 +7,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -15,6 +16,8 @@ import net.minecraftforge.items.IItemHandler; import org.jetbrains.annotations.Nullable; +import java.util.function.Predicate; + public final class LevelUtil { private LevelUtil() {} @@ -30,6 +33,13 @@ public static LazyOptional getItemHandler(ServerLevel level, Block return LazyOptional.empty(); } + /** + * @return max(skyLight, blockLight) in the range of [0, ..., 15] + */ + public static int getMaxBrightness(Level level, BlockPos pos) { + return level.getLightEngine().getRawBrightness(pos, 0); + } + public static int getNumOfBlocksAbove(BlockGetter level, BlockPos pos, Block targetBlock, int maxHeight) { int i = 0; while (i < maxHeight && level.getBlockState(pos.above(i + 1)).getBlock() == targetBlock) i++; @@ -42,6 +52,27 @@ public static int getNumOfBlocksBelow(BlockGetter level, BlockPos pos, Block tar return i; } + public static boolean isBlockNearby(ServerLevel level, BlockPos pos, int range, Predicate predicate) { + BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); + + for (int y = 0; y <= range; y = y > 0 ? -y : 1 - y) { + for (int xz = 0; xz < range; ++xz) { + for (int x = 0; x <= xz; x = x > 0 ? -x : 1 - x) { + for (int z = x < xz && x > -xz ? xz : 0; z <= xz; z = z > 0 ? -z : 1 - z) { + if ((x * x + y * y + z * z) > range * range) continue; + + mutablePos.setWithOffset(pos, x, y - 1, z); + if (predicate.test(level.getBlockState(mutablePos))) { + return true; + } + } + } + } + } + + return false; + } + public static boolean isChunkCloserToPosThan(int chunkX, int chunkZ, BlockPos pos, double distanceSquared) { int minBlockX = SectionPos.sectionToBlockCoord(chunkX); int minBlockZ = SectionPos.sectionToBlockCoord(chunkZ); @@ -57,6 +88,9 @@ public static boolean isChunkCloserToPosThan(int chunkX, int chunkZ, BlockPos po return (dx * dx + dz * dz) < distanceSquared; } + /** + * performance: vroom vroom + */ @Nullable public static T findNearestBlockEntity(ServerLevel level, BlockPos pos, int searchDist, Class clazz) { if (searchDist <= 0) return null; diff --git a/src/main/java/com/github/elenterius/biomancy/util/TemperatureUtil.java b/src/main/java/com/github/elenterius/biomancy/util/TemperatureUtil.java new file mode 100644 index 000000000..9291e08c1 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/util/TemperatureUtil.java @@ -0,0 +1,33 @@ +package com.github.elenterius.biomancy.util; + +import com.github.elenterius.biomancy.mixin.BiomeAccessor; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; + +public class TemperatureUtil { + public static final float MIN_TEMP = -0.7f; //frozen peaks biome + public static final float MAX_TEMP = 2; //desert biome + public static final float TEMP_DIFF = MAX_TEMP - MIN_TEMP; + + public static float rescale(float minecraftTemperature) { + return (minecraftTemperature - MIN_TEMP) / TEMP_DIFF; + } + + public static boolean isFreezing(float minecraftTemperature) { + return minecraftTemperature < 0.15; + } + + public static float convertMinecraftTemperatureToCelsius(float minecraftTemperature) { + return 27.8f * minecraftTemperature - 4.17f; + } + + public static float getTemperature(Level level, BlockPos pos) { + Biome biome = level.getBiome(pos).get(); + return getTemperature(biome, pos); + } + + public static float getTemperature(Biome biome, BlockPos pos) { + return ((BiomeAccessor) (Object) biome).biomancy$getTemperature(pos); + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/util/VectorUtil.java b/src/main/java/com/github/elenterius/biomancy/util/VectorUtil.java new file mode 100644 index 000000000..b1581e1af --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/util/VectorUtil.java @@ -0,0 +1,40 @@ +package com.github.elenterius.biomancy.util; + +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public class VectorUtil { + + public static final Vec3i XZ_PLANE = new Vec3i(1, 0, 1); + public static final Vec3i XY_PLANE = new Vec3i(1, 1, 0); + public static final Vec3i YZ_PLANE = new Vec3i(0, 1, 1); + + private VectorUtil() {} + + public static Vec3i abs(Vec3i vec) { + return new Vec3i(Math.abs(vec.getX()), Math.abs(vec.getY()), Math.abs(vec.getZ())); + } + + public static Vec3i axisAlignedPlane3i(Direction direction) { + Vec3i normal = direction.getNormal(); + return new Vec3i(1 - Math.abs(normal.getX()), 1 - Math.abs(normal.getY()), 1 - Math.abs(normal.getZ())); + } + + public static Vec3 axisAlignedPlane3d(Direction direction) { + Vec3i normal = direction.getNormal(); + return new Vec3(1d - Math.abs(normal.getX()), 1d - Math.abs(normal.getY()), 1d - Math.abs(normal.getZ())); + } + + public static double max(double a, double b, double c) { + return Math.max(Math.max(a, b), c); + } + + public static double boxDistanceSqr(AABB aabb, Vec3 pos) { + double dx = max(aabb.minX - pos.x, 0, pos.x - aabb.maxX); + double dy = max(aabb.minY - pos.y, 0, pos.y - aabb.maxY); + double dz = max(aabb.minZ - pos.z, 0, pos.z - aabb.maxZ); + return dx * dx + dy * dy + dz * dz; + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/util/function/FloatOperator.java b/src/main/java/com/github/elenterius/biomancy/util/function/FloatOperator.java new file mode 100644 index 000000000..a588625cd --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/util/function/FloatOperator.java @@ -0,0 +1,18 @@ +package com.github.elenterius.biomancy.util.function; + +@FunctionalInterface +public interface FloatOperator { + + FloatOperator IDENTITY = v -> v; + + float apply(float operand); + + default FloatOperator compose(FloatOperator before) { + return v -> apply(before.apply(v)); + } + + default FloatOperator andThen(FloatOperator after) { + return v -> after.apply(apply(v)); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/util/function/IntOperator.java b/src/main/java/com/github/elenterius/biomancy/util/function/IntOperator.java new file mode 100644 index 000000000..fc745e2d2 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/util/function/IntOperator.java @@ -0,0 +1,18 @@ +package com.github.elenterius.biomancy.util.function; + +@FunctionalInterface +public interface IntOperator { + + IntOperator IDENTITY = v -> v; + + int apply(int operand); + + default IntOperator compose(IntOperator before) { + return v -> apply(before.apply(v)); + } + + default IntOperator andThen(IntOperator after) { + return v -> after.apply(apply(v)); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/util/function/package-info.java b/src/main/java/com/github/elenterius/biomancy/util/function/package-info.java new file mode 100644 index 000000000..2be22b01d --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/util/function/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.util.function; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/util/math/DistanceFunction.java b/src/main/java/com/github/elenterius/biomancy/util/math/DistanceFunction.java deleted file mode 100644 index a0186012c..000000000 --- a/src/main/java/com/github/elenterius/biomancy/util/math/DistanceFunction.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.github.elenterius.biomancy.util.math; - -public interface DistanceFunction { - double SQRT_2 = Math.sqrt(2); - double SQRT_3 = Math.sqrt(3); - - DistanceFunction IDENTITY = new DistanceFunction() { - @Override - public double apply(double x, double z) { - return 1d; - } - - @Override - public double apply(double x, double y, double z) { - return 1d; - } - }; - DistanceFunction LENGTH = new DistanceFunction() { - @Override - public double apply(double x, double z) { - return Math.sqrt(x * x + z * z) / SQRT_2; - } - - @Override - public double apply(double x, double y, double z) { - return Math.sqrt(x * x + y * y + z * z) / SQRT_3; - } - }; - DistanceFunction LENGTH_SQUARED = new DistanceFunction() { - @Override - public double apply(double x, double z) { - return x * x + z * z; - } - - @Override - public double apply(double x, double y, double z) { - return x * x + y * y + z * z; - } - }; - DistanceFunction MANHATTAN = new DistanceFunction() { - @Override - public double apply(double x, double z) { - return x + z; - } - - @Override - public double apply(double x, double y, double z) { - return x + y + z; - } - }; - DistanceFunction QUADRATIC = new DistanceFunction() { - @Override - public double apply(double x, double z) { - return x * x + z * z + x * z; - } - - @Override - public double apply(double x, double y, double z) { - return x * x + y * y + z * z + x * y + x * z + y * z; - } - }; - DistanceFunction SPECIAL = new DistanceFunction() { - @Override - public double apply(double x, double z) { - return Math.pow(Math.E, Math.sqrt(x * x + z * z) / SQRT_2) / Math.E; - } - - @Override - public double apply(double x, double y, double z) { - return 1; - } - }; - - double apply(double xDistance, double zDistance); - - double apply(double xDistance, double yDistance, double zDistance); -} diff --git a/src/main/java/com/github/elenterius/biomancy/util/random/CellularNoise.java b/src/main/java/com/github/elenterius/biomancy/util/random/CellularNoise.java index fc0526bf2..b73d836df 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/random/CellularNoise.java +++ b/src/main/java/com/github/elenterius/biomancy/util/random/CellularNoise.java @@ -1,3 +1,7 @@ package com.github.elenterius.biomancy.util.random; -public interface CellularNoise extends Noise {} +public interface CellularNoise extends Noise { + float borderThreshold(); + + float coreThreshold(); +} diff --git a/src/main/java/com/github/elenterius/biomancy/util/random/CellularNoiseWithDomainWarp.java b/src/main/java/com/github/elenterius/biomancy/util/random/CellularNoiseWithDomainWarp.java index d795c97e9..522ae3d7b 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/random/CellularNoiseWithDomainWarp.java +++ b/src/main/java/com/github/elenterius/biomancy/util/random/CellularNoiseWithDomainWarp.java @@ -1,6 +1,6 @@ package com.github.elenterius.biomancy.util.random; -public record CellularNoiseWithDomainWarp(FastNoiseLite cellularNoise, FastNoiseLite domainWarp) implements CellularNoise { +public record CellularNoiseWithDomainWarp(FastNoiseLite cellularNoise, FastNoiseLite domainWarp, float borderThreshold, float coreThreshold) implements CellularNoise { @Override public float getValue(float x, float y) { FastNoiseLite.Vector2 vec = new FastNoiseLite.Vector2(x, y); diff --git a/src/main/java/com/github/elenterius/biomancy/util/random/Noise.java b/src/main/java/com/github/elenterius/biomancy/util/random/Noise.java index f0293fae9..e9fd78b49 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/random/Noise.java +++ b/src/main/java/com/github/elenterius/biomancy/util/random/Noise.java @@ -1,7 +1,18 @@ package com.github.elenterius.biomancy.util.random; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; + public interface Noise { float getValue(float x, float y); float getValue(float x, float y, float z); + + default float getValue(Vec3i pos) { + return getValue(pos.getX(), pos.getY(), pos.getZ()); + } + + default float getValueAtCenter(BlockPos pos) { + return getValue(pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f); + } } diff --git a/src/main/java/com/github/elenterius/biomancy/util/IntegerSerializable.java b/src/main/java/com/github/elenterius/biomancy/util/serialization/IntegerSerializable.java similarity index 67% rename from src/main/java/com/github/elenterius/biomancy/util/IntegerSerializable.java rename to src/main/java/com/github/elenterius/biomancy/util/serialization/IntegerSerializable.java index c5f56e2a5..fc1caafce 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/IntegerSerializable.java +++ b/src/main/java/com/github/elenterius/biomancy/util/serialization/IntegerSerializable.java @@ -1,4 +1,4 @@ -package com.github.elenterius.biomancy.util; +package com.github.elenterius.biomancy.util.serialization; public interface IntegerSerializable { int serializeToInteger(); diff --git a/src/main/java/com/github/elenterius/biomancy/util/serialization/NBTSerializable.java b/src/main/java/com/github/elenterius/biomancy/util/serialization/NBTSerializable.java new file mode 100644 index 000000000..e9cc82be3 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/util/serialization/NBTSerializable.java @@ -0,0 +1,5 @@ +package com.github.elenterius.biomancy.util.serialization; + +public interface NBTSerializable { + NBTSerializer getNBTSerializer(); +} diff --git a/src/main/java/com/github/elenterius/biomancy/util/serialization/NBTSerializer.java b/src/main/java/com/github/elenterius/biomancy/util/serialization/NBTSerializer.java new file mode 100644 index 000000000..9b15a6ab5 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/util/serialization/NBTSerializer.java @@ -0,0 +1,12 @@ +package com.github.elenterius.biomancy.util.serialization; + +import net.minecraft.nbt.CompoundTag; + +public interface NBTSerializer { + String id(); + + CompoundTag write(T t); + + T read(CompoundTag tag); + +} diff --git a/src/main/java/com/github/elenterius/biomancy/util/serialization/package-info.java b/src/main/java/com/github/elenterius/biomancy/util/serialization/package-info.java new file mode 100644 index 000000000..8144fc5c3 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/util/serialization/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.util.serialization; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/world/ChunkPosConsumer.java b/src/main/java/com/github/elenterius/biomancy/world/ChunkPosConsumer.java new file mode 100644 index 000000000..aa04e1e76 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/ChunkPosConsumer.java @@ -0,0 +1,6 @@ +package com.github.elenterius.biomancy.world; + +@FunctionalInterface +public interface ChunkPosConsumer { + void accept(int chunkX, int chunkZ); +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/MobSpawnFilter.java b/src/main/java/com/github/elenterius/biomancy/world/MobSpawnFilter.java new file mode 100644 index 000000000..b53317690 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/MobSpawnFilter.java @@ -0,0 +1,27 @@ +package com.github.elenterius.biomancy.world; + +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.level.LevelAccessor; + +import java.util.Set; + +public interface MobSpawnFilter { + + Set NATURAL_SPAWN_REASON = Set.of( + MobSpawnType.EVENT, + MobSpawnType.NATURAL, + MobSpawnType.CHUNK_GENERATION, + MobSpawnType.PATROL + ); + + static boolean isNaturalSpawn(MobSpawnType spawnReason) { + return NATURAL_SPAWN_REASON.contains(spawnReason); + } + + /** + * At the moment only checks natural mob spawns + */ + boolean isMobAllowedToSpawn(Mob mob, MobSpawnType spawnReason, LevelAccessor level, double x, double y, double z); + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/MobSpawnFilterShape.java b/src/main/java/com/github/elenterius/biomancy/world/MobSpawnFilterShape.java new file mode 100644 index 000000000..db0a33aab --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/MobSpawnFilterShape.java @@ -0,0 +1,78 @@ +package com.github.elenterius.biomancy.world; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import com.github.elenterius.biomancy.world.spatial.type.ShapeSerializers; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public class MobSpawnFilterShape implements Shape, MobSpawnFilter { + + private final Shape shape; + + public MobSpawnFilterShape(Shape shape) { + this.shape = shape; + } + + @Override + public boolean isMobAllowedToSpawn(Mob mob, MobSpawnType spawnReason, LevelAccessor level, double x, double y, double z) { + return false; + } + + @Override + public boolean contains(double x, double y, double z) { + return shape.contains(x, y, z); + } + + @Override + public boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + return shape.intersectsCuboid(minX, minY, minZ, maxX, maxY, maxZ); + } + + @Override + public Vec3 center() { + return shape.center(); + } + + @Override + public double distanceToSqr(double x, double y, double z) { + return shape.distanceToSqr(x, y, z); + } + + @Override + public AABB getAABB() { + return shape.getAABB(); + } + + @Override + public NBTSerializer getNBTSerializer() { + return ShapeSerializers.MOB_SPAWN_FILTER_SERIALIZER; + } + + public record Serializer(String id) implements NBTSerializer { + @Override + public CompoundTag write(MobSpawnFilterShape shape) { + CompoundTag tag = new CompoundTag(); + tag.put("Shape", shape.shape.getNBTSerializer().write(shape.shape)); + return tag; + } + + @Override + public MobSpawnFilterShape read(CompoundTag tag) { + Shape shape = EMPTY; + + CompoundTag shapeCompound = tag.getCompound("Shape"); + String serializerId = shapeCompound.getString("Serializer"); + NBTSerializer serializer = ShapeSerializers.get(serializerId); + if (serializer != null) { + shape = serializer.read(shapeCompound); + } + + return new MobSpawnFilterShape(shape); + } + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/PrimordialEcosystem.java b/src/main/java/com/github/elenterius/biomancy/world/PrimordialEcosystem.java index e67c63122..ee392691c 100644 --- a/src/main/java/com/github/elenterius/biomancy/world/PrimordialEcosystem.java +++ b/src/main/java/com/github/elenterius/biomancy/world/PrimordialEcosystem.java @@ -5,14 +5,17 @@ import com.github.elenterius.biomancy.block.veins.FleshVeinsBlock; import com.github.elenterius.biomancy.entity.PrimordialFleshkin; import com.github.elenterius.biomancy.entity.fleshblob.FleshBlob; +import com.github.elenterius.biomancy.init.ModBlockProperties; import com.github.elenterius.biomancy.init.ModBlocks; import com.github.elenterius.biomancy.init.ModSoundEvents; import com.github.elenterius.biomancy.init.tags.ModBlockTags; +import com.github.elenterius.biomancy.util.LevelUtil; import com.github.elenterius.biomancy.util.random.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.BlockTags; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; @@ -22,12 +25,16 @@ import net.minecraft.world.level.block.state.BlockState; import java.util.Optional; +import java.util.Set; import java.util.function.IntSupplier; import java.util.function.Supplier; public final class PrimordialEcosystem { + private static final RandomSource random = RandomSource.create(); public static final IntSupplier MAX_CHARGE_SUPPLIER = () -> 15; + public static final Set VALID_UPGRADE_TARGETS = Set.of(ModBlocks.MALIGNANT_FLESH_SLAB.get(), ModBlocks.MALIGNANT_FLESH_STAIRS.get()); + public static final Set FULL_FLESH_BLOCKS = Set.of(ModBlocks.MALIGNANT_FLESH.get(), ModBlocks.PRIMAL_FLESH.get(), ModBlocks.PRIMAL_PERMEABLE_MEMBRANE.get()); private PrimordialEcosystem() {} @@ -75,6 +82,58 @@ else if (currentState.canBeReplaced(new DirectionalPlaceContext(level, pos, Dire return false; } + public static boolean placeMalignantBloomOrBlocks(ServerLevel level, BlockPos pos, Direction direction) { + + BlockState state = level.getBlockState(pos); + if (VALID_UPGRADE_TARGETS.contains(state.getBlock())) { + level.setBlock(pos, ModBlocks.MALIGNANT_FLESH.get().defaultBlockState(), Block.UPDATE_CLIENTS); + level.playSound(null, pos, ModSoundEvents.FLESH_BLOCK_PLACE.get(), SoundSource.BLOCKS, 1f, 0.15f + level.random.nextFloat() * 0.5f); + return true; + } + + BlockPos relativePos = pos.relative(direction); + BlockState relativeState = level.getBlockState(relativePos); + RandomSource random = level.getRandom(); + + if (random.nextFloat() < 0.7f && ModBlocks.MALIGNANT_BLOOM.get().mayPlaceOn(level, pos, state)) { + boolean canBeReplaced = relativeState.canBeReplaced(new DirectionalPlaceContext(level, relativePos, direction.getOpposite(), ItemStack.EMPTY, direction)); + boolean noBloomNearby = !LevelUtil.isBlockNearby(level, pos, 4, blockState -> blockState.is(ModBlocks.MALIGNANT_BLOOM.get())); + if (canBeReplaced && noBloomNearby) { + BlockState blockState = ModBlocks.MALIGNANT_BLOOM.get().getStateForPlacement(level, relativePos, direction); + level.playSound(null, relativePos, ModSoundEvents.FLESH_BLOCK_PLACE.get(), SoundSource.BLOCKS, 1f, 0.5f + random.nextFloat() * 0.5f); + level.setBlock(relativePos, blockState, Block.UPDATE_CLIENTS); + return true; + } + } + + if (relativeState.is(ModBlocks.MALIGNANT_FLESH_VEINS.get())) { + if (PrimordialEcosystem.tryToReplaceBlock(level, relativePos, ModBlocks.PRIMAL_FLESH.get().defaultBlockState())) { + level.playSound(null, relativePos, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundSource.BLOCKS, 1f, 0.15f + random.nextFloat() * 0.5f); + return true; + } + else if (FleshVeinsBlock.convert(relativeState, level, relativePos, 0, null, 0.5f, null)) { + level.playSound(null, relativePos, ModSoundEvents.FLESH_BLOCK_PLACE.get(), SoundSource.BLOCKS, 1f, 0.15f + random.nextFloat() * 0.5f); + return true; + } + + return PrimordialEcosystem.spreadMalignantVeinsFromSource(level, relativePos, PrimordialEcosystem.MAX_CHARGE_SUPPLIER); + } + else if (relativeState.canBeReplaced(new DirectionalPlaceContext(level, relativePos, direction.getOpposite(), ItemStack.EMPTY, direction))) { + FleshVeinsBlock veinsBlock = ModBlocks.MALIGNANT_FLESH_VEINS.get(); + BlockState stateForPlacement = veinsBlock.getStateForPlacement(relativeState, level, relativePos, direction.getOpposite(), 15); + if (stateForPlacement != null) { + level.setBlock(relativePos, stateForPlacement, Block.UPDATE_CLIENTS); + for (int i = 0; i < 4; i++) { + if (random.nextFloat() < 0.4f) veinsBlock.getSpreader().spreadFromRandomFaceTowardRandomDirection(stateForPlacement, level, relativePos, random); + } + level.playSound(null, relativePos, ModSoundEvents.FLESH_BLOCK_STEP.get(), SoundSource.BLOCKS, 0.7f, 0.15f + random.nextFloat() * 0.5f); + return true; + } + } + + return false; + } + public static boolean spreadMalignantVeinsFromSource(ServerLevel level, BlockPos pos, IntSupplier chargeSupplier) { FleshVeinsBlock veinsBlock = ModBlocks.MALIGNANT_FLESH_VEINS.get(); BlockState state = level.getBlockState(pos); @@ -111,6 +170,38 @@ public static int increaseMalignantVeinsChargeAroundPos(ServerLevel level, Block return usedCharge; } + public static int countMalignantChargeAroundPos(ServerLevel level, BlockPos pos) { + int totalCharge = 0; + + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + for (int z = -1; z <= 1; z++) { + if (x == 0 && y == 0 && z == 0) continue; + BlockPos neighborPos = pos.offset(x, y, z); + BlockState neighborState = level.getBlockState(neighborPos); + totalCharge += neighborState.getOptionalValue(ModBlockProperties.CHARGE.get()).orElse(0); + } + } + } + + return totalCharge; + } + + public static int countMalignantVeinsAroundPos(ServerLevel level, BlockPos pos) { + int veins = 0; + + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + for (int z = -1; z <= 1; z++) { + if (x == 0 && y == 0 && z == 0) continue; + if (level.getBlockState(pos.offset(x, y, z)).is(ModBlocks.MALIGNANT_FLESH_VEINS.get())) veins += 1; + } + } + } + + return veins; + } + private static double step(double value, double threshold) { return value >= threshold ? 1 : 0; } @@ -140,7 +231,7 @@ public static void generateHollowVoronoiSphere(ServerLevel level, BlockPos pos, private static void trySetBlockInCellularLevel(ServerLevel level, BlockPos pos, BlockState placementState, int x, int y, int z) { Noise noise = getCellularNoise(level); float borderThreshold = 0.15f; - float n = noise.getValue(x, y, z); + float n = noise.getValue(x + 0.5f, y + 0.5f, z + 0.5f); if (n >= borderThreshold) { tryToReplaceBlock(level, pos.offset(x, y, z), placementState); } @@ -148,7 +239,7 @@ private static void trySetBlockInCellularLevel(ServerLevel level, BlockPos pos, private static void trySetBlock(ServerLevel level, BlockPos pos, BlockState placementState, CellularNoise noise, int x, int y, int z) { float borderThreshold = 0.12f; - float n = noise.getValue(x, y, z); + float n = noise.getValue(x + 0.5f, y + 0.5f, z + 0.5f); if (n >= borderThreshold) { tryToReplaceBlock(level, pos.offset(x, y, z), placementState); } @@ -156,7 +247,7 @@ private static void trySetBlock(ServerLevel level, BlockPos pos, BlockState plac public static boolean tryToReplaceBlock(ServerLevel level, BlockPos pos, BlockState replacementState) { BlockState state = level.getBlockState(pos); - if (state.getMaterial().isReplaceable() || state.is(ModBlockTags.PRIMORDIAL_ECO_SYSTEM_REPLACEABLE)) { + if (isReplaceable(state)) { level.setBlock(pos, replacementState, Block.UPDATE_CLIENTS); return true; } @@ -164,7 +255,7 @@ public static boolean tryToReplaceBlock(ServerLevel level, BlockPos pos, BlockSt } public static boolean tryToReplaceBlock(ServerLevel level, BlockPos pos, BlockState state, BlockState replacementState) { - if (state.getMaterial().isReplaceable() || state.is(ModBlockTags.PRIMORDIAL_ECO_SYSTEM_REPLACEABLE)) { + if (isReplaceable(state)) { level.setBlock(pos, replacementState, Block.UPDATE_CLIENTS); return true; } @@ -172,13 +263,26 @@ public static boolean tryToReplaceBlock(ServerLevel level, BlockPos pos, BlockSt } public static boolean tryToReplaceBlock(ServerLevel level, BlockPos pos, BlockState state, Supplier replacementStateSupplier) { - if (state.getMaterial().isReplaceable() || state.is(ModBlockTags.PRIMORDIAL_ECO_SYSTEM_REPLACEABLE)) { + if (isReplaceable(state)) { level.setBlock(pos, replacementStateSupplier.get(), Block.UPDATE_CLIENTS); return true; } return false; } + public static boolean isReplaceable(BlockState state) { + return state.getMaterial().isReplaceable() || state.is(ModBlockTags.FLESH_REPLACEABLE); + } + + public static boolean isReplaceableLog(BlockState state) { + return isReplaceable(state) && state.is(BlockTags.OVERWORLD_NATURAL_LOGS); + } + + public static RandomSource getRandomWithSeed(BlockPos pos) { + random.setSeed(Mth.getSeed(pos)); + return random; + } + public static CellularNoise getCellularNoise(ServerLevel level) { return ((CellularNoiseProvider) level).biomancy$getCellularNoise(); } @@ -195,6 +299,7 @@ public static CellularNoise createPreconfiguredCellularNoise(int seed) { domainWarp.SetDomainWarpAmp(50f); domainWarp.SetFrequency(0.005f); - return new CellularNoiseWithDomainWarp(cellularNoise, domainWarp); + return new CellularNoiseWithDomainWarp(cellularNoise, domainWarp, 0.16f, 0.13f); } + } diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/Chamber.java b/src/main/java/com/github/elenterius/biomancy/world/mound/Chamber.java new file mode 100644 index 000000000..eb6e8e28d --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/Chamber.java @@ -0,0 +1,26 @@ +package com.github.elenterius.biomancy.world.mound; + +import com.github.elenterius.biomancy.world.mound.decorator.ChamberDecorator; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; + +public interface Chamber extends Shape { + + int seed(); + + ChamberDecorator getDecorator(); + + Vec3 origin(); + + // default boolean containsValidBlock(Level level, BlockPos pos, BlockState blockState) { + // if (!contains(pos)) return false; + // ChamberDecorator.Result result = getDecorator().isBlockPartOfDecoration(this, level, pos, blockState); + // return result == ChamberDecorator.Result.POSITION_IS_VALID_AND_MATERIAL_IS_INVALID; + // } + + default boolean contains(BlockPos pos) { + return contains(pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/ChamberFactory.java b/src/main/java/com/github/elenterius/biomancy/world/mound/ChamberFactory.java new file mode 100644 index 000000000..6ecf511ac --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/ChamberFactory.java @@ -0,0 +1,103 @@ +package com.github.elenterius.biomancy.world.mound; + +import com.github.elenterius.biomancy.world.mound.decorator.ChamberDecorators; +import com.github.elenterius.biomancy.world.spatial.geometry.OctantEllipsoidShape; +import com.github.elenterius.biomancy.world.spatial.geometry.SphereShape; +import net.minecraft.util.RandomSource; +import net.minecraft.util.random.SimpleWeightedRandomList; + +import java.util.function.Consumer; + +public interface ChamberFactory { + void create(double x, double y, double z, float chamberRadius, RandomSource random, Consumer consumer); + + ChamberFactory ONE_SPHERE = (double x, double y, double z, float chamberRadius, RandomSource random, Consumer consumer) -> consumer.accept(new MoundChamber(new SphereShape(x, y, z, chamberRadius))); + + ChamberFactory EIGHT_SMALL_ELLIPSOIDS = (double x, double y, double z, float chamberRadius, RandomSource random, Consumer consumer) -> { + float halfR = chamberRadius / 2; + float quarterR = halfR / 2; + float p = chamberRadius / 3.8f; // radius / 4.25f + + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y + p, z + p, halfR, halfR, halfR, quarterR, quarterR, quarterR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y + p, z + p, quarterR, halfR, halfR, halfR, quarterR, quarterR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y + p, z - p, halfR, halfR, quarterR, quarterR, quarterR, halfR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y + p, z - p, quarterR, halfR, quarterR, halfR, quarterR, halfR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y - p, z + p, halfR, quarterR, halfR, quarterR, halfR, quarterR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y - p, z + p, quarterR, quarterR, halfR, halfR, halfR, quarterR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y - p, z - p, halfR, quarterR, quarterR, quarterR, halfR, halfR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y - p, z - p, quarterR, quarterR, quarterR, halfR, halfR, halfR))); + }; + + ChamberFactory ONE_BIG_FOUR_SMALL_ELLIPSOIDS = (double x, double y, double z, float chamberRadius, RandomSource random, Consumer consumer) -> { + float halfR = chamberRadius / 2; + float quarterR = halfR / 2; + float p = chamberRadius / 3.8f; // radius / 4.25f + + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x, y + p, z, halfR, halfR, halfR, halfR, quarterR, halfR))); + + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y - p, z + p, halfR, quarterR, halfR, quarterR, halfR, quarterR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y - p, z + p, quarterR, quarterR, halfR, halfR, halfR, quarterR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y - p, z - p, halfR, quarterR, quarterR, quarterR, halfR, halfR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y - p, z - p, quarterR, quarterR, quarterR, halfR, halfR, halfR))); + }; + + ChamberFactory FOUR_SMALL_ONE_BIG_ELLIPSOIDS = (double x, double y, double z, float chamberRadius, RandomSource random, Consumer consumer) -> { + float halfR = chamberRadius / 2; + float quarterR = halfR / 2; + float p = chamberRadius / 3.8f; // radius / 4.25f + + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y + p, z + p, halfR, halfR, halfR, quarterR, quarterR, quarterR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y + p, z + p, quarterR, halfR, halfR, halfR, quarterR, quarterR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y + p, z - p, halfR, halfR, quarterR, quarterR, quarterR, halfR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y + p, z - p, quarterR, halfR, quarterR, halfR, quarterR, halfR))); + + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x, y - p, z, halfR, quarterR, halfR, halfR, halfR, halfR))); + }; + + ChamberFactory TWO_BIG_ELLIPSOIDS = (double x, double y, double z, float chamberRadius, RandomSource random, Consumer consumer) -> { + float halfR = chamberRadius / 2; + float quarterR = halfR / 2; + float p = chamberRadius / 3.8f; // radius / 4.25f + + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x, y + p, z, halfR, halfR, halfR, halfR, quarterR, halfR))); + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x, y - p, z, halfR, quarterR, halfR, halfR, halfR, halfR))); + }; + + ChamberFactory SPECIAL_CRADLE = (double x, double y, double z, float chamberRadius, RandomSource random, Consumer consumer) -> { + float halfR = chamberRadius / 2; + float quarterR = halfR / 2; + float p = chamberRadius / 3.8f; // radius / 4.25f + + consumer.accept(new MoundChamber(new OctantEllipsoidShape(x, y + p - 1, z, halfR, halfR, halfR, halfR, quarterR, halfR))); + + Consumer wrappedConsumer = chamber -> { + chamber.setDecorator(ChamberDecorators.PRIMAL_ORIFICE_COMBS, random.nextInt()); + consumer.accept(chamber); + }; + + wrappedConsumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y - p, z + p, halfR, quarterR, halfR, quarterR, halfR, quarterR))); + wrappedConsumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y - p, z + p, quarterR, quarterR, halfR, halfR, halfR, quarterR))); + wrappedConsumer.accept(new MoundChamber(new OctantEllipsoidShape(x + p, y - p, z - p, halfR, quarterR, quarterR, quarterR, halfR, halfR))); + wrappedConsumer.accept(new MoundChamber(new OctantEllipsoidShape(x - p, y - p, z - p, quarterR, quarterR, quarterR, halfR, halfR, halfR))); + }; + + SimpleWeightedRandomList RANDOM_DEFAULTS_LIST = SimpleWeightedRandomList.builder() + .add(ONE_SPHERE, 5) + .add(ONE_BIG_FOUR_SMALL_ELLIPSOIDS, 20) + .add(FOUR_SMALL_ONE_BIG_ELLIPSOIDS, 20) + .add(TWO_BIG_ELLIPSOIDS, 10) + .add(EIGHT_SMALL_ELLIPSOIDS, 60) + .build(); + + ChamberFactory RANDOM_DEFAULT = (double x, double y, double z, float chamberRadius, RandomSource random, Consumer consumer) -> { + ChamberFactory factory = RANDOM_DEFAULTS_LIST.getRandomValue(random).orElse(ChamberFactory.EIGHT_SMALL_ELLIPSOIDS); + + Consumer wrappedConsumer = chamber -> { + chamber.setDecorator(ChamberDecorators.getRandomDefault(random), random.nextInt()); + consumer.accept(chamber); + }; + + factory.create(x, y, z, chamberRadius, random, wrappedConsumer); + }; + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/ChamberFactoryType.java b/src/main/java/com/github/elenterius/biomancy/world/mound/ChamberFactoryType.java new file mode 100644 index 000000000..fb5ee61ff --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/ChamberFactoryType.java @@ -0,0 +1,5 @@ +package com.github.elenterius.biomancy.world.mound; + +enum ChamberFactoryType { + DEFAULT, CRADLE, END_CAP_MAIN_SPIRE, END_CAP_SUB_SPIRE +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/MoundChamber.java b/src/main/java/com/github/elenterius/biomancy/world/mound/MoundChamber.java new file mode 100644 index 000000000..4cbb9ba04 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/MoundChamber.java @@ -0,0 +1,75 @@ +package com.github.elenterius.biomancy.world.mound; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import com.github.elenterius.biomancy.world.mound.decorator.ChamberDecorator; +import com.github.elenterius.biomancy.world.mound.decorator.ChamberDecorators; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public class MoundChamber implements Chamber { + private final Shape shape; + private final Vec3 origin; + private int seed = 1337; + private ChamberDecorator chamberDecorator = ChamberDecorators.EMPTY; + + public MoundChamber(Vec3 origin, Shape shape) { + this.shape = shape; + this.origin = origin; + } + + public MoundChamber(Shape shape) { + this.shape = shape; + origin = shape.center(); + } + + @Override + public boolean contains(double x, double y, double z) { + return shape.contains(x, y, z); + } + + @Override + public boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + return shape.intersectsCuboid(minX, minY, minZ, maxX, maxY, maxZ); + } + + @Override + public Vec3 center() { + return shape.center(); + } + + @Override + public int seed() { + return seed; + } + + public void setDecorator(ChamberDecorator chamberDecorator, int seed) { + this.chamberDecorator = chamberDecorator; + this.seed = seed; + } + + @Override + public ChamberDecorator getDecorator() { + return chamberDecorator; + } + + @Override + public Vec3 origin() { + return origin; + } + + @Override + public double distanceToSqr(double x, double y, double z) { + return shape.distanceToSqr(x, y, z); + } + + @Override + public AABB getAABB() { + return shape.getAABB(); + } + + @Override + public NBTSerializer getNBTSerializer() { + return null; + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/MoundGenerator.java b/src/main/java/com/github/elenterius/biomancy/world/mound/MoundGenerator.java new file mode 100644 index 000000000..30a30c729 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/MoundGenerator.java @@ -0,0 +1,219 @@ +package com.github.elenterius.biomancy.world.mound; + +import com.github.elenterius.biomancy.util.TemperatureUtil; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import com.github.elenterius.biomancy.world.spatial.geometry.SphereShape; +import com.mojang.math.Vector3d; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.phys.Vec3; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public final class MoundGenerator { + private MoundGenerator() {} + + public static MoundShape constructShape(BlockPos blockOrigin, MoundShape.ProcGenValues procGenValues) { + return genShape(blockOrigin, procGenValues); + } + + public static MoundShape constructShape(Level level, BlockPos blockOrigin, long seed) { + Biome biome = level.getBiome(blockOrigin).get(); + MoundShape.ProcGenValues procGenValues = new MoundShape.ProcGenValues( + seed, + (byte) -21, + (byte) 5, + (byte) 2, + level.getMaxBuildHeight(), + level.getSeaLevel(), + TemperatureUtil.getTemperature(biome, blockOrigin), + biome.getDownfall() + ); + + return genShape(blockOrigin, procGenValues); + } + + private static MoundShape genShape(BlockPos blockOrigin, MoundShape.ProcGenValues procGenValues) { + Context ctx = new Context(); + ctx.random = RandomSource.create(procGenValues.seed()); + Vec3 origin = ctx.origin = Vec3.atCenterOf(blockOrigin); + + float radius = 8f * (1 + Mth.clamp(procGenValues.radiusMultiplier(), -0.5f, 1.5f)); + + float biomeTemperature = procGenValues.biomeTemperature(); + float biomeHumidity = procGenValues.biomeHumidity(); + + float heatMultiplier = TemperatureUtil.rescale(biomeTemperature) * 0.5f + biomeTemperature / TemperatureUtil.MAX_TEMP * 0.5f; + float coldMultiplier = TemperatureUtil.isFreezing(biomeTemperature) ? 0.1f : 1; + float erosionMultiplier = 0.1f + biomeHumidity * coldMultiplier; + float erosionMultiplierInv = 1 - erosionMultiplier; + + ctx.spikiness = Mth.clamp(procGenValues.heightMultiplier() + heatMultiplier, 0, 1); + ctx.slantMultiplier = 0.1f + ctx.random.nextFloat() + heatMultiplier * 2f; + ctx.relativeWallThickness = Mth.clamp((1 - heatMultiplier) * 8, 2.25f, 8); + + ctx.minMoundRadius = 3 + erosionMultiplier * 3f; + ctx.baseMoundRadius = radius + (radius / 2) * erosionMultiplierInv; + ctx.maxMoundRadius = ctx.minMoundRadius + ctx.baseMoundRadius; + + float subSpireRadius = ctx.maxMoundRadius / 2; + float subSpires = Mth.clamp(procGenValues.subSpires(), 0, countCirclesOnCircumference(ctx.maxMoundRadius, subSpireRadius)); + + int maxBuildHeight = procGenValues.maxBuildHeight(); + int seaLevel = procGenValues.seaLevel(); + float maxMoundHeight = Mth.clamp(maxBuildHeight * ctx.spikiness, 0, (maxBuildHeight - seaLevel)); + + ctx.dirLean = new Vec3(ctx.random.nextFloat() - ctx.random.nextFloat(), 0, ctx.random.nextFloat() - ctx.random.nextFloat()).normalize(); + ctx.maxLean = ctx.dirLean.scale(2); + genSpire(origin.x, origin.y, origin.z, maxMoundHeight, ctx.baseMoundRadius, ctx, true); + + float subRadius = (ctx.baseMoundRadius + ctx.relativeWallThickness) * Mth.sin(Mth.PI / subSpires); + float r = Mth.lerp(ctx.spikiness, subSpireRadius, ctx.baseMoundRadius) + ctx.relativeWallThickness; + float startAngle = ctx.random.nextFloat() * (Mth.PI * 2); + float angle = (Mth.PI * 2) / subSpires; + + for (int n = 0; n < subSpires; n++) { + float arc = startAngle + angle * n; + double xn = origin.x + Mth.sin(arc) * r; + double zn = origin.z + Mth.cos(arc) * r; + genSpire(xn, origin.y, zn, maxMoundHeight / (1.5f + ctx.random.nextFloat() * 1.5f), subRadius, ctx); + } + + return new MoundShape(blockOrigin, ctx.boundingShapes, ctx.chambers, procGenValues); + } + + private static void genSpire(double x, double y, double z, float maxHeight, float baseRadius, Context context) { + genSpire(x, y, z, maxHeight, baseRadius, context, false); + } + + private static void genSpire(double x, double y, double z, float maxHeight, float baseRadius, Context context, boolean isMainSpire) { + + Vector3d prevLean = new Vector3d(0, 0, 0); + Vector3d leanOffset = new Vector3d(0, 0, 0); + float prevRadius = baseRadius + context.relativeWallThickness; + float totalHeight = 0; + + genSphereWithChambers(x, y, z, prevRadius, prevRadius - context.relativeWallThickness / 2f, context, isMainSpire ? ChamberFactoryType.CRADLE : ChamberFactoryType.DEFAULT); + + while (totalHeight < maxHeight) { + float t = totalHeight / maxHeight; + + float coldRadius = Mth.lerp(easeInQuad(t), baseRadius, context.minMoundRadius); + float warmRadius = Mth.lerp(easeOutQuad(t), baseRadius, context.minMoundRadius); + float radius = Mth.clamp(Mth.lerp(context.spikiness, coldRadius, warmRadius), context.minMoundRadius, context.maxMoundRadius) + context.relativeWallThickness; + float height = totalHeight + radius / 2 + Mth.lerp(t, 0, radius / 2.5f); + + if (height >= maxHeight) break; + + leanOffset.set(context.dirLean.x, context.dirLean.y, context.dirLean.z); + leanOffset.scale((context.random.nextFloat() - 1) * context.slantMultiplier); + double leanX = prevLean.x + leanOffset.x; + if (Math.abs(leanX) >= context.maxLean.x) { + leanX = prevLean.x - leanOffset.z; + } + double leanZ = prevLean.z + leanOffset.z; + if (Math.abs(leanZ) >= context.maxLean.z) { + leanZ = prevLean.z - leanOffset.z; + } + + genSphereWithChambers(x + leanX, y + height, z + leanZ, radius, radius - context.relativeWallThickness / 2f, context, ChamberFactoryType.DEFAULT); + + prevLean.set(leanX, 0, leanZ); + prevRadius = radius; + totalHeight = height; + } + + //end cap shape + ChamberFactoryType chamberType = isMainSpire ? ChamberFactoryType.END_CAP_MAIN_SPIRE : ChamberFactoryType.END_CAP_SUB_SPIRE; + genSphereWithChambers(x + prevLean.x, y + totalHeight + (prevRadius / 2) * 1.5f, z + prevLean.z, prevRadius / 2f, prevRadius / 2f, context, chamberType); + } + + private static void genSphereWithChambers(double x, double y, double z, float radius, float chamberRadius, Context context, ChamberFactoryType type) { + Vec3 pos = new Vec3(x, y, z); + context.boundingShapes.add(new SphereShape(pos, radius)); + + if (type == ChamberFactoryType.CRADLE) { + context.mainChamberRadius = chamberRadius * 0.9f; + + Consumer consumer = chamber -> { + context.cradleChambers.add(chamber); + context.chambers.add(chamber); + }; + + ChamberFactory.SPECIAL_CRADLE.create(x, y, z, chamberRadius, context.random, consumer); + return; + } + + Consumer filteredConsumer = chamber -> { + // Vec3 closestPoint = GeometryUtil.closestPointOnAABB(chamber.getAABB(), context.origin); + // double distSqr = closestPoint.distanceToSqr(context.origin); + // boolean noIntersectionWithCradleChambers = distSqr > context.mainChamberRadius * context.mainChamberRadius; + + boolean intersectingWithCradleChamber = false; + for (MoundChamber cradleChamber : context.cradleChambers) { + if (cradleChamber.intersectsCuboid(chamber.getAABB())) { + intersectingWithCradleChamber = true; + break; + } + } + + if (!intersectingWithCradleChamber) { + context.chambers.add(chamber); + } + }; + + if (chamberRadius < 8) { + filteredConsumer.accept(new MoundChamber(new SphereShape(pos, chamberRadius))); + return; + } + + switch (type) { + case DEFAULT -> ChamberFactory.RANDOM_DEFAULT.create(x, y, z, chamberRadius, context.random, filteredConsumer); + case END_CAP_MAIN_SPIRE -> { + ChamberFactory generator = context.random.nextFloat() < 0.8f ? ChamberFactory.ONE_SPHERE : ChamberFactory.ONE_BIG_FOUR_SMALL_ELLIPSOIDS; + generator.create(x, y, z, chamberRadius, context.random, filteredConsumer); + } + case END_CAP_SUB_SPIRE -> { + ChamberFactory generator = context.random.nextFloat() < 0.8f ? ChamberFactory.ONE_BIG_FOUR_SMALL_ELLIPSOIDS : ChamberFactory.ONE_SPHERE; + generator.create(x, y, z, chamberRadius, context.random, filteredConsumer); + } + default -> { /* do nothing */ } + } + } + + private static float countCirclesOnCircumference(float radius, float subCircleRadius) { + //https://stackoverflow.com/questions/56004326/calculate-the-number-of-circles-that-fit-on-the-circumference-of-another-circle + if (subCircleRadius > radius) return 0; + return Mth.PI / (float) Math.asin(subCircleRadius / radius); + } + + private static float easeInQuad(float x) { + return x * x; + } + + private static float easeOutQuad(float x) { + return 1f - (1f - x) * (1f - x); + } + + private static class Context { + List boundingShapes = new ArrayList<>(); + List chambers = new ArrayList<>(); + List cradleChambers = new ArrayList<>(); + RandomSource random; + Vec3 origin; + float spikiness; + float slantMultiplier; + float minMoundRadius; + float baseMoundRadius; + float maxMoundRadius; + float mainChamberRadius; + float relativeWallThickness; + Vec3 dirLean; + Vec3 maxLean; + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/MoundShape.java b/src/main/java/com/github/elenterius/biomancy/world/mound/MoundShape.java new file mode 100644 index 000000000..d961fdfc8 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/MoundShape.java @@ -0,0 +1,167 @@ +package com.github.elenterius.biomancy.world.mound; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import com.github.elenterius.biomancy.world.MobSpawnFilter; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import com.github.elenterius.biomancy.world.spatial.geometry.ShapeHierarchy; +import com.github.elenterius.biomancy.world.spatial.type.ShapeSerializers; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class MoundShape implements Shape, MobSpawnFilter { + private final ProcGenValues procGenValues; + BlockPos origin; + ShapeHierarchy boundingShapes; + ShapeHierarchy chamberShapes; + + MoundShape(BlockPos origin, List boundingShapes, List chamberShapes, ProcGenValues procGenValues) { + this.boundingShapes = new ShapeHierarchy<>(boundingShapes); + this.chamberShapes = new ShapeHierarchy<>(chamberShapes); + this.origin = origin; + this.procGenValues = procGenValues; + } + + public BlockPos getOrigin() { + return origin; + } + + @Override + public boolean contains(double x, double y, double z) { + return boundingShapes.contains(x, y, z); + } + + @Override + public boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + return boundingShapes.intersectsCuboid(minX, minY, minZ, maxX, maxY, maxZ); + } + + @Override + public Vec3 center() { + return boundingShapes.getCenter(); + } + + @Override + public double distanceToSqr(double x, double y, double z) { + return boundingShapes.distanceToSqr(x, y, z); + } + + @Override + public AABB getAABB() { + return boundingShapes.getAABB(); + } + + public boolean hasChamberAt(BlockPos pos) { + return chamberShapes.contains(pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d); + } + + @Nullable + public MoundChamber getChamberAt(BlockPos pos) { + return getChamberAt(pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d); + } + + @Nullable + public MoundChamber getChamberAt(double x, double y, double z) { + return chamberShapes.getClosestShapeContaining(x, y, z); + } + + @Nullable + public List getChambersAt(BlockPos pos) { + return getChambersAt(pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d); + } + + @Nullable + public List getChambersAt(double x, double y, double z) { + return chamberShapes.getShapesContaining(x, y, z); + } + + @Nullable + public Shape getBoundingShapeAt(BlockPos pos) { + return getBoundingShapeAt(pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d); + } + + @Nullable + public Shape getBoundingShapeAt(double x, double y, double z) { + return boundingShapes.getClosestShapeContaining(x, y, z); + } + + public ProcGenValues getProcGenValues() { + return procGenValues; + } + + @Override + public NBTSerializer getNBTSerializer() { + return ShapeSerializers.MOUND_SERIALIZER; + } + + @Override + public boolean isMobAllowedToSpawn(Mob mob, MobSpawnType spawnReason, LevelAccessor level, double x, double y, double z) { + return false; + } + + public record ProcGenValues(long seed, byte extraRadius, byte extraHeight, byte subSpires, int maxBuildHeight, int seaLevel, float biomeTemperature, float biomeHumidity) { + + public float heightMultiplier() { + return extraHeight / 100f; + } + + public float radiusMultiplier() { + return extraRadius / 100f; + } + + public void writeTo(CompoundTag tag) { + tag.putLong("Seed", seed); + + tag.putByte("Radius", extraRadius); + tag.putByte("Height", extraHeight); + tag.putByte("SubSpires", subSpires); + + tag.putInt("MaxBuildHeight", maxBuildHeight); + tag.putInt("SeaLevel", seaLevel); + tag.putFloat("BiomeTemperature", biomeTemperature); + tag.putFloat("BiomeHumidity", biomeHumidity); + } + + public static ProcGenValues readFrom(CompoundTag tag) { + long seed = tag.getLong("Seed"); + + byte extraRadius = tag.getByte("Radius"); + byte height = tag.getByte("Height"); + byte subSpires = tag.getByte("SubSpires"); + + int maxBuildHeight = tag.getInt("MaxBuildHeight"); + int seaLevel = tag.getInt("SeaLevel"); + float biomeTemperature = tag.getFloat("BiomeTemperature"); + float biomeHumidity = tag.getFloat("BiomeHumidity"); + + return new ProcGenValues(seed, extraRadius, height, subSpires, maxBuildHeight, seaLevel, biomeTemperature, biomeHumidity); + } + } + + public record Serializer(String id) implements NBTSerializer { + + @Override + public CompoundTag write(MoundShape shape) { + CompoundTag tag = new CompoundTag(); + tag.putLong("Origin", shape.origin.asLong()); + shape.procGenValues.writeTo(tag); + return tag; + } + + @Override + public MoundShape read(CompoundTag tag) { + BlockPos origin = BlockPos.of(tag.getLong("Origin")); + ProcGenValues procGenValues = ProcGenValues.readFrom(tag); + return MoundGenerator.constructShape(origin, procGenValues); + } + } +} + + diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberDecorator.java b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberDecorator.java new file mode 100644 index 000000000..9ff018fcb --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberDecorator.java @@ -0,0 +1,53 @@ +package com.github.elenterius.biomancy.world.mound.decorator; + +import com.github.elenterius.biomancy.world.mound.Chamber; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +public interface ChamberDecorator { + PartOfDecorationResult isBlockPartOfDecoration(Chamber chamber, Level level, BlockPos pos, BlockState state); + + boolean canPlace(Chamber chamber, Level level, BlockPos pos, Direction axisDirection); + + boolean place(Chamber chamber, Level level, BlockPos pos, Direction axisDirection); + + public enum PartOfDecorationResult { + POSITION_AND_MATERIAL_ARE_VALID(true, true), + POSITION_AND_MATERIAL_ARE_INVALID(false, false), + POSITION_IS_VALID_AND_MATERIAL_IS_INVALID(true, false), + POSITION_IS_INVALID_AND_MATERIAL_IS_VALID(false, true); + + static final PartOfDecorationResult[] sortedValues; + + static { + sortedValues = new PartOfDecorationResult[values().length]; + for (PartOfDecorationResult result : values()) { + int index = getIndex(result.positionIsValid, result.materialIsValid); + sortedValues[index] = result; + } + } + + public final boolean positionIsValid; + public final boolean materialIsValid; + + PartOfDecorationResult(boolean positionIsValid, boolean materialIsValid) { + this.positionIsValid = positionIsValid; + this.materialIsValid = materialIsValid; + } + + public int index() { + return getIndex(positionIsValid, materialIsValid); + } + + public static PartOfDecorationResult of(boolean positionValid, boolean materialValid) { + int index = getIndex(positionValid, materialValid); + return sortedValues[index]; + } + + private static int getIndex(boolean positionValid, boolean materialValid) { + return (positionValid ? 1 : 0) << 1 | (materialValid ? 1 : 0); + } + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberDecorators.java b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberDecorators.java new file mode 100644 index 000000000..1a9ed1ebc --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberDecorators.java @@ -0,0 +1,56 @@ +package com.github.elenterius.biomancy.world.mound.decorator; + +import com.github.elenterius.biomancy.init.ModBlocks; +import com.github.elenterius.biomancy.world.mound.Chamber; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.util.random.SimpleWeightedRandomList; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +public final class ChamberDecorators { + + public static final ChamberDecorator EMPTY = new ChamberDecorator() { + @Override + public PartOfDecorationResult isBlockPartOfDecoration(Chamber chamber, Level level, BlockPos pos, BlockState state) { + //consider everything as a decoration except full primal and malignant flesh blocks + //this allows all blocks placed inside the room to survive and not be destroyed + boolean materialValid = !state.is(ModBlocks.PRIMAL_FLESH.get()) && !state.is(ModBlocks.MALIGNANT_FLESH.get()); + return PartOfDecorationResult.of(true, materialValid); + } + + @Override + public boolean canPlace(Chamber chamber, Level level, BlockPos pos, Direction axisDirection) { + return false; + } + + @Override + public boolean place(Chamber chamber, Level level, BlockPos pos, Direction axisDirection) { + return false; + } + }; + + public static final ChamberDecorator PRIMAL_FLESH_PILLARS = new PillarsDecorator(ModBlocks.PRIMAL_FLESH.get().defaultBlockState()); + public static final ChamberDecorator MALIGNANT_FLESH_PILLARS = new PillarsDecorator(ModBlocks.MALIGNANT_FLESH.get().defaultBlockState()); + public static final ChamberDecorator PRIMAL_ORIFICE_PILLARS = new PillarsDecorator(ModBlocks.PRIMAL_ORIFICE.get().defaultBlockState()); + public static final ChamberDecorator PRIMAL_ORIFICE_COMBS = new HangingCombsDecorator(ModBlocks.PRIMAL_ORIFICE.get().defaultBlockState()); + public static final ChamberDecorator BONE_PILLARS = new PillarsDecorator(Blocks.BONE_BLOCK.defaultBlockState()); + + public static final SimpleWeightedRandomList RANDOM_DEFAULTS = SimpleWeightedRandomList.builder() + .add(EMPTY, 10) + .add(PRIMAL_FLESH_PILLARS, 25) + .add(MALIGNANT_FLESH_PILLARS, 15) + .add(PRIMAL_ORIFICE_PILLARS, 5) + .add(PRIMAL_ORIFICE_COMBS, 7) + .add(BONE_PILLARS, 17) + .build(); + + private ChamberDecorators() {} + + public static ChamberDecorator getRandomDefault(RandomSource random) { + return RANDOM_DEFAULTS.getRandomValue(random).orElse(PRIMAL_FLESH_PILLARS); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberSpecialDecorator.java b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberSpecialDecorator.java new file mode 100644 index 000000000..038736eab --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/ChamberSpecialDecorator.java @@ -0,0 +1,31 @@ +package com.github.elenterius.biomancy.world.mound.decorator; + +import com.github.elenterius.biomancy.init.ModBlocks; +import com.github.elenterius.biomancy.util.LevelUtil; +import com.github.elenterius.biomancy.world.PrimordialEcosystem; +import com.github.elenterius.biomancy.world.mound.Chamber; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +public interface ChamberSpecialDecorator { + + boolean canDecorate(Chamber chamber, Level level, BlockPos pos, Direction axisDirection, BlockPos closeOffsetPos, BlockState closeOffsetState, BlockPos farOffsetPos, BlockState farOffsetState); + + boolean decorate(Chamber chamber, Level level, BlockPos pos, Direction axisDirection, BlockPos closeOffsetPos, BlockState closeOffsetState, BlockPos farOffsetPos, BlockState farOffsetState); + + ChamberSpecialDecorator BLOOMLIGHT = new ChamberSpecialDecorator() { + @Override + public boolean canDecorate(Chamber chamber, Level level, BlockPos pos, Direction axisDirection, BlockPos closeOffsetPos, BlockState closeOffsetState, BlockPos farOffsetPos, BlockState farOffsetState) { + boolean isFleshBlock = PrimordialEcosystem.FULL_FLESH_BLOCKS.contains(farOffsetState.getBlock()); + return isFleshBlock && axisDirection != Direction.DOWN && LevelUtil.getMaxBrightness(level, pos) < 5; + } + + @Override + public boolean decorate(Chamber chamber, Level level, BlockPos pos, Direction axisDirection, BlockPos closeOffsetPos, BlockState closeOffsetState, BlockPos farOffsetPos, BlockState farOffsetState) { + return level.setBlock(closeOffsetPos, ModBlocks.BLOOMLIGHT.get().defaultBlockState(), Block.UPDATE_CLIENTS); + } + }; +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/HangingCombsDecorator.java b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/HangingCombsDecorator.java new file mode 100644 index 000000000..7eb783a90 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/HangingCombsDecorator.java @@ -0,0 +1,73 @@ +package com.github.elenterius.biomancy.world.mound.decorator; + +import com.github.elenterius.biomancy.util.random.FastNoiseLite; +import com.github.elenterius.biomancy.world.mound.Chamber; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public class HangingCombsDecorator implements ChamberDecorator { + + private final FastNoiseLite simplexNoise; + private final BlockState material; + + public HangingCombsDecorator(BlockState material) { + this.material = material; + this.simplexNoise = initNoise(); + } + + protected FastNoiseLite initNoise() { + FastNoiseLite noise = new FastNoiseLite(); + noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2S); + noise.SetFrequency(0.1f); + return noise; + } + + @Override + public PartOfDecorationResult isBlockPartOfDecoration(Chamber chamber, Level level, BlockPos pos, BlockState state) { + return PartOfDecorationResult.of(isPosInsideAnyComb(chamber, pos), state == material); + } + + @Override + public boolean canPlace(Chamber chamber, Level level, BlockPos pos, Direction axisDirection) { + return isPosInsideAnyComb(chamber, pos); + } + + @Override + public boolean place(Chamber chamber, Level level, BlockPos pos, Direction axisDirection) { + return level.setBlock(pos, material, Block.UPDATE_CLIENTS); + } + + protected float combThreshold(float y) { + return 0.5f + easeInExpo(1 - y) * 10; + } + + protected boolean isPosInsideAnyComb(Chamber chamber, BlockPos pos) { + Vec3 center = chamber.center(); + float x = (float) (center.x - (pos.getX() + 0.5d)); + float z = (float) (center.z - (pos.getZ() + 0.5d)); + + AABB aabb = chamber.getAABB(); + float yNormalized = normalize(pos.getY(), aabb.minY, aabb.maxY); + float threshold = combThreshold(yNormalized); + + simplexNoise.SetSeed(chamber.seed()); + + return simplexNoise.GetNoise(x, 0, z) >= threshold; + } + + private static float normalize(double value, double min, double max) { + float rescaled = (float) ((value - min) / (max - min)); //min-max rescale + return Mth.clamp(rescaled, 0f, 1f); + } + + private static float easeInExpo(float x) { + if (x == 0) return 0; + return (float) Math.pow(2, 10 * x - 10); + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/PillarsDecorator.java b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/PillarsDecorator.java new file mode 100644 index 000000000..43eb48e6d --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/decorator/PillarsDecorator.java @@ -0,0 +1,69 @@ +package com.github.elenterius.biomancy.world.mound.decorator; + +import com.github.elenterius.biomancy.util.random.FastNoiseLite; +import com.github.elenterius.biomancy.world.mound.Chamber; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public class PillarsDecorator implements ChamberDecorator { + + private final FastNoiseLite simplexNoise; + private final BlockState material; + + public PillarsDecorator(BlockState material) { + this.material = material; + this.simplexNoise = initNoise(); + } + + protected FastNoiseLite initNoise() { + FastNoiseLite noise = new FastNoiseLite(); + noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2S); + noise.SetFrequency(0.1f); + return noise; + } + + @Override + public PartOfDecorationResult isBlockPartOfDecoration(Chamber chamber, Level level, BlockPos pos, BlockState state) { + return PartOfDecorationResult.of(isPosInsideAnyPillar(chamber, pos), state == material); + } + + @Override + public boolean canPlace(Chamber chamber, Level level, BlockPos pos, Direction axisDirection) { + return isPosInsideAnyPillar(chamber, pos); + } + + @Override + public boolean place(Chamber chamber, Level level, BlockPos pos, Direction axisDirection) { + return level.setBlock(pos, material, Block.UPDATE_CLIENTS); + } + + protected float pillarThreshold(float y) { + float fx = (((y - 0.5f) * (y - 0.5f)) * 4f) * -1f + 1f; + return 0.42f + fx * 0.2f; + } + + protected boolean isPosInsideAnyPillar(Chamber chamber, BlockPos pos) { + Vec3 center = chamber.center(); + float x = (float) (center.x - (pos.getX() + 0.5d)); + float z = (float) (center.z - (pos.getZ() + 0.5d)); + + AABB aabb = chamber.getAABB(); + float yNormalized = normalize(pos.getY(), aabb.minY, aabb.maxY); + float threshold = pillarThreshold(yNormalized); + + simplexNoise.SetSeed(chamber.seed()); + + return simplexNoise.GetNoise(x, 0, z) >= threshold; + } + + private static float normalize(double value, double min, double max) { + float rescaled = (float) ((value - min) / (max - min)); //min-max rescale + return Mth.clamp(rescaled, 0f, 1f); + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/mound/package-info.java b/src/main/java/com/github/elenterius/biomancy/world/mound/package-info.java new file mode 100644 index 000000000..7ab4ea96b --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/mound/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.world.mound; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/world/section/SABB.java b/src/main/java/com/github/elenterius/biomancy/world/section/SABB.java new file mode 100644 index 000000000..57aa15eb5 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/section/SABB.java @@ -0,0 +1,223 @@ +package com.github.elenterius.biomancy.world.section; + +import com.github.elenterius.biomancy.world.ChunkPosConsumer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Cursor3D; +import net.minecraft.core.SectionPos; +import net.minecraft.world.phys.AABB; + +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * Section Aligned Bounding Box + * + * @param packedPosMin inclusie + * @param packedPosMax inclusive + * @implNote we store max and min SectionPos as packed long to safe 64 bits + */ +public record SABB(long packedPosMin, long packedPosMax) { + + public static SABB from(SectionPos posA, SectionPos posB) { + int minX = Math.min(posA.x(), posB.x()); + int minY = Math.min(posA.y(), posB.y()); + int minZ = Math.min(posA.z(), posB.z()); + int maxX = Math.max(posA.x(), posB.x()); + int maxY = Math.max(posA.y(), posB.y()); + int maxZ = Math.max(posA.z(), posB.z()); + + long min = SectionPos.asLong(minX, minY, minZ); + long max = SectionPos.asLong(maxX, maxY, maxZ); + + return new SABB(min, max); + } + + public static SABB from(BlockPos posA, BlockPos posB) { + int minX = Math.min(posA.getX(), posB.getX()); + int minY = Math.min(posA.getY(), posB.getY()); + int minZ = Math.min(posA.getZ(), posB.getZ()); + int maxX = Math.max(posA.getX(), posB.getX()); + int maxY = Math.max(posA.getY(), posB.getY()); + int maxZ = Math.max(posA.getZ(), posB.getZ()); + + long min = SectionPos.asLong( + SectionPos.blockToSectionCoord(minX), + SectionPos.blockToSectionCoord(minY), + SectionPos.blockToSectionCoord(minZ) + ); + long max = SectionPos.asLong( + SectionPos.blockToSectionCoord(maxX), + SectionPos.blockToSectionCoord(maxY), + SectionPos.blockToSectionCoord(maxZ) + ); + + return new SABB(min, max); + } + + public static SABB from(AABB boundingBox) { + long min = SectionPos.asLong( + SectionPos.blockToSectionCoord(boundingBox.minX), + SectionPos.blockToSectionCoord(boundingBox.minY), + SectionPos.blockToSectionCoord(boundingBox.minZ) + ); + long max = SectionPos.asLong( + SectionPos.blockToSectionCoord(boundingBox.maxX), + SectionPos.blockToSectionCoord(boundingBox.maxY), + SectionPos.blockToSectionCoord(boundingBox.maxZ) + ); + + return new SABB(min, max); + } + + public int getMinX() { + return SectionPos.x(packedPosMin); + } + + public int getMinY() { + return SectionPos.y(packedPosMin); + } + + public int getMinZ() { + return SectionPos.z(packedPosMin); + } + + public int getMaxX() { + return SectionPos.x(packedPosMax); + } + + public int getMaxY() { + return SectionPos.y(packedPosMax); + } + + public int getMaxZ() { + return SectionPos.z(packedPosMax); + } + + public SectionPos getMinPos() { + return SectionPos.of(packedPosMin); + } + + public SectionPos getMaxPos() { + return SectionPos.of(packedPosMax); + } + + public long getSize() { + long sizeX = getSizeX(); + long sizeY = getSizeY(); + long sizeZ = getSizeZ(); + return sizeX * sizeY * sizeZ; + } + + public int getSizeX() { + int minX = SectionPos.x(packedPosMin); + int maxX = SectionPos.x(packedPosMax); + return (maxX - minX + 1); + } + + public int getSizeY() { + int minY = SectionPos.y(packedPosMin); + int maxY = SectionPos.y(packedPosMax); + return (maxY - minY + 1); + } + + public int getSizeZ() { + int minZ = SectionPos.z(packedPosMin); + int maxZ = SectionPos.z(packedPosMax); + return (maxZ - minZ + 1); + } + + public boolean contains(BlockPos pos) { + return containsSection( + SectionPos.blockToSectionCoord(pos.getX()), + SectionPos.blockToSectionCoord(pos.getY()), + SectionPos.blockToSectionCoord(pos.getZ()) + ); + } + + public boolean containsSection(int x, int y, int z) { + int minX = SectionPos.x(packedPosMin); + int minY = SectionPos.y(packedPosMin); + int minZ = SectionPos.z(packedPosMin); + int maxX = SectionPos.x(packedPosMax); + int maxY = SectionPos.y(packedPosMax); + int maxZ = SectionPos.z(packedPosMax); + return minX <= x && x <= maxX && minY <= y && y <= maxY && minZ <= z && z <= maxZ; + } + + public void forEachSection(SectionPosConsumer action) { + int minX = SectionPos.x(packedPosMin); + int minY = SectionPos.y(packedPosMin); + int minZ = SectionPos.z(packedPosMin); + int maxX = SectionPos.x(packedPosMax); + int maxY = SectionPos.y(packedPosMax); + int maxZ = SectionPos.z(packedPosMax); + + for (int y = minY; y <= maxY; y++) { + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z <= maxZ; z++) { + action.accept(x, y, z); + } + } + } + } + + public void forEachChunk(ChunkPosConsumer action) { + int minX = SectionPos.x(packedPosMin); + int minZ = SectionPos.z(packedPosMin); + int maxX = SectionPos.x(packedPosMax); + int maxZ = SectionPos.z(packedPosMax); + + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z <= maxZ; z++) { + action.accept(x, z); + } + } + } + + public Stream stream() { + int minX = SectionPos.x(packedPosMin); + int minY = SectionPos.y(packedPosMin); + int minZ = SectionPos.z(packedPosMin); + int maxX = SectionPos.x(packedPosMax); + int maxY = SectionPos.y(packedPosMax); + int maxZ = SectionPos.z(packedPosMax); + long size = (long) (maxX - minX + 1) * (maxY - minY + 1) * (maxZ - minZ + 1); + + return StreamSupport.stream(new Spliterators.AbstractSpliterator<>(size, Spliterator.SIZED) { + final Cursor3D cursor = new Cursor3D(minX, minY, minZ, maxX, maxY, maxZ); + + public boolean tryAdvance(Consumer action) { + if (cursor.advance()) { + action.accept(SectionPos.of(cursor.nextX(), cursor.nextY(), cursor.nextZ())); + return true; + } + return false; + } + }, false); + } + + @Override + public String toString() { + int minX = SectionPos.x(packedPosMin); + int minY = SectionPos.y(packedPosMin); + int minZ = SectionPos.z(packedPosMin); + int maxX = SectionPos.x(packedPosMax); + int maxY = SectionPos.y(packedPosMax); + int maxZ = SectionPos.z(packedPosMax); + long size = ((long) maxX - minX + 1) * (maxY - minY + 1) * (maxZ - minZ + 1); + + return "SectionABB{" + + "minX=" + minX + + "minY=" + minY + + "minZ=" + minZ + + "maxX=" + maxX + + "maxY=" + maxY + + "maxZ=" + maxZ + + ", size=" + size + + '}'; + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/section/SectionPosConsumer.java b/src/main/java/com/github/elenterius/biomancy/world/section/SectionPosConsumer.java new file mode 100644 index 000000000..cd13e06dd --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/section/SectionPosConsumer.java @@ -0,0 +1,6 @@ +package com.github.elenterius.biomancy.world.section; + +@FunctionalInterface +public interface SectionPosConsumer { + void accept(int sectionX, int sectionY, int sectionZ); +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialKey.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialKey.java new file mode 100644 index 000000000..653a83618 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialKey.java @@ -0,0 +1,98 @@ +package com.github.elenterius.biomancy.world.spatial; + +import net.minecraft.world.phys.AABB; +import org.h2.mvstore.rtree.Spatial; + +import java.util.Arrays; + +public class SpatialKey implements Spatial { + + private final long id; + private final float[] minMax; + + public SpatialKey(long id, AABB aabb) { + this.id = id; + this.minMax = new float[]{ + (float) aabb.minX, (float) aabb.maxX, + (float) aabb.minY, (float) aabb.maxY, + (float) aabb.minZ, (float) aabb.maxZ + }; + } + + public SpatialKey(long id, float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + this.id = id; + this.minMax = new float[]{ + minX, maxX, + minY, maxY, + minZ, maxZ + }; + } + + public SpatialKey(long id, SpatialKey other) { + this.id = id; + this.minMax = other.minMax.clone(); + } + + @Override + public float min(int dim) { + return minMax[dim + dim]; + } + + @Override + public void setMin(int dim, float x) { + minMax[dim + dim] = x; + } + + @Override + public float max(int dim) { + return minMax[dim + dim + 1]; + } + + @Override + public void setMax(int dim, float x) { + minMax[dim + dim + 1] = x; + } + + @Override + public Spatial clone(long id) { + return new SpatialKey(id, this); + } + + @Override + public long getId() { + return id; + } + + @Override + public boolean isNull() { + return minMax.length == 0; + } + + @Override + public int hashCode() { + return Long.hashCode(id); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + else if (!(other instanceof SpatialKey)) { + return false; + } + + SpatialKey otherKey = (SpatialKey) other; + if (id != otherKey.id) { + return false; + } + + return equalsIgnoringId(otherKey); + } + + @Override + public boolean equalsIgnoringId(Spatial other) { + return Arrays.equals(minMax, ((SpatialKey) other).minMax); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialQuery.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialQuery.java new file mode 100644 index 000000000..795ae1f86 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialQuery.java @@ -0,0 +1,166 @@ +package com.github.elenterius.biomancy.world.spatial; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.h2.mvstore.rtree.Spatial; + +import java.util.Arrays; + +public final class SpatialQuery implements Spatial { + + private final float[] minMax; + + private SpatialQuery(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { + this.minMax = new float[]{ + minX, maxX, + minY, maxY, + minZ, maxZ + }; + } + + public SpatialQuery(SpatialQuery other) { + this.minMax = other.minMax.clone(); + } + + public float minX() { + return minMax[0]; + } + + public float maxX() { + return minMax[1]; + } + + public float minY() { + return minMax[2]; + } + + public float maxY() { + return minMax[3]; + } + + public float minZ() { + return minMax[4]; + } + + public float maxZ() { + return minMax[5]; + } + + @Override + public float min(int dim) { + return minMax[dim + dim]; + } + + @Override + public void setMin(int dim, float v) { + minMax[dim + dim] = v; + } + + @Override + public float max(int dim) { + return minMax[dim + dim + 1]; + } + + @Override + public void setMax(int dim, float x) { + minMax[dim + dim + 1] = x; + } + + @Override + public Spatial clone(long id) { + return new SpatialQuery(this); + } + + @Override + public long getId() { + return 0; + } + + @Override + public boolean isNull() { + return minMax.length == 0; + } + + @Override + public int hashCode() { + return Arrays.hashCode(minMax); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof SpatialQuery otherKey)) { + return false; + } + + return equalsIgnoringId(otherKey); + } + + @Override + public boolean equalsIgnoringId(Spatial other) { + return Arrays.equals(minMax, ((SpatialQuery) other).minMax); + } + + public static SpatialQuery of(BlockPos pos) { + return new SpatialQuery( + pos.getX(), pos.getY(), pos.getZ(), + pos.getX() + 1f, pos.getY() + 1f, pos.getZ() + 1f + ); + } + + public static SpatialQuery of(BlockPos posA, BlockPos posB) { + int minX = Math.min(posA.getX(), posB.getX()); + int minY = Math.min(posA.getY(), posB.getY()); + int minZ = Math.min(posA.getZ(), posB.getZ()); + int maxX = Math.max(posA.getX(), posB.getX()); + int maxY = Math.max(posA.getY(), posB.getY()); + int maxZ = Math.max(posA.getZ(), posB.getZ()); + + return new SpatialQuery(minX, minY, minZ, maxX, maxY, maxZ); + } + + public static SpatialQuery of(AABB aabb) { + return new SpatialQuery( + (float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, + (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ + ); + } + + public static SpatialQuery of(Entity entity) { + return of(entity.getBoundingBox()); + } + + public static SpatialQuery of(BoundingBox bb) { + return new SpatialQuery( + bb.minX(), bb.minY(), bb.minZ(), + bb.maxX() + 1f, bb.maxY() + 1f, bb.maxZ() + 1f + ); + } + + public static SpatialQuery of(Vec3 vecA, Vec3 vecB) { + return new SpatialQuery( + (float) vecA.x, (float) vecA.y, (float) vecA.z, + (float) vecB.x, (float) vecB.y, (float) vecB.z + ); + } + + public static SpatialQuery unitCubeFromLowerCorner(Vec3 vec) { + return new SpatialQuery( + (float) vec.x, (float) vec.y, (float) vec.z, + (float) vec.x + 1f, (float) vec.y + 1f, (float) vec.z + 1f + ); + } + + public static SpatialQuery unitCubeFromMiddle(Vec3 vec) { + return new SpatialQuery( + (float) vec.x - 0.5f, (float) vec.y - 0.5f, (float) vec.z - 0.5f, + (float) vec.x + 0.5f, (float) vec.y + 0.5f, (float) vec.z + 0.5f + ); + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialShapeManager.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialShapeManager.java new file mode 100644 index 000000000..85bc9d608 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialShapeManager.java @@ -0,0 +1,125 @@ +package com.github.elenterius.biomancy.world.spatial; + +import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraftforge.event.level.LevelEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import org.h2.mvstore.MVMap; +import org.h2.mvstore.rtree.MVRTreeMap; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Predicate; +import java.util.function.Supplier; + +@Mod.EventBusSubscriber(modid = BiomancyMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE) +public final class SpatialShapeManager { + + private SpatialShapeManager() {} + + @SubscribeEvent + public static void onLevelUnload(LevelEvent.Unload event) { + if (!event.getLevel().isClientSide() && event.getLevel() instanceof ServerLevel serverLevel && (serverLevel.dimension() == Level.OVERWORLD)) { + SpatialShapeStorage storage = SpatialShapeStorage.getInstance(serverLevel); + storage.close(); + } + } + + private static String getLevelKey(ServerLevel level) { + return level.dimension().location().toString(); + } + + public static Shape getOrCreateShape(ServerLevel level, BlockPos shapeId, Supplier factory) { + SpatialShapeStorage storage = SpatialShapeStorage.getInstance(level); + return storage.getOrCreate(getLevelKey(level), shapeId.asLong(), factory); + } + + public static void remove(ServerLevel level, BlockPos shapeId) { + SpatialShapeStorage spatialStorage = SpatialShapeStorage.getInstance(level); + spatialStorage.remove(getLevelKey(level), shapeId.asLong()); + } + + @Nullable + public static Shape getClosestShape(ServerLevel level, BlockPos blockPos) { + return getClosestShape(level, blockPos, shape -> true); + } + + @Nullable + public static Shape getClosestShape(ServerLevel level, BlockPos blockPos, Predicate predicate) { + SpatialQuery query = SpatialQuery.of(blockPos); + SpatialShapeStorage spatialStorage = SpatialShapeStorage.getInstance(level); + String levelKey = getLevelKey(level); + + final float x = Mth.lerp(0.5f, query.minX(), query.maxX()); + final float y = Mth.lerp(0.5f, query.minY(), query.maxY()); + final float z = Mth.lerp(0.5f, query.minZ(), query.maxZ()); + + double minDistSqr = Double.MAX_VALUE; + Shape closestShape = null; + + MVRTreeMap.RTreeCursor intersectingKeys = spatialStorage.findIntersecting(levelKey, query); + MVMap shapes = spatialStorage.getShapes(levelKey); + + while (intersectingKeys.hasNext()) { + long id = intersectingKeys.next().getId(); + Shape shape = shapes.get(id); + if (shape != null && shape.contains(x, y, z) && predicate.test(shape)) { + double distSqr = shape.distanceToSqr(x, y, z); + if (distSqr < minDistSqr) { + closestShape = shape; + minDistSqr = distSqr; + } + } + } + + return closestShape; + } + + @Nullable + public static Shape getAnyShape(ServerLevel level, Entity entity, QueryStrategy strategy, Predicate predicate) { + return getAnyShape(level, strategy, SpatialQuery.of(entity), predicate); + } + + @Nullable + public static Shape getAnyShape(ServerLevel level, QueryStrategy strategy, SpatialQuery query, Predicate predicate) { + SpatialShapeStorage spatialStorage = SpatialShapeStorage.getInstance(level); + String levelKey = getLevelKey(level); + + MVRTreeMap.RTreeCursor foundKeys = strategy.find(levelKey, query, spatialStorage); + MVMap shapes = spatialStorage.getShapes(levelKey); + + while (foundKeys.hasNext()) { + long id = foundKeys.next().getId(); + Shape shape = shapes.get(id); + if (shape != null && strategy.test(query, shape) && predicate.test(shape)) { + return shape; + } + } + + return null; + } + + public interface QueryStrategy { + MVRTreeMap.RTreeCursor find(String levelKey, SpatialQuery query, SpatialShapeStorage spatialStorage); + + boolean test(SpatialQuery query, Shape shape); + + QueryStrategy INTERSECTION = new QueryStrategy() { + @Override + public MVRTreeMap.RTreeCursor find(String levelKey, SpatialQuery query, SpatialShapeStorage spatialStorage) { + return spatialStorage.findIntersecting(levelKey, query); + } + + @Override + public boolean test(SpatialQuery query, Shape shape) { + return shape.intersectsCuboid(query.minX(), query.minY(), query.minZ(), query.maxX(), query.maxY(), query.maxZ()); + } + }; + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialShapeStorage.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialShapeStorage.java new file mode 100644 index 000000000..bf13773ef --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/SpatialShapeStorage.java @@ -0,0 +1,150 @@ +package com.github.elenterius.biomancy.world.spatial; + +import com.github.elenterius.biomancy.BiomancyMod; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import com.github.elenterius.biomancy.world.spatial.type.ShapeDataType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.storage.LevelResource; +import org.apache.logging.log4j.MarkerManager; +import org.h2.mvstore.MVMap; +import org.h2.mvstore.MVStore; +import org.h2.mvstore.rtree.MVRTreeMap; +import org.h2.mvstore.rtree.Spatial; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public final class SpatialShapeStorage extends SavedData implements AutoCloseable { + + public static final String LOG_MARKER = "SpatialStore"; + + public static final LevelResource DATA_DIR = new LevelResource("data"); + + private final MVStore store; + private final Map> levelTrees = new HashMap<>(); + private final Map> levelShapes = new HashMap<>(); + + private SpatialShapeStorage(String filePath) { + store = new MVStore.Builder().fileName(filePath).cacheSize(8).open(); + BiomancyMod.LOGGER.debug(MarkerManager.getMarker(LOG_MARKER), "initialized Store using file {}", filePath); + } + + @SuppressWarnings("unused") + private static SpatialShapeStorage load(CompoundTag tag, String path) { + return new SpatialShapeStorage(path); + } + + public static SpatialShapeStorage getInstance(ServerLevel level) { + String path = level.getServer().getWorldPath(DATA_DIR).resolve("biomancy.spatial.db").toFile().getPath(); + + //noinspection resource + return level.getServer() + .overworld() + .getDataStorage() + .computeIfAbsent(tag -> load(tag, path), () -> new SpatialShapeStorage(path), "biomancy.spatial"); + } + + @Override + public void close() { + levelTrees.clear(); + levelShapes.clear(); + + BiomancyMod.LOGGER.debug(MarkerManager.getMarker(LOG_MARKER), "Persisting pending changes and compacting the store"); + store.close(250); + } + + MVRTreeMap getTree(String levelKey) { + return levelTrees.computeIfAbsent(levelKey, this::createTreeForLevel); + } + + MVMap getShapes(String levelKey) { + return levelShapes.computeIfAbsent(levelKey, this::createMapForLevel); + } + + private MVRTreeMap createTreeForLevel(String levelKey) { + MVRTreeMap.Builder builder = new MVRTreeMap.Builder().dimensions(3); + MVRTreeMap tree = store.openMap(levelKey + "_rtree", builder); + if (!tree.isQuadraticSplit()) { + tree.setQuadraticSplit(true); + } + return tree; + } + + private MVMap createMapForLevel(String levelKey) { + MVMap.Builder builder = new MVMap.Builder().valueType(new ShapeDataType()); + return store.openMap(levelKey + "_shapes", builder); + } + + public MVRTreeMap.RTreeCursor findIntersecting(String levelKey, Spatial boundingBox) { + return getTree(levelKey).findIntersectingKeys(boundingBox); + } + + public MVRTreeMap.RTreeCursor findContained(String levelKey, Spatial boundingBox) { + return getTree(levelKey).findContainedKeys(boundingBox); + } + + public Shape getOrCreate(String levelKey, long shapeId, Supplier factory) { + MVRTreeMap tree = getTree(levelKey); + MVMap shapes = getShapes(levelKey); + + if (shapes.containsKey(shapeId)) { + Shape shape = shapes.get(shapeId); + BiomancyMod.LOGGER.debug(MarkerManager.getMarker(LOG_MARKER), "Fetched existing id={} shape={}", shapeId, shape); + return shape; + } + + Shape shape = factory.get(); + shapes.put(shapeId, shape); + tree.add(new SpatialKey(shapeId, shape.getAABB()), shapeId); + + BiomancyMod.LOGGER.debug(MarkerManager.getMarker(LOG_MARKER), "Created id={} shape={}", shapeId, shape); + + setDirty(); + + return shape; + } + + public void remove(String levelKey, long shapeId) { + MVMap shapes = getShapes(levelKey); + MVRTreeMap tree = getTree(levelKey); + + Shape shape = shapes.get(shapeId); + if (shape == null) return; + + Long removed = tree.remove(new SpatialKey(shapeId, shape.getAABB())); + shapes.remove(shapeId); + + BiomancyMod.LOGGER.debug(MarkerManager.getMarker(LOG_MARKER), "Removed id={} shape={}", removed, shape); + + setDirty(); + } + + @Override + public CompoundTag save(CompoundTag tag) { + return tag; + } + + @Override + public void save(File file) { + super.save(file); + + if (!store.isClosed()) { + if (store.hasUnsavedChanges()) { + long version = store.commit(); + + if (BiomancyMod.LOGGER.isDebugEnabled()) { + String message = version >= 0 ? "Flushed all changes to disk" : "Failed to flush changes"; + BiomancyMod.LOGGER.debug(MarkerManager.getMarker(LOG_MARKER), message); + } + } + else { + BiomancyMod.LOGGER.debug(MarkerManager.getMarker(LOG_MARKER), "Found no changes to flush to disk"); + } + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/CuboidShape.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/CuboidShape.java new file mode 100644 index 000000000..6e4b1131e --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/CuboidShape.java @@ -0,0 +1,92 @@ +package com.github.elenterius.biomancy.world.spatial.geometry; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import com.github.elenterius.biomancy.world.spatial.type.ShapeSerializers; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +/** + * Axis Aligned Box + */ +public class CuboidShape implements Shape { + + private final Vec3 origin; + private final AABB aabb; + + public CuboidShape(Vec3 min, Vec3 max) { + aabb = new AABB(min, max); + origin = aabb.getCenter(); + } + + public CuboidShape(BlockPos min, BlockPos max) { + aabb = new AABB(min, max); + origin = aabb.getCenter(); + } + + public CuboidShape(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + aabb = new AABB(minX, minY, minZ, maxX, maxY, maxZ); + origin = aabb.getCenter(); + } + + @Override + public boolean contains(double x, double y, double z) { + return aabb.contains(x, y, z); + } + + @Override + public Vec3 center() { + return origin; + } + + @Override + public double distanceToSqr(double x, double y, double z) { + return origin.distanceToSqr(x, y, z); + } + + @Override + public AABB getAABB() { + return aabb; + } + + @Override + public boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + return aabb.intersects(minX, minY, minZ, maxX, maxY, maxZ); + } + + @Override + public NBTSerializer getNBTSerializer() { + return ShapeSerializers.CUBOID_SERIALIZER; + } + + public record Serializer(String id) implements NBTSerializer { + @Override + public CompoundTag write(CuboidShape shape) { + CompoundTag tag = new CompoundTag(); + tag.putLongArray("MinMax", new long[]{ + Double.doubleToLongBits(shape.aabb.minX), + Double.doubleToLongBits(shape.aabb.minY), + Double.doubleToLongBits(shape.aabb.minZ), + Double.doubleToLongBits(shape.aabb.maxX), + Double.doubleToLongBits(shape.aabb.maxY), + Double.doubleToLongBits(shape.aabb.maxZ) + }); + + return tag; + } + + @Override + public CuboidShape read(CompoundTag tag) { + long[] minMax = tag.getLongArray("MinMax"); + double minX = Double.longBitsToDouble(minMax[0]); + double minY = Double.longBitsToDouble(minMax[1]); + double minZ = Double.longBitsToDouble(minMax[2]); + double maxX = Double.longBitsToDouble(minMax[3]); + double maxY = Double.longBitsToDouble(minMax[4]); + double maxZ = Double.longBitsToDouble(minMax[5]); + return new CuboidShape(minX, minY, minZ, maxX, maxY, maxZ); + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/GeometryUtil.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/GeometryUtil.java new file mode 100644 index 000000000..b3f1e71fd --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/GeometryUtil.java @@ -0,0 +1,24 @@ +package com.github.elenterius.biomancy.world.spatial.geometry; + +import net.minecraft.util.Mth; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public final class GeometryUtil { + private GeometryUtil() {} + + public static Vec3 closestPointOnAABB(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vec3 pos) { + double x = Mth.clamp(pos.x, minX, maxX); + double y = Mth.clamp(pos.y, minY, maxY); + double z = Mth.clamp(pos.z, minZ, maxZ); + return new Vec3(x, y, z); + } + + public static Vec3 closestPointOnAABB(AABB aabb, Vec3 pos) { + double x = Mth.clamp(pos.x, aabb.minX, aabb.maxX); + double y = Mth.clamp(pos.y, aabb.minY, aabb.maxY); + double z = Mth.clamp(pos.z, aabb.minZ, aabb.maxZ); + return new Vec3(x, y, z); + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/HasRadius.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/HasRadius.java new file mode 100644 index 000000000..5d1c2f4c5 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/HasRadius.java @@ -0,0 +1,5 @@ +package com.github.elenterius.biomancy.world.spatial.geometry; + +public interface HasRadius { + float getRadius(); +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/OctantEllipsoidShape.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/OctantEllipsoidShape.java new file mode 100644 index 000000000..01e58212e --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/OctantEllipsoidShape.java @@ -0,0 +1,125 @@ +package com.github.elenterius.biomancy.world.spatial.geometry; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import com.github.elenterius.biomancy.world.spatial.type.ShapeSerializers; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +/** + * Axis Aligned Ellipsoid made up of 8 segments (octant's) + */ +public class OctantEllipsoidShape implements Shape, HasRadius { + private final Vec3 center; + private final float radius; + private final Vec3 origin; + private final float aPos; + private final float bPos; + private final float cPos; + private final float aNeg; + private final float bNeg; + private final float cNeg; + + public OctantEllipsoidShape(Vec3 pos, float aPos, float bPos, float cPos, float aNeg, float bNeg, float cNeg) { + this.origin = pos; + this.aPos = aPos; + this.bPos = bPos; + this.cPos = cPos; + this.aNeg = aNeg; + this.bNeg = bNeg; + this.cNeg = cNeg; + + Vec3 min = new Vec3(origin.x - aNeg, origin.y - bNeg, origin.z - cNeg); + Vec3 max = new Vec3(origin.x + aPos, origin.y + bPos, origin.z + cPos); + center = min.lerp(max, 0.5d); + + float a = (aPos + aNeg) / 2f; + float b = (bPos + bNeg) / 2f; + float c = (cPos + cNeg) / 2f; + radius = Math.max(Math.max(a, b), c); + } + + public OctantEllipsoidShape(double x, double y, double z, float aPos, float bPos, float cPos, float aNeg, float bNeg, float cNeg) { + this(new Vec3(x, y, z), aPos, bPos, cPos, aNeg, bNeg, cNeg); + } + + @Override + public boolean contains(double x, double y, double z) { + double dx = origin.x - x; + double dy = origin.y - y; + double dz = origin.z - z; + + dx /= dx <= 0 ? aPos : aNeg; + dy /= dy <= 0 ? bPos : bNeg; + dz /= dz <= 0 ? cPos : cNeg; + + return dx * dx + dy * dy + dz * dz < 1; // x^2/a^2 + y^2/b^2 + z^2/c^2 = 1 + } + + @Override + public boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + Vec3 closestPoint = GeometryUtil.closestPointOnAABB(minX, minY, minZ, maxX, maxY, maxZ, origin); + return contains(closestPoint.x, closestPoint.y, closestPoint.z); + } + + @Override + public Vec3 center() { + return center; + } + + @Override + public double distanceToSqr(double x, double y, double z) { + return center.distanceToSqr(x, y, z); + } + + @Override + public AABB getAABB() { + return new AABB(origin.x - aNeg, origin.y - bNeg, origin.z - cNeg, origin.x + aPos, origin.y + bPos, origin.z + cPos); + } + + @Override + public float getRadius() { + return radius; + } + + @Override + public NBTSerializer getNBTSerializer() { + return ShapeSerializers.OCTANT_ELLIPSOID_SERIALIZER; + } + + public record Serializer(String id) implements NBTSerializer { + + @Override + public CompoundTag write(OctantEllipsoidShape shape) { + CompoundTag tag = new CompoundTag(); + + tag.putDouble("X", shape.origin.x); + tag.putDouble("Y", shape.origin.y); + tag.putDouble("Z", shape.origin.z); + + tag.putFloat("A+", shape.aPos); + tag.putFloat("B+", shape.bPos); + tag.putFloat("C+", shape.cPos); + tag.putFloat("A-", shape.aNeg); + tag.putFloat("B-", shape.bNeg); + tag.putFloat("C-", shape.cNeg); + return tag; + } + + @Override + public OctantEllipsoidShape read(CompoundTag tag) { + double x = tag.getDouble("X"); + double y = tag.getDouble("Y"); + double z = tag.getDouble("Z"); + + float aPos = tag.getFloat("A+"); + float bPos = tag.getFloat("B+"); + float cPos = tag.getFloat("C+"); + float aNeg = tag.getFloat("A-"); + float bNeg = tag.getFloat("B-"); + float cNeg = tag.getFloat("C-"); + + return new OctantEllipsoidShape(x, y, z, aPos, bPos, cPos, aNeg, bNeg, cNeg); + } + } +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/Shape.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/Shape.java new file mode 100644 index 000000000..e764ded39 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/Shape.java @@ -0,0 +1,86 @@ +package com.github.elenterius.biomancy.world.spatial.geometry; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializable; +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public interface Shape extends NBTSerializable { + + boolean contains(double x, double y, double z); + + //boolean containsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ); + + default boolean intersectsCuboid(AABB aabb) { + return intersectsCuboid(aabb.minX, aabb.minY, aabb.minZ, aabb.maxX, aabb.maxY, aabb.maxZ); + } + + boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ); + + Vec3 center(); + + double distanceToSqr(double x, double y, double z); + + AABB getAABB(); + + default boolean isEmpty() { + return this == EMPTY; + } + + Shape EMPTY = new Shape() { + static final Serializer SERIALIZER = new Serializer("empty"); + + @Override + public boolean contains(double x, double y, double z) { + return false; + } + + @Override + public boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + return false; + } + + @Override + public Vec3 center() { + return Vec3.ZERO; + } + + @Override + public double distanceToSqr(double x, double y, double z) { + return Double.MAX_VALUE; + } + + @Override + public AABB getAABB() { + return AABB.unitCubeFromLowerCorner(Vec3.ZERO); + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); //compare by identity + } + + @Override + public NBTSerializer getNBTSerializer() { + return SERIALIZER; + } + + public record Serializer(String id) implements NBTSerializer { + @Override + public CompoundTag write(Shape shape) { + return new CompoundTag(); + } + + @Override + public Shape read(CompoundTag tag) { + return Shape.EMPTY; + } + } + }; +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/ShapeHierarchy.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/ShapeHierarchy.java new file mode 100644 index 000000000..43fce7c98 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/ShapeHierarchy.java @@ -0,0 +1,186 @@ +package com.github.elenterius.biomancy.world.spatial.geometry; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.core.SectionPos; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class ShapeHierarchy { + + /** + * spatial hash map based on a 16x16x16 cell grid + */ + protected final Long2ObjectMap> sections = new Long2ObjectOpenHashMap<>(); + + protected final AABB aabb; + + public ShapeHierarchy(Iterable shapes) { + double aabbMinX = Double.MAX_VALUE; + double aabbMinY = Double.MAX_VALUE; + double aabbMinZ = Double.MAX_VALUE; + double aabbMaxX = Double.MIN_VALUE; + double aabbMaxY = Double.MIN_VALUE; + double aabbMaxZ = Double.MIN_VALUE; + + for (T shape : shapes) { + AABB boundingBox = shape.getAABB(); + + if (boundingBox.minX < aabbMinX) aabbMinX = boundingBox.minX; + if (boundingBox.minY < aabbMinY) aabbMinY = boundingBox.minY; + if (boundingBox.minZ < aabbMinZ) aabbMinZ = boundingBox.minZ; + if (boundingBox.maxX > aabbMaxX) aabbMaxX = boundingBox.maxX; + if (boundingBox.maxY > aabbMaxY) aabbMaxY = boundingBox.maxY; + if (boundingBox.maxZ > aabbMaxZ) aabbMaxZ = boundingBox.maxZ; + + addShapeToSections(shape); + } + + this.aabb = new AABB(aabbMinX, aabbMinY, aabbMinZ, aabbMaxX, aabbMaxY, aabbMaxZ); + } + + protected void addShapesToSection(long sectionKey, Collection shapes) { + sections.computeIfAbsent(sectionKey, k -> new HashSet<>()).addAll(shapes); + } + + protected void addShapeToSections(T shape) { + AABB boundingBox = shape.getAABB(); + int minSectionX = SectionPos.blockToSectionCoord(boundingBox.minX); + int minSectionY = SectionPos.blockToSectionCoord(boundingBox.minY); + int minSectionZ = SectionPos.blockToSectionCoord(boundingBox.minZ); + int maxSectionX = SectionPos.blockToSectionCoord(boundingBox.maxX); + int maxSectionY = SectionPos.blockToSectionCoord(boundingBox.maxY); + int maxSectionZ = SectionPos.blockToSectionCoord(boundingBox.maxZ); + + for (int y = minSectionY; y <= maxSectionY; y++) { + for (int x = minSectionX; x <= maxSectionX; x++) { + for (int z = minSectionZ; z <= maxSectionZ; z++) { + sections.computeIfAbsent(SectionPos.asLong(x, y, z), k -> new HashSet<>()).add(shape); + } + } + } + } + + @Nullable + protected Set getShapesInSection(double x, double y, double z) { + int sectionX = SectionPos.blockToSectionCoord(x); + int sectionY = SectionPos.blockToSectionCoord(y); + int sectionZ = SectionPos.blockToSectionCoord(z); + return sections.get(SectionPos.asLong(sectionX, sectionY, sectionZ)); + } + + public Vec3 getCenter() { + return aabb.getCenter(); + } + + public double distanceToSqr(double x, double y, double z) { + double smallestDistSqr = Double.MAX_VALUE; + + Set shapesInSection = getShapesInSection(x, y, z); + if (shapesInSection != null) { + for (Shape shape : shapesInSection) { + if (shape.contains(x, y, z)) { + double distSqr = shape.distanceToSqr(x, y, z); + if (distSqr < smallestDistSqr) { + smallestDistSqr = distSqr; + } + } + } + } + + return smallestDistSqr; + } + + public AABB getAABB() { + return aabb; + } + + @Nullable + public T getClosestShapeContaining(double x, double y, double z) { + if (!aabb.contains(x, y, z)) return null; + + T closestShape = null; + + Set shapesInSection = getShapesInSection(x, y, z); + if (shapesInSection != null) { + double minDistSqr = Double.MAX_VALUE; + for (T shape : shapesInSection) { + if (shape.contains(x, y, z)) { + double distSqr = shape.distanceToSqr(x, y, z); + if (distSqr < minDistSqr) { + closestShape = shape; + minDistSqr = distSqr; + } + } + } + } + + return closestShape; + } + + @Nullable + public List getShapesContaining(double x, double y, double z) { + if (!aabb.contains(x, y, z)) return null; + + Set shapesInSection = getShapesInSection(x, y, z); + if (shapesInSection != null) { + List shapes = new ArrayList<>(); + for (T shape : shapesInSection) { + if (shape.contains(x, y, z)) { + shapes.add(shape); + } + } + return shapes; + } + + return null; + } + + public boolean contains(double x, double y, double z) { + if (!aabb.contains(x, y, z)) return false; + + Set shapesInSection = getShapesInSection(x, y, z); + if (shapesInSection != null) { + for (Shape shape : shapesInSection) { + if (shape.contains(x, y, z)) { + return true; + } + } + } + + return false; + } + + public boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + if (!aabb.intersects(minX, minY, minZ, maxX, maxY, maxZ)) return false; + + int sectionMinX = SectionPos.blockToSectionCoord(Mth.clamp(minX, aabb.minX, aabb.maxX)); + int sectionMinY = SectionPos.blockToSectionCoord(Mth.clamp(minY, aabb.minY, aabb.maxY)); + int sectionMinZ = SectionPos.blockToSectionCoord(Mth.clamp(minZ, aabb.minZ, aabb.maxZ)); + int sectionMaxX = SectionPos.blockToSectionCoord(Mth.clamp(maxX, aabb.minX, aabb.maxX)); + int sectionMaxY = SectionPos.blockToSectionCoord(Mth.clamp(maxY, aabb.minY, aabb.maxY)); + int sectionMaxZ = SectionPos.blockToSectionCoord(Mth.clamp(maxZ, aabb.minZ, aabb.maxZ)); + + for (int y = sectionMinY; y <= sectionMaxY; y++) { + for (int x = sectionMinX; x <= sectionMaxX; x++) { + for (int z = sectionMinZ; z <= sectionMaxZ; z++) { + Set shapesInSection = sections.get(SectionPos.asLong(x, y, z)); + if (shapesInSection != null) { + for (Shape shape : shapesInSection) { + if (shape.intersectsCuboid(minX, minY, minZ, maxX, maxY, maxZ)) { + return true; + } + } + } + } + } + } + + return false; + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/SphereShape.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/SphereShape.java new file mode 100644 index 000000000..9913efdce --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/SphereShape.java @@ -0,0 +1,82 @@ +package com.github.elenterius.biomancy.world.spatial.geometry; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import com.github.elenterius.biomancy.world.spatial.type.ShapeSerializers; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public class SphereShape implements Shape, HasRadius { + + private final Vec3 origin; + private final float radius; + private final AABB aabb; + + public SphereShape(Vec3 origin, float radius) { + this.origin = origin; + this.radius = radius; + aabb = new AABB(origin.x - radius, origin.y - radius, origin.z - radius, origin.x + radius, origin.y + radius, origin.z + radius); + } + + public SphereShape(double x, double y, double z, float radius) { + this(new Vec3(x, y, z), radius); + } + + @Override + public boolean contains(double x, double y, double z) { + return origin.distanceToSqr(x, y, z) < radius * radius; + } + + @Override + public boolean intersectsCuboid(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + Vec3 closestPoint = GeometryUtil.closestPointOnAABB(minX, minY, minZ, maxX, maxY, maxZ, origin); + double distSqr = closestPoint.distanceToSqr(origin); + return distSqr < radius * radius; + } + + @Override + public Vec3 center() { + return origin; + } + + @Override + public double distanceToSqr(double x, double y, double z) { + return origin.distanceToSqr(x, y, z); + } + + @Override + public AABB getAABB() { + return aabb; + } + + public float getRadius() { + return radius; + } + + @Override + public NBTSerializer getNBTSerializer() { + return ShapeSerializers.SPHERE_SERIALIZER; + } + + public record Serializer(String id) implements NBTSerializer { + @Override + public CompoundTag write(SphereShape shape) { + CompoundTag tag = new CompoundTag(); + tag.putFloat("Radius", shape.radius); + tag.putDouble("X", shape.origin.x); + tag.putDouble("Y", shape.origin.y); + tag.putDouble("Z", shape.origin.z); + return tag; + } + + @Override + public SphereShape read(CompoundTag tag) { + float radius = tag.getFloat("Radius"); + double x = tag.getDouble("X"); + double y = tag.getDouble("Y"); + double z = tag.getDouble("Z"); + return new SphereShape(x, y, z, radius); + } + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/package-info.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/package-info.java new file mode 100644 index 000000000..1b041ef7a --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/geometry/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.world.spatial.geometry; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/package-info.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/package-info.java new file mode 100644 index 000000000..6c38e3c65 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.elenterius.biomancy.world.spatial; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/type/ShapeDataType.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/type/ShapeDataType.java new file mode 100644 index 000000000..6f8e6fa64 --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/type/ShapeDataType.java @@ -0,0 +1,104 @@ +package com.github.elenterius.biomancy.world.spatial.type; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import org.h2.mvstore.DataUtils; +import org.h2.mvstore.WriteBuffer; +import org.h2.mvstore.type.BasicDataType; +import org.h2.util.Utils; + +import java.io.*; +import java.nio.ByteBuffer; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class ShapeDataType extends BasicDataType { + + private final Object2IntOpenHashMap> averageSizes = new Object2IntOpenHashMap<>(); + + public static byte[] writeCompressed(CompoundTag compoundTag) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + try (DataOutputStream dataoutputstream = new DataOutputStream(new GZIPOutputStream(byteArrayOutputStream))) { + NbtIo.write(compoundTag, dataoutputstream); + } + catch (IOException e) { + return Utils.EMPTY_BYTES; + } + + return byteArrayOutputStream.toByteArray(); + } + + public static CompoundTag readCompressed(byte[] bytes) { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + + try (DataInputStream inputStream = new DataInputStream(new GZIPInputStream(byteArrayInputStream))) { + return NbtIo.read(inputStream, NbtAccounter.UNLIMITED); + } + catch (IOException e) { + return new CompoundTag(); + } + } + + @Override + public int getMemory(Shape shape) { + return averageSizes.getOrDefault(shape.getClass(), 1000); + } + + private void computeAverageSize(Shape shape, byte[] data) { + int averageSize = averageSizes.getOrDefault(shape.getClass(), 1000); + int size = data.length * 2; + long n = 15L; + averageSize = (int) ((size + n * averageSize) / (n + 1)); //exponential moving average + averageSizes.put(shape.getClass(), averageSize); + } + + @Override + public void write(WriteBuffer buffer, Shape shape) { + if (shape == Shape.EMPTY) { + buffer.putVarInt(0); + return; + } + + NBTSerializer serializer = shape.getNBTSerializer(); + CompoundTag nbt = serializer.write(shape); + nbt.putString("Serializer", serializer.id()); + + byte[] data = writeCompressed(nbt); + buffer.putVarInt(data.length); + if (data.length > 0) { + buffer.put(data); + computeAverageSize(shape, data); + } + } + + @Override + public Shape read(ByteBuffer buffer) { + int length = DataUtils.readVarInt(buffer); + if (length > 0) { + byte[] data = new byte[length]; + buffer.get(data); + CompoundTag nbt = readCompressed(data); + + String serializerId = nbt.getString("Serializer"); + NBTSerializer serializer = ShapeSerializers.get(serializerId); + if (serializer != null) { + Shape shape = serializer.read(nbt); + computeAverageSize(shape, data); + return shape; + } + } + + return Shape.EMPTY; + } + + @Override + public Shape[] createStorage(int size) { + return new Shape[size]; + } + +} diff --git a/src/main/java/com/github/elenterius/biomancy/world/spatial/type/ShapeSerializers.java b/src/main/java/com/github/elenterius/biomancy/world/spatial/type/ShapeSerializers.java new file mode 100644 index 000000000..50bd5fd5a --- /dev/null +++ b/src/main/java/com/github/elenterius/biomancy/world/spatial/type/ShapeSerializers.java @@ -0,0 +1,44 @@ +package com.github.elenterius.biomancy.world.spatial.type; + +import com.github.elenterius.biomancy.util.serialization.NBTSerializer; +import com.github.elenterius.biomancy.world.MobSpawnFilterShape; +import com.github.elenterius.biomancy.world.mound.MoundShape; +import com.github.elenterius.biomancy.world.spatial.geometry.CuboidShape; +import com.github.elenterius.biomancy.world.spatial.geometry.OctantEllipsoidShape; +import com.github.elenterius.biomancy.world.spatial.geometry.Shape; +import com.github.elenterius.biomancy.world.spatial.geometry.SphereShape; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public final class ShapeSerializers { + private static final Map> SERIALIZERS = new HashMap<>(); + public static final NBTSerializer CUBOID_SERIALIZER = register("cuboid", CuboidShape.Serializer::new); + public static final NBTSerializer SPHERE_SERIALIZER = register("sphere", SphereShape.Serializer::new); + public static final NBTSerializer OCTANT_ELLIPSOID_SERIALIZER = register("octant_ellipsoid", OctantEllipsoidShape.Serializer::new); + public static final NBTSerializer MOUND_SERIALIZER = register("mound", MoundShape.Serializer::new); + public static final NBTSerializer MOB_SPAWN_FILTER_SERIALIZER = register("mob_spawn_filter", MobSpawnFilterShape.Serializer::new); + + private ShapeSerializers() {} + + public static NBTSerializer register(String id, Factory factory) { + NBTSerializer serializer = cast(factory.create(id)); + SERIALIZERS.put(id, serializer); + return serializer; + } + + public static @Nullable NBTSerializer get(String id) { + return SERIALIZERS.get(id); + } + + private static NBTSerializer cast(NBTSerializer serializer) { + //noinspection unchecked + return (NBTSerializer) serializer; + } + + @FunctionalInterface + public interface Factory { + NBTSerializer create(String id); + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 6f3e80b9b..1be206280 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -1,2 +1,3 @@ public net.minecraft.world.item.crafting.RecipeManager m_44054_(Lnet/minecraft/world/item/crafting/RecipeType;)Ljava/util/Map; # byType protected net.minecraft.client.gui.components.EditBox m_94219_()Z # isBordered +protected net.minecraft.world.level.material.FlowingFluid m_75963_(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/world/level/material/Fluid;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/material/FluidState;)Z # canPassThrough diff --git a/src/main/resources/assets/biomancy/animations/entity/acid_blob.animation.json b/src/main/resources/assets/biomancy/animations/entity/acid_blob.animation.json new file mode 100644 index 000000000..6a88f07f5 --- /dev/null +++ b/src/main/resources/assets/biomancy/animations/entity/acid_blob.animation.json @@ -0,0 +1,6 @@ +{ + "format_version": "1.8.0", + "animations": { + }, + "geckolib_format_version": 2 +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/animations/entity/sapberry.animation.json b/src/main/resources/assets/biomancy/animations/entity/sapberry.animation.json new file mode 100644 index 000000000..6a88f07f5 --- /dev/null +++ b/src/main/resources/assets/biomancy/animations/entity/sapberry.animation.json @@ -0,0 +1,6 @@ +{ + "format_version": "1.8.0", + "animations": { + }, + "geckolib_format_version": 2 +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/geo/entity/acid_blob.geo.json b/src/main/resources/assets/biomancy/geo/entity/acid_blob.geo.json new file mode 100644 index 000000000..a119e59d1 --- /dev/null +++ b/src/main/resources/assets/biomancy/geo/entity/acid_blob.geo.json @@ -0,0 +1,109 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.biomancy:sapberry", + "texture_width": 32, + "texture_height": 32, + "visible_bounds_width": 2, + "visible_bounds_height": 2.5, + "visible_bounds_offset": [ 0, 0.75, 0 ] + }, + "bones": [ + { + "name": "root", + "pivot": [ 0, 3, 0.16667 ], + "cubes": [ + { + "origin": [ -3, 0, -5 ], + "size": [ 6, 6, 6 ], + "uv": { + "north": { + "uv": [ 0, 0 ], + "uv_size": [ 6, 6 ] + }, + "east": { + "uv": [ 0, 6 ], + "uv_size": [ 6, 6 ] + }, + "south": { + "uv": [ 6, 0 ], + "uv_size": [ 6, 6 ] + }, + "west": { + "uv": [ 6, 6 ], + "uv_size": [ 6, 6 ] + }, + "up": { + "uv": [ 0, 12 ], + "uv_size": [ 6, 6 ] + }, + "down": { + "uv": [ 12, 6 ], + "uv_size": [ 6, -6 ] + } + } + }, + { + "origin": [ -2, 1, -4.5 ], + "size": [ 4, 4, 7 ], + "uv": { + "north": { + "uv": [ 10, 17 ], + "uv_size": [ 4, 4 ] + }, + "east": { + "uv": [ 6, 12 ], + "uv_size": [ 7, 4 ] + }, + "south": { + "uv": [ 17, 10 ], + "uv_size": [ 4, 4 ] + }, + "west": { + "uv": [ 12, 6 ], + "uv_size": [ 7, 4 ] + }, + "up": { + "uv": [ 13, 10 ], + "uv_size": [ 4, 7 ] + }, + "down": { + "uv": [ 6, 23 ], + "uv_size": [ 4, -7 ] + } + } + }, + { + "origin": [ -1, 2, 2.5 ], + "size": [ 2, 2, 2 ], + "uv": { + "east": { + "uv": [ 17, 14 ], + "uv_size": [ 2, 2 ] + }, + "south": { + "uv": [ 16, 17 ], + "uv_size": [ 2, 2 ] + }, + "west": { + "uv": [ 0, 18 ], + "uv_size": [ 2, 2 ] + }, + "up": { + "uv": [ 18, 0 ], + "uv_size": [ 2, 2 ] + }, + "down": { + "uv": [ 2, 20 ], + "uv_size": [ 2, -2 ] + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/geo/entity/sapberry.geo.json b/src/main/resources/assets/biomancy/geo/entity/sapberry.geo.json new file mode 100644 index 000000000..d04dc022a --- /dev/null +++ b/src/main/resources/assets/biomancy/geo/entity/sapberry.geo.json @@ -0,0 +1,83 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.biomancy:sapberry", + "texture_width": 32, + "texture_height": 32, + "visible_bounds_width": 2, + "visible_bounds_height": 2.5, + "visible_bounds_offset": [ 0, 0.75, 0 ] + }, + "bones": [ + { + "name": "root", + "pivot": [ 0, 4, 0 ], + "cubes": [ + { + "origin": [ -4, 0, -4 ], + "size": [ 8, 8, 8 ], + "uv": { + "north": { + "uv": [ 0, 0 ], + "uv_size": [ 8, 8 ] + }, + "east": { + "uv": [ 16, 0 ], + "uv_size": [ 8, 8 ] + }, + "south": { + "uv": [ 8, 0 ], + "uv_size": [ 8, 8 ] + }, + "west": { + "uv": [ 16, 8 ], + "uv_size": [ 8, 8 ] + }, + "up": { + "uv": [ 8, 8 ], + "uv_size": [ 8, 8 ] + }, + "down": { + "uv": [ 0, 16 ], + "uv_size": [ 8, -8 ] + } + } + }, + { + "origin": [ -3, 1, -3.5 ], + "size": [ 6, 6, 7 ], + "uv": { + "north": { + "uv": [ 6, 16 ], + "uv_size": [ 6, 6 ] + }, + "east": { + "uv": [ 12, 16 ], + "uv_size": [ 7, 6 ] + }, + "south": { + "uv": [ 0, 16 ], + "uv_size": [ 6, 6 ] + }, + "west": { + "uv": [ 12, 22 ], + "uv_size": [ 7, 6 ] + }, + "up": { + "uv": [ 6, 22 ], + "uv_size": [ 6, 7 ] + }, + "down": { + "uv": [ 0, 29 ], + "uv_size": [ 6, -7 ] + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/chiseled_flesh.json b/src/main/resources/assets/biomancy/models/block/chiseled_flesh.json new file mode 100644 index 000000000..0c7609647 --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/chiseled_flesh.json @@ -0,0 +1,43 @@ +{ + "credit": "Made by Elenterius", + "parent": "block/block", + "render_type": "minecraft:solid", + "textures": { + "0": "biomancy:block/chiseld_flesh_side", + "1": "biomancy:block/chiseled_flesh_top", + "2": "biomancy:block/chiseled_flesh_bottom", + "particle": "biomancy:block/chiseld_flesh_side" + }, + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#0" + }, + "west": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#0" + }, + "up": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#1" + }, + "down": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#2" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/malignant_bloom_1.json b/src/main/resources/assets/biomancy/models/block/malignant_bloom_1.json new file mode 100644 index 000000000..b21dd4542 --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/malignant_bloom_1.json @@ -0,0 +1,152 @@ +{ + "credit": "Made by The Shroome", + "parent": "block/block", + "render_type": "minecraft:cutout", + "texture_size": [ 32, 32 ], + "textures": { + "0": "biomancy:block/malignant_bloom_1", + "particle": "biomancy:block/primal_flesh" + }, + "elements": [ + { + "name": "tentacles", + "from": [ 0, 0.01, 0 ], + "to": [ 16, 0.01, 16 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "up": { + "uv": [ 0, 0, 8, 8 ], + "texture": "#0" + }, + "down": { + "uv": [ 0, 8, 8, 0 ], + "texture": "#0" + } + } + }, + { + "name": "base", + "from": [ 5, 0, 5 ], + "to": [ 11, 2, 11 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 12, 3, 13 ], + "texture": "#0" + }, + "east": { + "uv": [ 3, 12, 6, 13 ], + "texture": "#0" + }, + "south": { + "uv": [ 9, 12, 12, 13 ], + "texture": "#0" + }, + "west": { + "uv": [ 6, 12, 9, 13 ], + "texture": "#0" + }, + "up": { + "uv": [ 3, 9, 6, 12 ], + "texture": "#0" + }, + "down": { + "uv": [ 6, 12, 9, 9 ], + "texture": "#0" + } + } + }, + { + "name": "frond1", + "from": [ 4, 2, 8 ], + "to": [ 12, 6, 8 ], + "rotation": { + "angle": 45, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 9, 0.5, 13, 2.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 9, 0.5, 13, 2.5 ], + "texture": "#0" + } + } + }, + { + "name": "frond2", + "from": [ 4, 2, 8 ], + "to": [ 12, 6, 8 ], + "rotation": { + "angle": -45, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 9, 0.5, 13, 2.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 9, 0.5, 13, 2.5 ], + "texture": "#0" + } + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [ 75, 45, 0 ], + "translation": [ 0, 1.5, 2 ], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "thirdperson_lefthand": { + "rotation": [ 75, 45, 0 ], + "translation": [ 0, 1.5, 2 ], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 45, 0 ], + "translation": [ 0, 4, 0 ], + "scale": [ 0.4, 0.4, 0.4 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 45, 0 ], + "translation": [ 0, 4, 0 ], + "scale": [ 0.4, 0.4, 0.4 ] + }, + "ground": { + "translation": [ 0, 1, 0 ], + "scale": [ 0.25, 0.25, 0.25 ] + }, + "gui": { + "rotation": [ 30, 225, 0 ], + "scale": [ 0.625, 0.625, 0.625 ] + }, + "head": { + "translation": [ 0, 14.5, 0 ] + }, + "fixed": { + "scale": [ 0.5, 0.5, 0.5 ] + } + }, + "groups": [ + { + "name": "stage1", + "origin": [ 8, 0, 40 ], + "color": 0, + "children": [ 0, 1, 2, 3 ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/malignant_bloom_2.json b/src/main/resources/assets/biomancy/models/block/malignant_bloom_2.json new file mode 100644 index 000000000..cdac0ef23 --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/malignant_bloom_2.json @@ -0,0 +1,107 @@ +{ + "credit": "Made by The Shroome", + "parent": "block/block", + "render_type": "minecraft:cutout", + "texture_size": [ 32, 32 ], + "textures": { + "2": "biomancy:block/malignant_bloom_2", + "particle": "biomancy:block/primal_flesh" + }, + "elements": [ + { + "name": "tentacles", + "from": [ 0, 0.01, 0 ], + "to": [ 16, 0.01, 16 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "up": { + "uv": [ 8, 8, 0, 0 ], + "texture": "#2" + }, + "down": { + "uv": [ 8, 0, 0, 8 ], + "texture": "#2" + } + } + }, + { + "name": "base", + "from": [ 5, 0, 5 ], + "to": [ 11, 2, 11 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 8.5, 4, 11.5, 5 ], + "texture": "#2" + }, + "east": { + "uv": [ 12.5, 10, 15.5, 11 ], + "texture": "#2" + }, + "south": { + "uv": [ 8.5, 10, 11.5, 11 ], + "texture": "#2" + }, + "west": { + "uv": [ 12.5, 4, 15.5, 5 ], + "texture": "#2" + }, + "down": { + "uv": [ 15.5, 12.5, 12.5, 15.5 ], + "texture": "#2" + } + } + }, + { + "from": [ 4, 2, 4 ], + "to": [ 12, 10, 12 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 0, 12, 4 ], + "texture": "#2" + }, + "east": { + "uv": [ 12, 6, 16, 10 ], + "texture": "#2" + }, + "south": { + "uv": [ 8, 6, 12, 10 ], + "texture": "#2" + }, + "west": { + "uv": [ 12, 0, 16, 4 ], + "texture": "#2" + }, + "up": { + "uv": [ 12, 16, 8, 12 ], + "texture": "#2" + }, + "down": { + "uv": [ 16, 12, 12, 16 ], + "texture": "#2" + } + } + } + ], + "groups": [ + { + "name": "stage2", + "origin": [ 8, 0, -8 ], + "color": 0, + "children": [ 0, 1, 2 ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/malignant_bloom_3.json b/src/main/resources/assets/biomancy/models/block/malignant_bloom_3.json new file mode 100644 index 000000000..5e35e7249 --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/malignant_bloom_3.json @@ -0,0 +1,111 @@ +{ + "credit": "Made by The Shroome", + "parent": "block/block", + "render_type": "minecraft:cutout", + "texture_size": [ 48, 48 ], + "textures": { + "0": "biomancy:block/malignant_bloom_3", + "particle": "biomancy:block/primal_flesh" + }, + "elements": [ + { + "name": "tentacles", + "from": [ 0, 0.01, 0 ], + "to": [ 16, 0.01, 16 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "up": { + "uv": [ 5.33333, 5.33333, 0, 0 ], + "texture": "#0" + }, + "down": { + "uv": [ 5.33333, 0, 0, 5.33333 ], + "texture": "#0" + } + } + }, + { + "name": "base", + "from": [ 5, 0, 5 ], + "to": [ 11, 2, 11 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 7.33333, 2, 8 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 8, 2, 8.66667 ], + "texture": "#0" + }, + "south": { + "uv": [ 2, 8, 4, 8.66667 ], + "texture": "#0" + }, + "west": { + "uv": [ 2, 7.33333, 4, 8 ], + "texture": "#0" + }, + "up": { + "uv": [ 2, 7.33333, 0, 5.33333 ], + "texture": "#0" + }, + "down": { + "uv": [ 4, 5.33333, 2, 7.33333 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 2, 2 ], + "to": [ 14, 15, 14 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 5.33333, 4, 9.33333, 8.33333 ], + "texture": "#0" + }, + "east": { + "uv": [ 5.33333, 4, 9.33333, 8.33333 ], + "texture": "#0" + }, + "south": { + "uv": [ 5.33333, 4, 9.33333, 8.33333 ], + "texture": "#0" + }, + "west": { + "uv": [ 5.33333, 4, 9.33333, 8.33333 ], + "texture": "#0" + }, + "up": { + "uv": [ 5.33333, 0, 9.33333, 4 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.33333, 0, 13.33333, 4 ], + "texture": "#0" + } + } + } + ], + "groups": [ + { + "name": "stage3", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 0, 1, 2 ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/malignant_bloom_4.json b/src/main/resources/assets/biomancy/models/block/malignant_bloom_4.json new file mode 100644 index 000000000..3a831feb8 --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/malignant_bloom_4.json @@ -0,0 +1,786 @@ +{ + "credit": "Made by The Shroome", + "parent": "block/block", + "render_type": "minecraft:cutout", + "texture_size": [ 64, 64 ], + "textures": { + "0": "biomancy:block/malignant_bloom_4", + "particle": "biomancy:block/primal_flesh" + }, + "elements": [ + { + "name": "tentacles", + "from": [ 0, 0.01, 0 ], + "to": [ 16, 0.01, 16 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "up": { + "uv": [ 4, 4, 0, 0 ], + "texture": "#0" + }, + "down": { + "uv": [ 4, 0, 0, 4 ], + "texture": "#0" + } + } + }, + { + "name": "berry overlay", + "from": [ 4, 5, 4 ], + "to": [ 12, 13, 12 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 9, 8 ] + }, + "faces": { + "north": { + "uv": [ 7.5, 2, 9.5, 4 ], + "texture": "#0" + }, + "east": { + "uv": [ 5.5, 2, 7.5, 4 ], + "texture": "#0" + }, + "south": { + "uv": [ 11.5, 2, 13.5, 4 ], + "texture": "#0" + }, + "west": { + "uv": [ 9.5, 2, 11.5, 4 ], + "texture": "#0" + }, + "up": { + "uv": [ 9.5, 2, 7.5, 0 ], + "texture": "#0" + }, + "down": { + "uv": [ 11.5, 0, 9.5, 2 ], + "texture": "#0" + } + } + }, + { + "name": "berry", + "from": [ 5, 5, 5 ], + "to": [ 11, 12, 11 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 8.5, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 6, 9.5, 7.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.5, 6, 8, 7.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 11, 6, 12.5, 7.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 9.5, 6, 11, 7.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 9.5, 6, 8, 4.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 11, 4.5, 9.5, 6 ], + "texture": "#0" + } + } + }, + { + "name": "frond1", + "from": [ 4, 2, 8 ], + "to": [ 12, 6, 8 ], + "rotation": { + "angle": 45, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 4.75, 2, 5.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 2, 4.75, 0, 5.75 ], + "texture": "#0" + } + } + }, + { + "name": "frond2", + "from": [ 4, 2, 8 ], + "to": [ 12, 6, 8 ], + "rotation": { + "angle": -45, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 2, 4.75, 0, 5.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 4.75, 2, 5.75 ], + "texture": "#0" + } + } + }, + { + "name": "base", + "from": [ 5, 0, 5 ], + "to": [ 11, 2, 11 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 1.5, 7.75, 3, 8.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 7.75, 1.5, 8.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 4.5, 7.75, 6, 8.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 3, 7.75, 4.5, 8.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 3, 7.75, 1.5, 6.25 ], + "texture": "#0" + }, + "down": { + "uv": [ 4.5, 6.25, 3, 7.75 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 2, 8 ], + "to": [ 14, 3, 14 ], + "rotation": { + "angle": -22.5, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 11, 12.5, 9.5, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.5, 12.5, 5, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 8, 12.5, 6.5, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 9.5, 12.5, 8, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 8, 11, 6.5, 12.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.5, 12.5, 8, 11 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 14, 8 ], + "to": [ 14, 15, 14 ], + "rotation": { + "angle": -22.5, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 11, 10.5, 9.5, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.5, 10.5, 5, 10.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 8, 10.5, 6.5, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 9.5, 10.5, 8, 10.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 8, 9, 6.5, 10.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.5, 10.5, 8, 9 ], + "texture": "#0" + } + } + }, + { + "from": [ 13, 3, 8 ], + "to": [ 14, 14, 14 ], + "rotation": { + "angle": -22.5, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 3.5, 10.5, 3.25, 13.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 1.5, 10.5, 0, 13.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 1.75, 10.5, 1.5, 13.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 3.25, 10.5, 1.75, 13.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 1.75, 9, 1.5, 10.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 2, 10.5, 1.75, 9 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 3, 13 ], + "to": [ 13, 14, 14 ], + "rotation": { + "angle": -22.5, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 15.75, 10, 14.5, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 13, 10, 12.75, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 14.25, 12.75, 13, 10 ], + "texture": "#0" + }, + "west": { + "uv": [ 14.5, 10, 14.25, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 14.25, 9.75, 13, 10 ], + "texture": "#0" + }, + "down": { + "uv": [ 15.5, 10, 14.25, 9.75 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 2, 2 ], + "to": [ 14, 3, 8 ], + "rotation": { + "angle": -22.5, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 6.5, 12.5, 8, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 5, 12.5, 6.5, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 9.5, 12.5, 11, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 8, 12.5, 9.5, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 8, 12.5, 6.5, 11 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.5, 11, 8, 12.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 14, 2 ], + "to": [ 14, 15, 8 ], + "rotation": { + "angle": -22.5, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 6.5, 10.5, 8, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 5, 10.5, 6.5, 10.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 9.5, 10.5, 11, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 8, 10.5, 9.5, 10.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 8, 10.5, 6.5, 9 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.5, 9, 8, 10.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 13, 3, 2 ], + "to": [ 14, 14, 8 ], + "rotation": { + "angle": -22.5, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 1.5, 10.5, 1.75, 13.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 13.25, 1.5, 10.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 3.25, 10.5, 3.5, 13.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 1.75, 10.5, 3.25, 13.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 1.75, 10.5, 1.5, 9 ], + "texture": "#0" + }, + "down": { + "uv": [ 2, 9, 1.75, 10.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 3, 2 ], + "to": [ 13, 14, 3 ], + "rotation": { + "angle": -22.5, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 13, 10, 14.25, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 12.75, 10, 13, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 14.5, 10, 15.75, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 14.25, 10, 14.5, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 14.25, 10, 13, 9.75 ], + "texture": "#0" + }, + "down": { + "uv": [ 15.5, 9.75, 14.25, 10 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 2, 2 ], + "to": [ 8, 3, 8 ], + "rotation": { + "angle": 22.5, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 12.5, 6.5, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 9.5, 12.5, 8, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 11, 12.5, 9.5, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 6.5, 12.5, 5, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.5, 12.5, 8, 11 ], + "texture": "#0" + }, + "down": { + "uv": [ 8, 11, 9.5, 12.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 14, 2 ], + "to": [ 8, 15, 8 ], + "rotation": { + "angle": 22.5, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 10.5, 6.5, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 9.5, 10.5, 8, 10.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 11, 10.5, 9.5, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 6.5, 10.5, 5, 10.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.5, 10.5, 8, 9 ], + "texture": "#0" + }, + "down": { + "uv": [ 8, 9, 9.5, 10.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 3, 2 ], + "to": [ 3, 14, 8 ], + "rotation": { + "angle": 22.5, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 1.75, 10.5, 1.5, 13.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 3.25, 10.5, 1.75, 13.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 3.5, 10.5, 3.25, 13.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 1.5, 10.5, 0, 13.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 1.5, 10.5, 1.75, 9 ], + "texture": "#0" + }, + "down": { + "uv": [ 1.75, 9, 2, 10.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 3, 3, 2 ], + "to": [ 8, 14, 3 ], + "rotation": { + "angle": 22.5, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 14.25, 12.75, 13, 10 ], + "texture": "#0" + }, + "east": { + "uv": [ 14.5, 10, 14.25, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 15.75, 10, 14.5, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 13, 10, 12.75, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 13, 10, 14.25, 9.75 ], + "texture": "#0" + }, + "down": { + "uv": [ 14.25, 9.75, 15.5, 10 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 2, 8 ], + "to": [ 8, 3, 14 ], + "rotation": { + "angle": 22.5, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 9.5, 12.5, 11, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 8, 12.5, 9.5, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 6.5, 12.5, 8, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 5, 12.5, 6.5, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.5, 11, 8, 12.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 8, 12.5, 9.5, 11 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 14, 8 ], + "to": [ 8, 15, 14 ], + "rotation": { + "angle": 22.5, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 9.5, 10.5, 11, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 8, 10.5, 9.5, 10.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 6.5, 10.5, 8, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 5, 10.5, 6.5, 10.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.5, 9, 8, 10.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 8, 10.5, 9.5, 9 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 3, 8 ], + "to": [ 3, 14, 14 ], + "rotation": { + "angle": 22.5, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 3.25, 10.5, 3.5, 13.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 1.75, 10.5, 3.25, 13.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 1.5, 10.5, 1.75, 13.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 0, 13.25, 1.5, 10.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 1.5, 9, 1.75, 10.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 1.75, 10.5, 2, 9 ], + "texture": "#0" + } + } + }, + { + "from": [ 3, 3, 13 ], + "to": [ 8, 14, 14 ], + "rotation": { + "angle": 22.5, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 14.5, 10, 15.75, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 14.25, 10, 14.5, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 13, 10, 14.25, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 12.75, 10, 13, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 13, 9.75, 14.25, 10 ], + "texture": "#0" + }, + "down": { + "uv": [ 14.25, 10, 15.5, 9.75 ], + "texture": "#0" + } + } + } + ], + "groups": [ + { + "name": "stage3", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ + 0, + 1, + 2, + 3, + 4, + 5, + { + "name": "mouthbits", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ + { + "name": "mouthcorner4", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 6, 7, 8, 9 ] + }, + { + "name": "mouthcorner", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 10, 11, 12, 13 ] + }, + { + "name": "mouthcorner2", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 14, 15, 16, 17 ] + }, + { + "name": "mouthcorner3", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 18, 19, 20, 21 ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/malignant_bloom_5.json b/src/main/resources/assets/biomancy/models/block/malignant_bloom_5.json new file mode 100644 index 000000000..45cfeb0d6 --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/malignant_bloom_5.json @@ -0,0 +1,802 @@ +{ + "credit": "Made by The Shroome", + "parent": "block/block", + "render_type": "minecraft:cutout", + "texture_size": [ 64, 64 ], + "textures": { + "0": "biomancy:block/malignant_bloom_4", + "particle": "biomancy:block/primal_flesh" + }, + "elements": [ + { + "name": "tentacles", + "from": [ 0, 0.01, 0 ], + "to": [ 16, 0.01, 16 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "up": { + "uv": [ 4, 4, 0, 0 ], + "texture": "#0" + }, + "down": { + "uv": [ 4, 0, 0, 4 ], + "texture": "#0" + } + } + }, + { + "name": "berry overlay", + "from": [ 4, 5, 4 ], + "to": [ 12, 13, 12 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 9, 8 ] + }, + "faces": { + "north": { + "uv": [ 7.5, 2, 9.5, 4 ], + "texture": "#0" + }, + "east": { + "uv": [ 5.5, 2, 7.5, 4 ], + "texture": "#0" + }, + "south": { + "uv": [ 11.5, 2, 13.5, 4 ], + "texture": "#0" + }, + "west": { + "uv": [ 9.5, 2, 11.5, 4 ], + "texture": "#0" + }, + "up": { + "uv": [ 9.5, 2, 7.5, 0 ], + "texture": "#0" + }, + "down": { + "uv": [ 11.5, 0, 9.5, 2 ], + "texture": "#0" + } + } + }, + { + "name": "berry", + "from": [ 5, 5, 5 ], + "to": [ 11, 12, 11 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 8.5, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 6, 9.5, 7.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.5, 6, 8, 7.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 11, 6, 12.5, 7.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 9.5, 6, 11, 7.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 9.5, 6, 8, 4.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 11, 4.5, 9.5, 6 ], + "texture": "#0" + } + } + }, + { + "name": "frond1", + "from": [ 4, 2, 8 ], + "to": [ 12, 6, 8 ], + "rotation": { + "angle": 45, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 4.75, 2, 5.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 2, 4.75, 0, 5.75 ], + "texture": "#0" + } + } + }, + { + "name": "frond2", + "from": [ 4, 2, 8 ], + "to": [ 12, 6, 8 ], + "rotation": { + "angle": -45, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 2, 4.75, 0, 5.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 4.75, 2, 5.75 ], + "texture": "#0" + } + } + }, + { + "name": "base", + "from": [ 5, 0, 5 ], + "to": [ 11, 2, 11 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 0, 8 ] + }, + "faces": { + "north": { + "uv": [ 1.5, 7.75, 3, 8.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 7.75, 1.5, 8.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 4.5, 7.75, 6, 8.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 3, 7.75, 4.5, 8.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 3, 7.75, 1.5, 6.25 ], + "texture": "#0" + }, + "down": { + "uv": [ 4.5, 6.25, 3, 7.75 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 2, 8 ], + "to": [ 14, 3, 14 ], + "rotation": { + "angle": -45, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 11, 12.5, 9.5, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.5, 12.5, 5, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 8, 12.5, 6.5, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 9.5, 12.5, 8, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 8, 11, 6.5, 12.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.5, 12.5, 8, 11 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 14, 8 ], + "to": [ 14, 15, 14 ], + "rotation": { + "angle": -45, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 11, 10.5, 9.5, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.5, 10.5, 5, 10.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 8, 10.5, 6.5, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 9.5, 10.5, 8, 10.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 8, 9, 6.5, 10.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.5, 10.5, 8, 9 ], + "texture": "#0" + } + } + }, + { + "from": [ 13, 3, 8 ], + "to": [ 14, 14, 14 ], + "rotation": { + "angle": -45, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 3.5, 10.5, 3.25, 13.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 1.5, 10.5, 0, 13.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 1.75, 10.5, 1.5, 13.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 3.25, 10.5, 1.75, 13.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 1.75, 9, 1.5, 10.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 2, 10.5, 1.75, 9 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 3, 13 ], + "to": [ 13, 14, 14 ], + "rotation": { + "angle": -45, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 15.75, 10, 14.5, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 13, 10, 12.75, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 14.25, 12.75, 13, 10 ], + "texture": "#0" + }, + "west": { + "uv": [ 14.5, 10, 14.25, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 14.25, 9.75, 13, 10 ], + "texture": "#0" + }, + "down": { + "uv": [ 15.5, 10, 14.25, 9.75 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 2, 2 ], + "to": [ 14, 3, 8 ], + "rotation": { + "angle": -45, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 6.5, 12.5, 8, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 5, 12.5, 6.5, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 9.5, 12.5, 11, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 8, 12.5, 9.5, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 8, 12.5, 6.5, 11 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.5, 11, 8, 12.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 14, 2 ], + "to": [ 14, 15, 8 ], + "rotation": { + "angle": -45, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 6.5, 10.5, 8, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 5, 10.5, 6.5, 10.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 9.5, 10.5, 11, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 8, 10.5, 9.5, 10.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 8, 10.5, 6.5, 9 ], + "texture": "#0" + }, + "down": { + "uv": [ 9.5, 9, 8, 10.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 13, 3, 2 ], + "to": [ 14, 14, 8 ], + "rotation": { + "angle": -45, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 1.5, 10.5, 1.75, 13.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 13.25, 1.5, 10.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 3.25, 10.5, 3.5, 13.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 1.75, 10.5, 3.25, 13.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 1.75, 10.5, 1.5, 9 ], + "texture": "#0" + }, + "down": { + "uv": [ 2, 9, 1.75, 10.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 8, 3, 2 ], + "to": [ 13, 14, 3 ], + "rotation": { + "angle": -45, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 13, 10, 14.25, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 12.75, 10, 13, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 14.5, 10, 15.75, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 14.25, 10, 14.5, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 14.25, 10, 13, 9.75 ], + "texture": "#0" + }, + "down": { + "uv": [ 15.5, 9.75, 14.25, 10 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 2, 2 ], + "to": [ 8, 3, 8 ], + "rotation": { + "angle": 45, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 12.5, 6.5, 12.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 9.5, 12.5, 8, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 11, 12.5, 9.5, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 6.5, 12.5, 5, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.5, 12.5, 8, 11 ], + "texture": "#0" + }, + "down": { + "uv": [ 8, 11, 9.5, 12.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 14, 2 ], + "to": [ 8, 15, 8 ], + "rotation": { + "angle": 45, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 10.5, 6.5, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 9.5, 10.5, 8, 10.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 11, 10.5, 9.5, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 6.5, 10.5, 5, 10.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.5, 10.5, 8, 9 ], + "texture": "#0" + }, + "down": { + "uv": [ 8, 9, 9.5, 10.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, 3, 2 ], + "to": [ 3, 14, 8 ], + "rotation": { + "angle": 45, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 1.75, 10.5, 1.5, 13.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 3.25, 10.5, 1.75, 13.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 3.5, 10.5, 3.25, 13.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 1.5, 10.5, 0, 13.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 1.5, 10.5, 1.75, 9 ], + "texture": "#0" + }, + "down": { + "uv": [ 1.75, 9, 2, 10.5 ], + "texture": "#0" + } + } + }, + { + "from": [ 3, 3, 2 ], + "to": [ 8, 14, 3 ], + "rotation": { + "angle": 45, + "axis": "z", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 14.25, 12.75, 13, 10 ], + "texture": "#0" + }, + "east": { + "uv": [ 14.5, 10, 14.25, 12.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 15.75, 10, 14.5, 12.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 13, 10, 12.75, 12.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 13, 10, 14.25, 9.75 ], + "texture": "#0" + }, + "down": { + "uv": [ 14.25, 9.75, 15.5, 10 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, -4, 8 ], + "to": [ 8, 2, 9 ], + "rotation": { + "angle": -45, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 12.5, 9.5, 11 ], + "rotation": 180, + "texture": "#0" + }, + "east": { + "uv": [ 8, 12.5, 9.5, 12.75 ], + "rotation": 270, + "texture": "#0" + }, + "south": { + "uv": [ 6.5, 11, 8, 12.5 ], + "texture": "#0" + }, + "west": { + "uv": [ 5, 12.5, 6.5, 12.75 ], + "rotation": 90, + "texture": "#0" + }, + "up": { + "uv": [ 9.5, 12.5, 11, 12.75 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 6.5, 12.5, 8, 12.75 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, -4, 20 ], + "to": [ 8, 2, 21 ], + "rotation": { + "angle": -45, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 8, 10.5, 9.5, 9 ], + "rotation": 180, + "texture": "#0" + }, + "east": { + "uv": [ 8, 10.5, 9.5, 10.75 ], + "rotation": 270, + "texture": "#0" + }, + "south": { + "uv": [ 6.5, 9, 8, 10.5 ], + "texture": "#0" + }, + "west": { + "uv": [ 5, 10.5, 6.5, 10.75 ], + "rotation": 90, + "texture": "#0" + }, + "up": { + "uv": [ 9.5, 10.5, 11, 10.75 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 6.5, 10.5, 8, 10.75 ], + "texture": "#0" + } + } + }, + { + "from": [ 2, -4, 9 ], + "to": [ 3, 2, 20 ], + "rotation": { + "angle": -45, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 1.75, 10.5, 2, 9 ], + "rotation": 180, + "texture": "#0" + }, + "east": { + "uv": [ 1.75, 10.5, 3.25, 13.25 ], + "rotation": 270, + "texture": "#0" + }, + "south": { + "uv": [ 1.5, 9, 1.75, 10.5 ], + "texture": "#0" + }, + "west": { + "uv": [ 0, 13.25, 1.5, 10.5 ], + "rotation": 90, + "texture": "#0" + }, + "up": { + "uv": [ 3.25, 10.5, 3.5, 13.25 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 1.5, 10.5, 1.75, 13.25 ], + "texture": "#0" + } + } + }, + { + "from": [ 3, -4, 9 ], + "to": [ 8, -3, 20 ], + "rotation": { + "angle": -45, + "axis": "x", + "origin": [ 8, 2, 8 ] + }, + "faces": { + "north": { + "uv": [ 14.25, 10, 15.5, 9.75 ], + "rotation": 180, + "texture": "#0" + }, + "east": { + "uv": [ 14.25, 10, 14.5, 12.75 ], + "rotation": 270, + "texture": "#0" + }, + "south": { + "uv": [ 13, 9.75, 14.25, 10 ], + "texture": "#0" + }, + "west": { + "uv": [ 12.75, 10, 13, 12.75 ], + "rotation": 90, + "texture": "#0" + }, + "up": { + "uv": [ 14.5, 10, 15.75, 12.75 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 13, 10, 14.25, 12.75 ], + "texture": "#0" + } + } + } + ], + "groups": [ + { + "name": "stage3", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ + 0, + 1, + 2, + 3, + 4, + 5, + { + "name": "mouthbits", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ + { + "name": "mouthcorner4", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 6, 7, 8, 9 ] + }, + { + "name": "mouthcorner", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 10, 11, 12, 13 ] + }, + { + "name": "mouthcorner2", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 14, 15, 16, 17 ] + }, + { + "name": "mouthcorner3", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ 18, 19, 20, 21 ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/neural_interceptor.json b/src/main/resources/assets/biomancy/models/block/neural_interceptor.json new file mode 100644 index 000000000..ab671424e --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/neural_interceptor.json @@ -0,0 +1,691 @@ +{ + "credit": "Made by Elenterius/RhinoW", + "parent": "block/block", + "render_type": "minecraft:translucent", + "texture_size": [ 64, 64 ], + "textures": { + "0": "biomancy:block/neural_interceptor", + "particle": "biomancy:block/primal_flesh" + }, + "elements": [ + { + "from": [ 2, 0, 2 ], + "to": [ 14, 5, 14 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 0, 3, 1.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 0, 3, 1.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 3, 1.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 0, 0, 3, 1.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 3, 4.25, 0, 1.25 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 3, 4.25, 0, 7.25 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 12, 5, 12 ], + "to": [ 14, 8, 14 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 7.25, 0.5, 7.75, 1.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.75, 0.5, 7.25, 1.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 6.25, 0.5, 6.75, 1.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 5.75, 0.5, 6.25, 1.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.75, 0.5, 6.25, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 7.25, 0, 6.75, 0.5 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 2, 5, 12 ], + "to": [ 4, 8, 14 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 7.75, 0.5, 7.25, 1.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.25, 0.5, 5.75, 1.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 6.75, 0.5, 6.25, 1.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 7.25, 0.5, 6.75, 1.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.25, 0.5, 6.75, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 6.75, 0, 7.25, 0.5 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 12, 5, 2 ], + "to": [ 14, 8, 4 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 6.75, 0.5, 6.25, 1.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 7.25, 0.5, 6.75, 1.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 7.75, 0.5, 7.25, 1.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 6.25, 0.5, 5.75, 1.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.75, 0, 6.25, 0.5 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 7.25, 0.5, 6.75, 0 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 2, 5, 2 ], + "to": [ 4, 8, 4 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 6.25, 0.5, 6.75, 1.25 ], + "texture": "#0" + }, + "east": { + "uv": [ 5.75, 0.5, 6.25, 1.25 ], + "texture": "#0" + }, + "south": { + "uv": [ 7.25, 0.5, 7.75, 1.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 6.75, 0.5, 7.25, 1.25 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.25, 0, 6.75, 0.5 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 6.75, 0.5, 7.25, 0 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 8, 0, 16.48528 ], + "to": [ 8, 8, 19.48528 ], + "rotation": { + "angle": 45, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "east": { + "uv": [ 5, 5.5, 5.75, 7.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "west": { + "uv": [ 5.75, 5.5, 5, 7.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 8, 0, 16.48528 ], + "to": [ 8, 8, 19.48528 ], + "rotation": { + "angle": -45, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "east": { + "uv": [ 5, 5.5, 5.75, 7.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "west": { + "uv": [ 5.75, 5.5, 5, 7.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 8, 0, -3.48528 ], + "to": [ 8, 8, -0.48528 ], + "rotation": { + "angle": 45, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "east": { + "uv": [ 5.75, 5.5, 5, 7.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "west": { + "uv": [ 5, 5.5, 5.75, 7.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 8, 0, -3.48528 ], + "to": [ 8, 8, -0.48528 ], + "rotation": { + "angle": -45, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "east": { + "uv": [ 5.75, 5.5, 5, 7.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "west": { + "uv": [ 5, 5.5, 5.75, 7.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 8, 4, 13.07107 ], + "to": [ 8, 15, 18.07107 ], + "rotation": { + "angle": 45, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "east": { + "uv": [ 3.5, 5.75, 4.75, 8.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "west": { + "uv": [ 4.75, 5.75, 3.5, 8.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 8, 4, 13.07107 ], + "to": [ 8, 15, 18.07107 ], + "rotation": { + "angle": -45, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "east": { + "uv": [ 3.5, 5.75, 4.75, 8.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "west": { + "uv": [ 4.75, 5.75, 3.5, 8.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 0, 0, 0, 0 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 3, 4, 3 ], + "to": [ 13, 8, 13 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 5.5, 3.75, 8, 4.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 5.5, 3.75, 3, 4.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 3, 3.75, 5.5, 4.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 3, 3.75, 5.5, 4.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 5.5, 3.75, 3, 1.25 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 8, 1.25, 5.5, 3.75 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 0, 0.01, 0 ], + "to": [ 16, 0.01, 16 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 5.3, 8 ] + }, + "faces": { + "north": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "west": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + }, + "up": { + "uv": [ 12.25, 4, 8.25, 0 ], + "rotation": 180, + "texture": "#0" + }, + "down": { + "uv": [ 12.25, 0, 8.25, 4 ], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "from": [ 3, 8, 3 ], + "to": [ 13, 17, 13 ], + "faces": { + "north": { + "uv": [ 8.25, 8.5, 10.75, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 6.75, 10.25, 9.25, 12.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 6.75, 8.5, 9.25, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 8.25, 10.25, 10.75, 12.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 6.75, 8.5, 9.25, 11 ], + "texture": "#0" + }, + "down": { + "uv": [ 0, 0, 0, 0 ], + "texture": "#0" + } + } + }, + { + "from": [ 4, 9, 4 ], + "to": [ 7, 16, 12 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 5, 9 ] + }, + "faces": { + "north": { + "uv": [ 11.5, 9, 12.25, 10.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 13, 9, 15, 10.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 13.75, 10, 14.5, 11.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 12.25, 10.25, 14.25, 12 ], + "texture": "#0" + }, + "up": { + "uv": [ 12.25, 9.25, 13, 11.25 ], + "texture": "#0" + }, + "down": { + "uv": [ 12.75, 9.75, 13.5, 11.75 ], + "texture": "#0" + } + } + }, + { + "from": [ 9, 9, 4 ], + "to": [ 12, 16, 12 ], + "rotation": { + "angle": 0, + "axis": "z", + "origin": [ 10.5, 12.5, 8 ] + }, + "faces": { + "north": { + "uv": [ 14.25, 9.25, 15, 11 ], + "texture": "#0" + }, + "east": { + "uv": [ 11.75, 9.75, 13.75, 11.5 ], + "texture": "#0" + }, + "south": { + "uv": [ 12.5, 10.5, 13.25, 12.25 ], + "texture": "#0" + }, + "west": { + "uv": [ 13.25, 10.75, 15.25, 12.5 ], + "texture": "#0" + }, + "up": { + "uv": [ 12, 8.5, 12.75, 10.5 ], + "texture": "#0" + }, + "down": { + "uv": [ 13.75, 9, 14.5, 11 ], + "texture": "#0" + } + } + }, + { + "from": [ 6, 8, 6 ], + "to": [ 10, 11, 10 ], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [ 8, 4, 8.5 ] + }, + "faces": { + "north": { + "uv": [ 13.25, 9, 14.25, 9.75 ], + "texture": "#0" + }, + "east": { + "uv": [ 11.75, 9, 12.75, 9.75 ], + "texture": "#0" + }, + "south": { + "uv": [ 13.25, 10, 14.25, 10.75 ], + "texture": "#0" + }, + "west": { + "uv": [ 11.75, 10, 12.75, 10.75 ], + "texture": "#0" + }, + "up": { + "uv": [ 13.5, 11, 14.5, 12 ], + "texture": "#0" + }, + "down": { + "uv": [ 12, 11, 13, 12 ], + "texture": "#0" + } + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [ 75, 45, 0 ], + "translation": [ 0, 2.5, 0 ], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "thirdperson_lefthand": { + "rotation": [ 75, 45, 0 ], + "translation": [ 0, 2.5, 0 ], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 45, 0 ], + "scale": [ 0.4, 0.4, 0.4 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 225, 0 ], + "scale": [ 0.4, 0.4, 0.4 ] + }, + "ground": { + "translation": [ 0, 3, 0 ], + "scale": [ 0.25, 0.25, 0.25 ] + }, + "gui": { + "rotation": [ 30, 225, 0 ], + "scale": [ 0.625, 0.625, 0.625 ] + }, + "head": { + "translation": [ 0, 6.25, 0 ] + }, + "fixed": { + "scale": [ 0.5, 0.5, 0.5 ] + } + }, + "groups": [ + { + "name": "root", + "origin": [ 8, 0, 8 ], + "color": 0, + "children": [ + { + "name": "base", + "origin": [ 8, 8, 8 ], + "color": 0, + "children": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ] + }, + { + "name": "neural", + "origin": [ 8, 8, 8 ], + "color": 0, + "children": [ + 13, + { + "name": "brain", + "origin": [ 8, 8, 8 ], + "color": 0, + "children": [ 14, 15, 16 ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/primal_orifice.json b/src/main/resources/assets/biomancy/models/block/primal_orifice.json new file mode 100644 index 000000000..94c336299 --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/primal_orifice.json @@ -0,0 +1,43 @@ +{ + "credit": "Made by Elenterius", + "parent": "block/block", + "render_type": "minecraft:solid", + "textures": { + "0": "biomancy:block/primal_orifice", + "1": "biomancy:block/primal_flesh", + "2": "biomancy:block/primal_orifice_2", + "particle": "biomancy:block/primal_orifice" + }, + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#0" + }, + "east": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#0" + }, + "south": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#0" + }, + "west": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#0" + }, + "up": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#1" + }, + "down": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#2" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/models/block/primal_orifice_leaking.json b/src/main/resources/assets/biomancy/models/block/primal_orifice_leaking.json new file mode 100644 index 000000000..37ad84e3e --- /dev/null +++ b/src/main/resources/assets/biomancy/models/block/primal_orifice_leaking.json @@ -0,0 +1,43 @@ +{ + "credit": "Made by Elenterius", + "parent": "block/block", + "render_type": "minecraft:solid", + "textures": { + "1": "biomancy:block/primal_flesh", + "3": "biomancy:block/primal_orifice_leaking", + "4": "biomancy:block/primal_orifice_2_full", + "particle": "biomancy:block/primal_orifice" + }, + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#3" + }, + "east": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#3" + }, + "south": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#3" + }, + "west": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#3" + }, + "up": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#1" + }, + "down": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#4" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/biomancy/textures/block/bloomlight.png b/src/main/resources/assets/biomancy/textures/block/bloomlight.png new file mode 100644 index 000000000..55ea6a035 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/bloomlight.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/chiseld_flesh_side.png b/src/main/resources/assets/biomancy/textures/block/chiseld_flesh_side.png new file mode 100644 index 000000000..089dfbb53 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/chiseld_flesh_side.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/chiseled_flesh_bottom.png b/src/main/resources/assets/biomancy/textures/block/chiseled_flesh_bottom.png new file mode 100644 index 000000000..637fd22a1 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/chiseled_flesh_bottom.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/chiseled_flesh.png b/src/main/resources/assets/biomancy/textures/block/chiseled_flesh_top.png similarity index 75% rename from src/main/resources/assets/biomancy/textures/block/chiseled_flesh.png rename to src/main/resources/assets/biomancy/textures/block/chiseled_flesh_top.png index 40639df87..4dd5e7286 100644 Binary files a/src/main/resources/assets/biomancy/textures/block/chiseled_flesh.png and b/src/main/resources/assets/biomancy/textures/block/chiseled_flesh_top.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/flesh_eye_enemy.png b/src/main/resources/assets/biomancy/textures/block/flesh_eye_enemy.png deleted file mode 100644 index c5d3e3486..000000000 Binary files a/src/main/resources/assets/biomancy/textures/block/flesh_eye_enemy.png and /dev/null differ diff --git a/src/main/resources/assets/biomancy/textures/block/flesh_eye_friend.png b/src/main/resources/assets/biomancy/textures/block/flesh_eye_friend.png deleted file mode 100644 index a2f8a2e05..000000000 Binary files a/src/main/resources/assets/biomancy/textures/block/flesh_eye_friend.png and /dev/null differ diff --git a/src/main/resources/assets/biomancy/textures/block/flesh_mouth.png b/src/main/resources/assets/biomancy/textures/block/flesh_mouth.png deleted file mode 100644 index 35eed94d2..000000000 Binary files a/src/main/resources/assets/biomancy/textures/block/flesh_mouth.png and /dev/null differ diff --git a/src/main/resources/assets/biomancy/textures/block/flesh_overlay.png b/src/main/resources/assets/biomancy/textures/block/flesh_overlay.png deleted file mode 100644 index 89cba6095..000000000 Binary files a/src/main/resources/assets/biomancy/textures/block/flesh_overlay.png and /dev/null differ diff --git a/src/main/resources/assets/biomancy/textures/block/malignant_bloom_1.png b/src/main/resources/assets/biomancy/textures/block/malignant_bloom_1.png new file mode 100644 index 000000000..5cedca472 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/malignant_bloom_1.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/malignant_bloom_2.png b/src/main/resources/assets/biomancy/textures/block/malignant_bloom_2.png new file mode 100644 index 000000000..ae28131a5 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/malignant_bloom_2.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/malignant_bloom_3.png b/src/main/resources/assets/biomancy/textures/block/malignant_bloom_3.png new file mode 100644 index 000000000..d574e0523 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/malignant_bloom_3.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/malignant_bloom_4.png b/src/main/resources/assets/biomancy/textures/block/malignant_bloom_4.png new file mode 100644 index 000000000..9fe557dcb Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/malignant_bloom_4.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/neural_interceptor.png b/src/main/resources/assets/biomancy/textures/block/neural_interceptor.png new file mode 100644 index 000000000..a4b01d0d5 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/neural_interceptor.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/flesh_eye_enemy_active.png b/src/main/resources/assets/biomancy/textures/block/primal_orifice.png similarity index 82% rename from src/main/resources/assets/biomancy/textures/block/flesh_eye_enemy_active.png rename to src/main/resources/assets/biomancy/textures/block/primal_orifice.png index 5efff0d6a..99e0f13e2 100644 Binary files a/src/main/resources/assets/biomancy/textures/block/flesh_eye_enemy_active.png and b/src/main/resources/assets/biomancy/textures/block/primal_orifice.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/flesh_eye_friend_active.png b/src/main/resources/assets/biomancy/textures/block/primal_orifice_2.png similarity index 81% rename from src/main/resources/assets/biomancy/textures/block/flesh_eye_friend_active.png rename to src/main/resources/assets/biomancy/textures/block/primal_orifice_2.png index 0868baf39..82b1007e4 100644 Binary files a/src/main/resources/assets/biomancy/textures/block/flesh_eye_friend_active.png and b/src/main/resources/assets/biomancy/textures/block/primal_orifice_2.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/primal_orifice_2_full.png b/src/main/resources/assets/biomancy/textures/block/primal_orifice_2_full.png new file mode 100644 index 000000000..29ef9f984 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/primal_orifice_2_full.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/primal_orifice_leaking.png b/src/main/resources/assets/biomancy/textures/block/primal_orifice_leaking.png new file mode 100644 index 000000000..03dca6a99 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/block/primal_orifice_leaking.png differ diff --git a/src/main/resources/assets/biomancy/textures/entity/acid_blob.png b/src/main/resources/assets/biomancy/textures/entity/acid_blob.png new file mode 100644 index 000000000..59ae4ab76 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/entity/acid_blob.png differ diff --git a/src/main/resources/assets/biomancy/textures/entity/sapberry.png b/src/main/resources/assets/biomancy/textures/entity/sapberry.png new file mode 100644 index 000000000..acb95ee61 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/entity/sapberry.png differ diff --git a/src/main/resources/assets/biomancy/textures/fluid/acid_overlay.png b/src/main/resources/assets/biomancy/textures/fluid/acid_overlay.png new file mode 100644 index 000000000..6364a59e7 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/fluid/acid_overlay.png differ diff --git a/src/main/resources/assets/biomancy/textures/gui/modonomicon/book_content.png b/src/main/resources/assets/biomancy/textures/gui/modonomicon/book_content.png new file mode 100644 index 000000000..72de47167 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/gui/modonomicon/book_content.png differ diff --git a/src/main/resources/assets/biomancy/textures/gui/modonomicon/book_overview.png b/src/main/resources/assets/biomancy/textures/gui/modonomicon/book_overview.png new file mode 100644 index 000000000..e76576f46 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/gui/modonomicon/book_overview.png differ diff --git a/src/main/resources/assets/biomancy/textures/gui/modonomicon/crafting_textures.png b/src/main/resources/assets/biomancy/textures/gui/modonomicon/crafting_textures.png new file mode 100644 index 000000000..93121c276 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/gui/modonomicon/crafting_textures.png differ diff --git a/src/main/resources/assets/biomancy/textures/gui/modonomicon/entry_textures.png b/src/main/resources/assets/biomancy/textures/gui/modonomicon/entry_textures.png new file mode 100644 index 000000000..21e953274 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/gui/modonomicon/entry_textures.png differ diff --git a/src/main/resources/assets/biomancy/textures/gui/modonomicon/main_background.png b/src/main/resources/assets/biomancy/textures/gui/modonomicon/main_background.png new file mode 100644 index 000000000..3c1fa7597 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/gui/modonomicon/main_background.png differ diff --git a/src/main/resources/assets/biomancy/textures/gui/modonomicon/test_background.png b/src/main/resources/assets/biomancy/textures/gui/modonomicon/test_background.png new file mode 100644 index 000000000..bc5bcf002 Binary files /dev/null and b/src/main/resources/assets/biomancy/textures/gui/modonomicon/test_background.png differ diff --git a/src/main/resources/assets/biomancy/textures/block/flesh_eye.png b/src/main/resources/assets/biomancy/textures/item/bloomberry.png similarity index 84% rename from src/main/resources/assets/biomancy/textures/block/flesh_eye.png rename to src/main/resources/assets/biomancy/textures/item/bloomberry.png index 25a8611fd..f3445eb44 100644 Binary files a/src/main/resources/assets/biomancy/textures/block/flesh_eye.png and b/src/main/resources/assets/biomancy/textures/item/bloomberry.png differ diff --git a/src/main/resources/mixins.biomancy.json b/src/main/resources/mixins.biomancy.json index 9939429d0..b8d757ffd 100644 --- a/src/main/resources/mixins.biomancy.json +++ b/src/main/resources/mixins.biomancy.json @@ -8,10 +8,12 @@ "defaultRequire": 1 }, "mixins": [ - "AgeableMobAccessor", "ArmorStandAccessor", "DamageSourceAccessor", "IntegerPropertyAccessor", "LivingEntityAccessor", "MobEffectInstanceAccessor", - "MobEffectInstanceMixin", "MobEntityAccessor", "PhantomMixin", "PlayerMixin", "ServerLevelMixin", "SlimeAccessor", "SwordItemMixinAccessor", - "TadpoleAccessor", "TextureSlotAccessor", "ZombieVillagerMixinAccessor" + "AgeableMobAccessor", "ArmorStandAccessor", "BiomeAccessor", "DamageSourceAccessor", "IntegerPropertyAccessor", "LivingEntityAccessor", "LivingEntityMixin", + "MobEffectInstanceAccessor", "MobEffectInstanceMixin", "MobEntityAccessor", "PhantomMixin", "PlayerMixin", "ServerLevelMixin", "SlimeAccessor", + "SwordItemMixinAccessor", "TadpoleAccessor", "TextureSlotAccessor", "ZombieVillagerMixinAccessor" + ], + "client": [ + "client.ClientPackListenerMixin", "client.ClientRecipeBookMixin", "client.PlayerRendererMixin", "client.RecipeCollectionAccessor", "client.ScreenMixin" ], - "client": [ "client.ClientPackListenerMixin", "client.PlayerRendererMixin", "client.RecipeCollectionAccessor", "client.ScreenMixin" ], "server": [ ] } \ No newline at end of file