From b98c44609db23129303dfb1e1a314c6b677a4e86 Mon Sep 17 00:00:00 2001 From: dags- Date: Fri, 28 Feb 2020 22:25:38 +0000 Subject: [PATCH] make it public --- .gitignore | 7 +- TerraForgedMod/README.md | 7 + TerraForgedMod/build.gradle | 107 +++++ TerraForgedMod/gradlew | 172 ++++++++ TerraForgedMod/gradlew.bat | 84 ++++ TerraForgedMod/psd/biomes.psd | Bin 0 -> 179445 bytes TerraForgedMod/psd/icon.png | Bin 0 -> 27685 bytes TerraForgedMod/psd/terraforged.png | Bin 0 -> 35748 bytes TerraForgedMod/psd/terraforged.psd | Bin 0 -> 307313 bytes .../main/java/com/terraforged/mod/Log.java | 46 +++ .../com/terraforged/mod/TerraForgedMod.java | 81 ++++ .../java/com/terraforged/mod/TerraWorld.java | 157 ++++++++ .../com/terraforged/mod/biome/ColdSteppe.java | 93 +++++ .../com/terraforged/mod/biome/ModBiomes.java | 60 +++ .../terraforged/mod/biome/SavannaScrub.java | 111 ++++++ .../mod/biome/ShatteredSavannaScrub.java | 114 ++++++ .../mod/biome/SnowyTaigaScrub.java | 79 ++++ .../com/terraforged/mod/biome/Steppe.java | 93 +++++ .../com/terraforged/mod/biome/TaigaScrub.java | 83 ++++ .../com/terraforged/mod/biome/WarmBeach.java | 78 ++++ .../mod/biome/map/AbstractBiomeMap.java | 198 ++++++++++ .../mod/biome/map/BasicBiomeMap.java | 82 ++++ .../terraforged/mod/biome/map/BiomeGroup.java | 38 ++ .../terraforged/mod/biome/map/BiomeMap.java | 71 ++++ .../mod/biome/map/BiomeMapBuilder.java | 176 +++++++++ .../mod/biome/map/BiomePredicate.java | 101 +++++ .../mod/biome/map/GridBiomeMap.java | 127 ++++++ .../modifier/AbstractMaxHeightModifier.java | 67 ++++ .../modifier/AbstractOffsetModifier.java | 50 +++ .../mod/biome/modifier/BeachModifier.java | 62 +++ .../biome/modifier/BiomeModifierManager.java | 95 +++++ .../biome/modifier/DesertColorModifier.java | 63 +++ .../mod/biome/modifier/SandBiomeModifier.java | 67 ++++ .../mod/biome/modifier/SwampModifier.java | 57 +++ .../biome/provider/AbstractBiomeProvider.java | 65 ++++ .../mod/biome/provider/BiomeHelper.java | 243 ++++++++++++ .../mod/biome/provider/BiomeProvider.java | 165 ++++++++ .../mod/biome/provider/DesertBiomes.java | 117 ++++++ .../mod/biome/provider/TerraBiomeManager.java | 54 +++ .../biome/provider/TerraBiomeRegistry.java | 58 +++ .../mod/biome/tag/BiomeTagManager.java | 72 ++++ .../mod/chunk/ChunkGeneratorFactory.java | 35 ++ .../mod/chunk/ObfHelperChunkGenerator.java | 150 +++++++ .../mod/chunk/TerraChunkGenerator.java | 366 ++++++++++++++++++ .../terraforged/mod/chunk/TerraContainer.java | 131 +++++++ .../terraforged/mod/chunk/TerraContext.java | 68 ++++ .../mod/chunk/TerraGenSettings.java | 41 ++ .../mod/chunk/TerraTerrainProvider.java | 45 +++ .../mod/chunk/fix/ChunkCarverFix.java | 74 ++++ .../terraforged/mod/chunk/fix/RegionFix.java | 109 ++++++ .../com/terraforged/mod/chunk/test/Test.java | 44 +++ .../mod/chunk/test/TestBiomeProvider.java | 48 +++ .../mod/chunk/test/TestChunkGenerator.java | 87 +++++ .../terraforged/mod/command/TerraCommand.java | 256 ++++++++++++ .../mod/command/arg/BiomeArgType.java | 72 ++++ .../mod/command/arg/TerrainArgType.java | 117 ++++++ .../mod/command/task/FindBiomeTask.java | 80 ++++ .../mod/command/task/FindBothTask.java | 57 +++ .../mod/command/task/FindTask.java | 109 ++++++ .../mod/command/task/FindTerrainTask.java | 59 +++ .../com/terraforged/mod/data/DataGen.java | 72 ++++ .../terraforged/mod/data/WorldGenBiomes.java | 71 ++++ .../terraforged/mod/data/WorldGenBlocks.java | 63 +++ .../mod/data/WorldGenFeatures.java | 68 ++++ .../mod/decorator/ChunkPopulator.java | 49 +++ .../mod/decorator/base/BedrockDecorator.java | 43 ++ .../mod/decorator/base/CoastDecorator.java | 76 ++++ .../mod/decorator/base/ErosionDecorator.java | 193 +++++++++ .../mod/decorator/base/GeologyDecorator.java | 56 +++ .../mod/decorator/base/RiverDecorator.java | 84 ++++ .../mod/decorator/feature/LayerDecorator.java | 87 +++++ .../mod/decorator/feature/SnowEroder.java | 93 +++++ .../mod/decorator/surface/DesertDunes.java | 98 +++++ .../mod/decorator/surface/FrozenOcean.java | 115 ++++++ .../com/terraforged/mod/feature/Matchers.java | 39 ++ .../mod/feature/TerrainHelper.java | 178 +++++++++ .../mod/feature/predicate/TreeLine.java | 57 +++ .../mod/feature/tree/SaplingConfig.java | 122 ++++++ .../mod/feature/tree/SaplingFeature.java | 66 ++++ .../mod/feature/tree/SaplingListener.java | 141 +++++++ .../mod/feature/tree/SaplingManager.java | 90 +++++ .../mod/feature/tree/SaplingPlacer.java | 151 ++++++++ .../mod/feature/tree/TreeBuffer.java | 123 ++++++ .../terraforged/mod/gui/OverlayRenderer.java | 33 ++ .../terraforged/mod/gui/OverlayScreen.java | 87 +++++ .../com/terraforged/mod/gui/ScrollPane.java | 123 ++++++ .../terraforged/mod/gui/SettingsScreen.java | 257 ++++++++++++ .../terraforged/mod/gui/element/CheckBox.java | 60 +++ .../terraforged/mod/gui/element/Element.java | 59 +++ .../mod/gui/element/TerraButton.java | 35 ++ .../mod/gui/element/TerraLabel.java | 52 +++ .../mod/gui/element/TerraSlider.java | 100 +++++ .../terraforged/mod/gui/element/Toggle.java | 86 ++++ .../terraforged/mod/gui/page/BasePage.java | 33 ++ .../terraforged/mod/gui/page/FeaturePage.java | 58 +++ .../terraforged/mod/gui/page/FilterPage.java | 65 ++++ .../mod/gui/page/GeneratorPage.java | 65 ++++ .../com/terraforged/mod/gui/page/Page.java | 201 ++++++++++ .../terraforged/mod/gui/page/RiverPage.java | 66 ++++ .../mod/gui/page/StructurePage.java | 58 +++ .../terraforged/mod/gui/page/TerrainPage.java | 66 ++++ .../terraforged/mod/gui/preview/Preview.java | 259 +++++++++++++ .../mod/gui/preview/PreviewPage.java | 100 +++++ .../mod/gui/preview/PreviewSettings.java | 36 ++ .../mod/gui/preview/RenderMode.java | 83 ++++ .../mod/material/MaterialHelper.java | 151 ++++++++ .../terraforged/mod/material/Materials.java | 163 ++++++++ .../mod/material/geology/GeoGenerator.java | 118 ++++++ .../mod/material/geology/GeoManager.java | 116 ++++++ .../mod/settings/FeatureSettings.java | 48 +++ .../mod/settings/StructureSettings.java | 58 +++ .../mod/settings/TerraSettings.java | 37 ++ .../mod/util/DummyBlockReader.java | 80 ++++ .../com/terraforged/mod/util/Environment.java | 43 ++ .../com/terraforged/mod/util/ListUtils.java | 74 ++++ .../terraforged/mod/util/annotation/Name.java | 31 ++ .../terraforged/mod/util/annotation/Ref.java | 31 ++ .../terraforged/mod/util/nbt/NBTHelper.java | 116 ++++++ .../terraforged/mod/util/nbt/NBTReader.java | 121 ++++++ .../terraforged/mod/util/nbt/NBTWriter.java | 132 +++++++ .../mod/util/setup/SetupDebug.java | 69 ++++ .../mod/util/setup/SetupHooks.java | 77 ++++ .../src/main/resources/META-INF/mods.toml | 12 + .../assets/terraforged/lang/en_us.json | 4 + TerraForgedMod/src/main/resources/biomes.txt | 13 + .../data/terraforged/features/ores/coal.json | 32 ++ .../terraforged/features/ores/diamond.json | 32 ++ .../data/terraforged/features/ores/gold.json | 32 ++ .../terraforged/features/ores/gold_extra.json | 39 ++ .../data/terraforged/features/ores/iron.json | 32 ++ .../data/terraforged/features/ores/lapis.json | 31 ++ .../terraforged/features/ores/redstone.json | 35 ++ .../features/shrubs/dead_bush.json | 42 ++ .../features/shrubs/forest_grass.json | 52 +++ .../terraforged/features/trees/acacia.json | 42 ++ .../terraforged/features/trees/birch.json | 45 +++ .../terraforged/features/trees/birch_oak.json | 57 +++ .../terraforged/features/trees/dark_oak.json | 56 +++ .../terraforged/features/trees/jungle.json | 73 ++++ .../features/trees/oak_badlands.json | 43 ++ .../features/trees/oak_forest.json | 45 +++ .../features/trees/oak_plains.json | 46 +++ .../data/terraforged/features/trees/pine.json | 44 +++ .../terraforged/features/trees/redwood.json | 46 +++ .../terraforged/features/trees/spruce.json | 46 +++ .../terraforged/features/trees/willow.json | 48 +++ .../trees/acacia/bush/acacia_bush_1.nbt | Bin 0 -> 684 bytes .../trees/acacia/bush/acacia_bush_2.nbt | Bin 0 -> 684 bytes .../trees/acacia/large/acacia_1.nbt | Bin 0 -> 500 bytes .../trees/acacia/large/acacia_2.nbt | Bin 0 -> 567 bytes .../trees/acacia/small/acacia_small_1.nbt | Bin 0 -> 530 bytes .../trees/acacia/small/acacia_small_2.nbt | Bin 0 -> 510 bytes .../trees/birch/forest/forest_birch_1.nbt | Bin 0 -> 627 bytes .../trees/birch/forest/forest_birch_2.nbt | Bin 0 -> 550 bytes .../trees/birch/forest/forest_birch_3.nbt | Bin 0 -> 577 bytes .../trees/birch/forest/forest_birch_4.nbt | Bin 0 -> 798 bytes .../structures/trees/birch/large/birch_1.nbt | Bin 0 -> 351 bytes .../structures/trees/birch/large/birch_2.nbt | Bin 0 -> 298 bytes .../structures/trees/birch/large/birch_3.nbt | Bin 0 -> 608 bytes .../trees/birch/small/birch_bush_1.nbt | Bin 0 -> 301 bytes .../trees/birch/small/birch_bush_2.nbt | Bin 0 -> 315 bytes .../trees/birch/small/birch_small_1.nbt | Bin 0 -> 439 bytes .../trees/birch/small/birch_small_2.nbt | Bin 0 -> 435 bytes .../trees/dark_oak/large/dark_oak_tall_1.nbt | Bin 0 -> 651 bytes .../trees/dark_oak/large/dark_oak_tall_2.nbt | Bin 0 -> 711 bytes .../trees/dark_oak/large/dark_oak_tall_3.nbt | Bin 0 -> 581 bytes .../trees/dark_oak/large/dark_oak_tall_4.nbt | Bin 0 -> 904 bytes .../trees/dark_oak/large/dark_oak_tall_5.nbt | Bin 0 -> 798 bytes .../trees/dark_oak/small/dark_oak_1.nbt | Bin 0 -> 759 bytes .../trees/dark_oak/small/dark_oak_2.nbt | Bin 0 -> 696 bytes .../trees/dark_oak/small/dark_oak_3.nbt | Bin 0 -> 744 bytes .../trees/dark_oak/small/dark_oak_bush_1.nbt | Bin 0 -> 385 bytes .../trees/dark_oak/small/dark_oak_bush_2.nbt | Bin 0 -> 396 bytes .../trees/jungle/huge/jungle_massive_1.nbt | Bin 0 -> 2225 bytes .../trees/jungle/huge/jungle_massive_2.nbt | Bin 0 -> 1813 bytes .../trees/jungle/huge/jungle_massive_3.nbt | Bin 0 -> 2386 bytes .../trees/jungle/huge/jungle_massive_4.nbt | Bin 0 -> 2537 bytes .../trees/jungle/huge/jungle_massive_5.nbt | Bin 0 -> 2210 bytes .../trees/jungle/huge/jungle_massive_6.nbt | Bin 0 -> 2505 bytes .../trees/jungle/large/jungle_tall_1.nbt | Bin 0 -> 1593 bytes .../trees/jungle/large/jungle_tall_2.nbt | Bin 0 -> 1796 bytes .../trees/jungle/large/jungle_tall_3.nbt | Bin 0 -> 1876 bytes .../trees/jungle/large/jungle_tall_4.nbt | Bin 0 -> 1718 bytes .../trees/jungle/small/jungle_small_1.nbt | Bin 0 -> 844 bytes .../trees/jungle/small/jungle_small_2.nbt | Bin 0 -> 704 bytes .../trees/jungle/small/jungle_small_3.nbt | Bin 0 -> 734 bytes .../trees/oak/forest/forest_oak_1.nbt | Bin 0 -> 585 bytes .../trees/oak/forest/forest_oak_2.nbt | Bin 0 -> 582 bytes .../structures/trees/oak/huge/oak_big_1.nbt | Bin 0 -> 1702 bytes .../structures/trees/oak/huge/oak_big_2.nbt | Bin 0 -> 1267 bytes .../structures/trees/oak/large/oak_1.nbt | Bin 0 -> 530 bytes .../structures/trees/oak/large/oak_2.nbt | Bin 0 -> 530 bytes .../structures/trees/oak/large/oak_3.nbt | Bin 0 -> 772 bytes .../structures/trees/oak/large/oak_4.nbt | Bin 0 -> 579 bytes .../structures/trees/oak/large/oak_5.nbt | Bin 0 -> 1089 bytes .../structures/trees/oak/small/oak_bush_1.nbt | Bin 0 -> 298 bytes .../structures/trees/oak/small/oak_bush_2.nbt | Bin 0 -> 314 bytes .../trees/oak/small/oak_small_1.nbt | Bin 0 -> 336 bytes .../trees/oak/small/oak_small_2.nbt | Bin 0 -> 327 bytes .../trees/oak/small/oak_small_3.nbt | Bin 0 -> 337 bytes .../trees/oak/small/oak_small_4.nbt | Bin 0 -> 354 bytes .../trees/pine/huangshan_pine_1.nbt | Bin 0 -> 567 bytes .../trees/pine/huangshan_pine_2.nbt | Bin 0 -> 446 bytes .../trees/pine/huangshan_pine_3.nbt | Bin 0 -> 478 bytes .../structures/trees/pine/scots_pine_1.nbt | Bin 0 -> 654 bytes .../structures/trees/pine/scots_pine_2.nbt | Bin 0 -> 705 bytes .../trees/pine/scots_pine_small_1.nbt | Bin 0 -> 651 bytes .../trees/pine/scots_pine_small_2.nbt | Bin 0 -> 622 bytes .../structures/trees/pine/stone_pine_1.nbt | Bin 0 -> 618 bytes .../structures/trees/pine/stone_pine_2.nbt | Bin 0 -> 671 bytes .../trees/redwood/huge/redwood_massive_1.nbt | Bin 0 -> 1059 bytes .../trees/redwood/huge/redwood_massive_2.nbt | Bin 0 -> 1292 bytes .../trees/redwood/huge/redwood_massive_3.nbt | Bin 0 -> 1320 bytes .../trees/redwood/huge/redwood_massive_4.nbt | Bin 0 -> 1011 bytes .../trees/redwood/huge/redwood_massive_5.nbt | Bin 0 -> 965 bytes .../trees/redwood/huge/redwood_massive_6.nbt | Bin 0 -> 1578 bytes .../trees/redwood/large/redwood_tall_1.nbt | Bin 0 -> 932 bytes .../trees/redwood/large/redwood_tall_2.nbt | Bin 0 -> 921 bytes .../trees/redwood/large/redwood_tall_3.nbt | Bin 0 -> 1097 bytes .../trees/redwood/large/redwood_tall_4.nbt | Bin 0 -> 631 bytes .../trees/spruce/bush/spruce_bush_1.nbt | Bin 0 -> 312 bytes .../trees/spruce/bush/spruce_bush_2.nbt | Bin 0 -> 276 bytes .../trees/spruce/large/spruce_1.nbt | Bin 0 -> 720 bytes .../trees/spruce/large/spruce_2.nbt | Bin 0 -> 409 bytes .../trees/spruce/large/spruce_3.nbt | Bin 0 -> 398 bytes .../trees/spruce/large/spruce_4.nbt | Bin 0 -> 384 bytes .../trees/spruce/large/spruce_5.nbt | Bin 0 -> 347 bytes .../trees/spruce/small/spruce_small_1.nbt | Bin 0 -> 308 bytes .../trees/spruce/small/spruce_small_2.nbt | Bin 0 -> 409 bytes .../trees/spruce/small/spruce_small_3.nbt | Bin 0 -> 337 bytes .../trees/spruce/small/spruce_small_4.nbt | Bin 0 -> 402 bytes .../willow/large/weeping_willow_big_1.nbt | Bin 0 -> 1418 bytes .../willow/large/weeping_willow_big_2.nbt | Bin 0 -> 1229 bytes .../willow/small/weeping_willow_small_1.nbt | Bin 0 -> 866 bytes .../willow/small/weeping_willow_small_2.nbt | Bin 0 -> 870 bytes .../data/terraforged/tags/biomes/alpine.json | 12 + .../terraforged/tags/biomes/cold_steppe.json | 6 + .../data/terraforged/tags/biomes/desert.json | 14 + .../terraforged/tags/biomes/grassland.json | 8 + .../data/terraforged/tags/biomes/savanna.json | 11 + .../data/terraforged/tags/biomes/steppe.json | 6 + .../data/terraforged/tags/biomes/taiga.json | 11 + .../tags/biomes/temperate_forest.json | 13 + .../tags/biomes/temperate_rainforest.json | 10 + .../tags/biomes/tropical_rainforest.json | 10 + .../data/terraforged/tags/biomes/tundra.json | 10 + .../data/terraforged/tags/blocks/clay.json | 6 + .../data/terraforged/tags/blocks/earth.json | 7 + .../terraforged/tags/blocks/erodible.json | 10 + .../data/terraforged/tags/blocks/ore.json | 12 + .../data/terraforged/tags/blocks/rock.json | 9 + .../terraforged/tags/blocks/sediment.json | 8 + .../templates/trees/acacia_large.json | 8 + .../templates/trees/acacia_small.json | 9 + .../templates/trees/birch_forest.json | 13 + .../templates/trees/birch_large.json | 13 + .../templates/trees/birch_small.json | 8 + .../templates/trees/dark_oak_large.json | 8 + .../templates/trees/dark_oak_small.json | 8 + .../templates/trees/jungle_huge.json | 8 + .../templates/trees/jungle_large.json | 8 + .../templates/trees/jungle_small.json | 8 + .../templates/trees/oak_forest.json | 13 + .../terraforged/templates/trees/oak_huge.json | 13 + .../templates/trees/oak_large.json | 13 + .../templates/trees/oak_small.json | 8 + .../terraforged/templates/trees/pine.json | 8 + .../templates/trees/redwood_huge.json | 21 + .../templates/trees/redwood_large.json | 8 + .../templates/trees/spruce_large.json | 8 + .../templates/trees/spruce_small.json | 9 + .../templates/trees/willow_large.json | 8 + .../templates/trees/willow_small.json | 8 + TerraForgedMod/src/main/resources/license.txt | 21 + TerraForgedMod/src/main/resources/pack.mcmeta | 6 + .../src/main/resources/terraforged.png | Bin 0 -> 35748 bytes 276 files changed, 12274 insertions(+), 1 deletion(-) create mode 100644 TerraForgedMod/README.md create mode 100644 TerraForgedMod/build.gradle create mode 100644 TerraForgedMod/gradlew create mode 100644 TerraForgedMod/gradlew.bat create mode 100644 TerraForgedMod/psd/biomes.psd create mode 100644 TerraForgedMod/psd/icon.png create mode 100644 TerraForgedMod/psd/terraforged.png create mode 100644 TerraForgedMod/psd/terraforged.psd create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/Log.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/TerraForgedMod.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/TerraWorld.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/ColdSteppe.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/ModBiomes.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/SavannaScrub.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/ShatteredSavannaScrub.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/SnowyTaigaScrub.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/Steppe.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/TaigaScrub.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/WarmBeach.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/AbstractBiomeMap.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BasicBiomeMap.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeGroup.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeMap.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeMapBuilder.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomePredicate.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/GridBiomeMap.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/AbstractMaxHeightModifier.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/AbstractOffsetModifier.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/BeachModifier.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/BiomeModifierManager.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/DesertColorModifier.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/SandBiomeModifier.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/SwampModifier.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/AbstractBiomeProvider.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeHelper.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeProvider.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/DesertBiomes.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/TerraBiomeManager.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/TerraBiomeRegistry.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/biome/tag/BiomeTagManager.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/ChunkGeneratorFactory.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/ObfHelperChunkGenerator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraContainer.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraContext.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraGenSettings.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraTerrainProvider.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/fix/ChunkCarverFix.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/fix/RegionFix.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/Test.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/TestBiomeProvider.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/TestChunkGenerator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/command/TerraCommand.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/BiomeArgType.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/TerrainArgType.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindBiomeTask.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindBothTask.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindTask.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindTerrainTask.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/data/DataGen.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenBiomes.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenBlocks.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenFeatures.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/ChunkPopulator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/BedrockDecorator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/CoastDecorator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/ErosionDecorator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/GeologyDecorator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/RiverDecorator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/feature/LayerDecorator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/feature/SnowEroder.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/surface/DesertDunes.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/decorator/surface/FrozenOcean.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/Matchers.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/TerrainHelper.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/predicate/TreeLine.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingConfig.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingFeature.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingListener.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingManager.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingPlacer.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/TreeBuffer.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/OverlayRenderer.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/OverlayScreen.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/ScrollPane.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/SettingsScreen.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/CheckBox.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/Element.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraButton.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraLabel.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraSlider.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/Toggle.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/BasePage.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/FeaturePage.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/FilterPage.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/GeneratorPage.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/Page.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/RiverPage.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/StructurePage.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/TerrainPage.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/PreviewPage.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/PreviewSettings.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/RenderMode.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/material/MaterialHelper.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/material/Materials.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/material/geology/GeoGenerator.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/material/geology/GeoManager.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/settings/FeatureSettings.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/settings/StructureSettings.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/settings/TerraSettings.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/DummyBlockReader.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/Environment.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/ListUtils.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/annotation/Name.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/annotation/Ref.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTHelper.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTReader.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTWriter.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/setup/SetupDebug.java create mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/util/setup/SetupHooks.java create mode 100644 TerraForgedMod/src/main/resources/META-INF/mods.toml create mode 100644 TerraForgedMod/src/main/resources/assets/terraforged/lang/en_us.json create mode 100644 TerraForgedMod/src/main/resources/biomes.txt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/ores/coal.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/ores/diamond.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/ores/gold.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/ores/gold_extra.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/ores/iron.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/ores/lapis.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/ores/redstone.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/shrubs/dead_bush.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/shrubs/forest_grass.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/acacia.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/birch.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/birch_oak.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/dark_oak.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_badlands.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_forest.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_plains.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/pine.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/redwood.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/spruce.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/features/trees/willow.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/bush/acacia_bush_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/bush/acacia_bush_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/large/acacia_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/large/acacia_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/small/acacia_small_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/small/acacia_small_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/forest/forest_birch_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/forest/forest_birch_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/forest/forest_birch_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/forest/forest_birch_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/large/birch_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/large/birch_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/large/birch_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/small/birch_bush_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/small/birch_bush_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/small/birch_small_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/small/birch_small_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_5.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_bush_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_bush_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_5.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_6.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/large/jungle_tall_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/large/jungle_tall_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/large/jungle_tall_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/large/jungle_tall_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/small/jungle_small_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/small/jungle_small_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/small/jungle_small_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/forest/forest_oak_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/forest/forest_oak_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/huge/oak_big_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/huge/oak_big_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/large/oak_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/large/oak_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/large/oak_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/large/oak_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/large/oak_5.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_bush_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_bush_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_small_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_small_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_small_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_small_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/huangshan_pine_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/huangshan_pine_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/huangshan_pine_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/scots_pine_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/scots_pine_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/scots_pine_small_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/scots_pine_small_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/stone_pine_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/stone_pine_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_5.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_6.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/bush/spruce_bush_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/bush/spruce_bush_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_5.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/small/spruce_small_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/small/spruce_small_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/small/spruce_small_3.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/small/spruce_small_4.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/willow/large/weeping_willow_big_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/willow/large/weeping_willow_big_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/willow/small/weeping_willow_small_1.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/structures/trees/willow/small/weeping_willow_small_2.nbt create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/alpine.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/cold_steppe.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/desert.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/grassland.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/savanna.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/steppe.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/taiga.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/temperate_forest.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/temperate_rainforest.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/tropical_rainforest.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/biomes/tundra.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/blocks/clay.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/blocks/earth.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/blocks/erodible.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/blocks/ore.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/blocks/rock.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/tags/blocks/sediment.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/acacia_large.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/acacia_small.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/birch_forest.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/birch_large.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/birch_small.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/dark_oak_large.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/dark_oak_small.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/jungle_huge.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/jungle_large.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/jungle_small.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/oak_forest.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/oak_huge.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/oak_large.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/oak_small.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/pine.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/redwood_huge.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/redwood_large.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/spruce_large.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/spruce_small.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/willow_large.json create mode 100644 TerraForgedMod/src/main/resources/data/terraforged/templates/trees/willow_small.json create mode 100644 TerraForgedMod/src/main/resources/license.txt create mode 100644 TerraForgedMod/src/main/resources/pack.mcmeta create mode 100644 TerraForgedMod/src/main/resources/terraforged.png diff --git a/.gitignore b/.gitignore index a1c2a23..4dc7817 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ .mtj.tmp/ # Package Files # -*.jar +#*.jar *.war *.nar *.ear @@ -21,3 +21,8 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +build/ +out/ +run/ +*.iml \ No newline at end of file diff --git a/TerraForgedMod/README.md b/TerraForgedMod/README.md new file mode 100644 index 0000000..073aa26 --- /dev/null +++ b/TerraForgedMod/README.md @@ -0,0 +1,7 @@ +# TerraForged + +### Commands +- `/terra query` - print the Terrain and Biome types at your location +- `/terra find ` - find the nearest instance of the given biome type +- `/terra find ` - find the nearest instance of the given terrain type +- `/terra find ` - find the nearest instance of the given terrain and biome type \ No newline at end of file diff --git a/TerraForgedMod/build.gradle b/TerraForgedMod/build.gradle new file mode 100644 index 0000000..359d786 --- /dev/null +++ b/TerraForgedMod/build.gradle @@ -0,0 +1,107 @@ +buildscript { + repositories { + jcenter() + mavenCentral() + maven { url "https://files.minecraftforge.net/maven" } + } + dependencies { + classpath group: "net.minecraftforge.gradle", name: "ForgeGradle", version: "3.+", changing: true + } +} + +apply plugin: "net.minecraftforge.gradle" +apply plugin: "maven-publish" +apply plugin: "eclipse" + +version = "${mc_version}-${version}${getBuildNumber()}" +archivesBaseName = "TerraForged" + +repositories { + jcenter() + mavenCentral() +} + +dependencies { + minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" + compile project(":Noise2D") + compile (project(":TerraForgedCore")) { + transitive false + } + compile (project(":FeatureManager")) { + transitive false + } + compile (project(":TerraForgedAPI")) { + transitive false + } +} + +minecraft { + mappings channel: mcp_channel, version: mcp_version + runs { + client { + workingDirectory project.file("run/client") + property "forge.logging.markers", "SCAN,REGISTRIES,REGISTRYDUMP" + property "forge.logging.console.level", "debug" + jvmArgs "-Xmx8G", "-Xms6G", "-Ddev" + mods { + terraforged { + source sourceSets.main + } + } + } + server { + workingDirectory project.file("run/server") + property "forge.logging.markers", "SCAN,REGISTRIES,REGISTRYDUMP" + property "forge.logging.console.level", "debug" + jvmArgs "-Xmx8G", "-Xms6G", "-Ddev" + mods { + terraforged { + source sourceSets.main + } + } + } + } +} + +task collectClasses(type: Copy) { + configurations.collectMany { it.allDependencies }.findAll{ it instanceof ProjectDependency }.each { + ProjectDependency project = (ProjectDependency) it + from("$project.dependencyProject.buildDir/classes/java/main") + } + into("build/classes/java/main") +} + +task collectResources(type: Copy) { + configurations.collectMany { it.allDependencies }.findAll{ it instanceof ProjectDependency }.each { + ProjectDependency project = (ProjectDependency) it + from("$project.dependencyProject.buildDir/resources/main") + } + into("build/resources/main") +} + +processResources { + dependsOn(collectResources) + + filesMatching("**/mods.toml") { + expand( + "version": version, + "mc_version": mc_version + ) + } +} + +classes { + dependsOn(collectClasses) +} + +publish { + dependsOn("reobfJar") +} + +static def getBuildNumber() { + def buildNumber = System.getenv("BUILD_NUMBER") + if (buildNumber == null) { + return "" + } + return "-${buildNumber}" +} \ No newline at end of file diff --git a/TerraForgedMod/gradlew b/TerraForgedMod/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/TerraForgedMod/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/TerraForgedMod/gradlew.bat b/TerraForgedMod/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/TerraForgedMod/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/TerraForgedMod/psd/biomes.psd b/TerraForgedMod/psd/biomes.psd new file mode 100644 index 0000000000000000000000000000000000000000..eedc297d809041cec1b957c80d9d488f13689c0c GIT binary patch literal 179445 zcmeFa1wd3y)Hi;&ix9*PLPCM11Ql$sP?6YOIwTfE5s*-^A0Ho|$Dnoz2}MB+OhCZE z4iE{uySp%nz2BL;l!Af6_r34`?_T%rxie>e=ggTi=g!Q%jDyT=co+$jeyA8h!X*=< zNR#~K2ANx0Q6UFDU3!w7F-i}Ou?@du41<3d!J|R&bSQG`@*pfP@cp#|rg4X!M7R=p z>L_pYe?x}P4)*k(CJa;Y68iZE4pn=#@1UBBzt2!L2NNp;t6&S^6#sE^LWI0Illb0q zrg@wCs0|;+9x~f(_VnQC!Z1&j+0z38L(OInRrB--@)DW>jgs}%R0x)^X+zaUq6`&B zD;pJypb(*oiQWLNx1sSMm4T*uh69WZ`VY`mF)}bT)i)TVZ`hw}U}$DAz|3TzO5I0o z7#pY|AwIrlcB97BiG!=5YE#0(g3a{xXU&?WH>7$tp{Xgr80i}sae;#y8W9-g zIhz|8+D8gfk26Xb>K)=A9OfSssDf}ky@JBShN`I{K@Fd}dQA^*5EK}yN7PZzJ7~K8 zY|mhQLp=limJ0cJH~9K& zHnFm5klmub)2Fv6Iy7wbOfW&6Nn0!#%8v*Z>e~rJgTh0+g`;OeRr<74`_M2uVPot3 z1Qf70HW1i<2voz&B1GsJ78Jq{3JMq|bs(F1>!?^*5Duf#*TFx~Cumk^zajcx7inzD zMl|6l&oJRIMXK-+UzqcpyUj3PYK6O6X z;2cBr8(Eu_Z^$>Z2nq-a0X>Do`VY}>DY?Z`mKGK^AwjDb!0lr54eT_|wyuAk+57DoKXw(bg5~*KG zY+9f1>5E*;%F4_#Ff_~)s$)577$oTVgJuJThCYVA27|bujxpEE$aoN(3{1F2z5@-t zjfB2~e7uYZhxxL2qh@u`_0j?QcPcK`vk6!;LDnJu5dA#^n(&~y_&{BRp58`Y{Y?gO zeTAM-jsC_a+(E{^LawK=*8pQv(*dTQUZ(YRX#u90sB!+N-6OtiVWN3?!WmztHnuwv zr-MC1LWL*_4OOcTLk)qw!Glm4BAS_bdZRuv%o{}#q0bQgFH#%TYA9J(%)fba{+tP< z_)Us`TgI#@!ocrEA}JKX^=**QAm6ZAo*}}Ke$bzOCP+$Azby7Oe9OJ0Z5kpdaHcS% zHBA8x?g^s_Aw&t^kf7-*{?k4Eg!;jOekx%>DnE>a(&{$B_`g`!P|uma=W+v}v-t{5 z2XIXc2YR9Lk~i1W*I3B)9q8@l^U2i58BBuWD zsq=5gR0AWU{{4kQPp*%T*C4I|>X}|bUt_NKKm%h4s3zV6O#k#u|8^LHwVk&%bie=a zj{r;u3B61WgnOD)%I(V-1){zkBl)pRXI3iuY~`f0KB z?l%U_3DBe-?ruMr7(xrfGLu=8@mBp*{bz3qUlspiL|TXLQ@6J9o3nMPs!hOJk`?s3 z6LzVT7GN5s)wjc_MswHS9HhiEvyjhH!Km?muZ-2w+qpzcDuk!LH`J8 zm2v-=>yNowWr0>P|M9Lr=4zD%TE+awyZ)G~RTgL!^B?c}W3EyNowWr0>P|M9Lr=4zD%TE+awyZ)G~RTgL!^LM?A-MHzBFc5ZH znFU*_5PPzi5&N|~fO7}Y8dOo)YH;aD(&!q~wL+;By}mA1tA-1UkU)#Z zi@>nPi-}$Vp$$2c{K957T#TL`Fsk7K?AQox?mf+~&Ja>7VXy&)1!zNT$YbZDqT&-2 z?lmIlHqwyzj138FmS!H%oXULE!AvhJ_4DNvA#1)45kfb=hh-+eZ zCPEk4hU?zMb+<3CDV4-Ei#WqcilT*j1N1El}fIV8*(&hIzy`}*WB**RW*#70g{ zjH@8UHy_)O0Y$J$L4nBDtgxV97(|2$n?_f50EFRYsWM(f*lw22fj>E%@-;ir^ZFt| zFiHS-9{!r3rF~SMNQTHPGj5`PU=SJHe>&V*(f(16PR=Tfa&UhZ^gwWQPw&uRE8ZA1 z6v1F`DHXKIloW|LgCBpmDD7nDxZ`Y8RGQ@d45{E990Ipkk_|BT_ksQ2fL;Q0z^t%f zl#aAk@|uQdGU{?lA)p}8T@mdkrF97&DLn$wKGOri!O2MHV4vweh&~4Ne>208<5MOA zy>zC(a2C+#fmRO)pY9JdinvPCg`UtEsmNyPVM6aIKpOxp6T-KHdo*c$XyHR2q)4Y-}tl%FE>w2{@ zO?@p4YL{SGenpe|bVmNtWHUg2p|>6~Xv61+3=N#$CYs38L-HCnKT{bc?ZPU^tG!`w zweS!X7+H7=Rk&X}@h=x_qE-_*ccc70yv9FpQBdOm54f(|R zixW8-F3F+(#BBkyu;Z(Ehlk9RvQW_v4aP+Bm=e|v>y7on`od_&2pfnE#ztVHvGJHS zjNF_scNnWpfqQodHVd1F{e>;XR$wt$JeG{5Ve7HY*mf)jE5Hh|!`KPz6m}ZBfL+6G zV-K)r*lX+qRt!!NW(~@NfSwSBq!2jk{>CM6i%8)`kSPJg0o7(y1M(DpW3YFm)VNK=r21pe~@Uq$X20QS+(CsOPA6sIRFK znk=m+O^0Siv!prF{AjaiOKAzT4YWMkaoPpi1KI~Vo!*(QMIT5XM|Yx6rO&0Wq_3v$ zpdX^2q2HswXD}Gu7&?rh3~PobBaE?>k;K@_ILJ85c)<9?<)M$9oxXJ#OCF*AXg z#XP_~%Y4MFW_4g`u*_K2EFV?`E1I>QwU<@Fdcdk?E3mcLL)m=xRQ6x&RqXBTsQ;iST2MTVlL;%G%5#U+Xviboaib)}1@@zSHbZDV_Fry4{)5S+lcc z=YY=9opU;0>|E8QSC^4pgk6?(+1}-Bmycb0bRE%E*mZf=9bGSUt?H)IZFIM3-D0~H zbi2`=+FiT5ZTDH-*K|ML{do_i9)o*$_gLN|r^nTv;d0>NwSK z)pe@ns?};*Y67*zYCF_!^kMfI)W^3^T%RL-UaKpsTdB`e-=cn1gQYP@V~R$iMv=xx zO)X6a&3`ouG#_hq)w0x@qqRlrdSBVT!~4$Yo8I?)KYG7G{igL>-S4zES=&T=igt>2 zg$`L~fR4Y;YMnE>G+k5OK;3lROI#W5NbXGT7VaIr&UzE|7V8!0z0_CNch-;9FVdG7 zm>2{YtT(u3sAxFeaIxWD!?#A-M&3p#M(6v>_8-%KVgKFz-x%u{3ys$pUpDDzVrBBL z$q|#<0Rsnw4A?&4$w2jilLw{_yfjE@kj3_WBl?W+8L?r+qmlha zP93>*20~$^7Z(E+C*={G@ zySoRt@Ar`R@bt)?Oq=X5Icsu_r?qFg=X)e`_2V?_X&a~E0S*B>rZcBcp1vngF>qSo zv7p{TvxCkA>jWZo0&56(=7X0 zxw92!2hJ{y(2iIZ@o3JNIhk{*bG_#tnWsAMuX%UokDR}50cnBPf+Gw2EL^hi!J@H? zHvPr^YuaCBiwzcw7Qg%3;qSdmdM#PB?VbNvcVnl6)b>JY`2}&(vk9A6NUVKD%bb zn(b-5(pIEZt@U4fdEMA``RSVJ2^sW^nHdk)+pjOm9F)0vL-!3UHq>qm+<1GF-KJxk z2X5Y+)iW!43wcZUmM2?Xx1QcMYTNGZ-0kajbl$OIC$=+u=hN)T*%xyr8bz#zlGZzIHFJE%K zbo;W;<;Pb7uDrQA^J?|AMb~N9m*41cBjIMxo9Vau-P&<`!0r8aM%^jB%e#B+p7*_H z_e1a3JXrEj_F=*!t6M^;X@gPdFkB{?}cC3KQgk`__;836ncroe;>tfi3DFtP%PqCmoL!2F+$A0rJ1 zEopVDAD}UOC>V)MrO_Eo7MlahUteaDFfye+vjaw=k|<;T8130W+%;C1p2 zHGZQ(M+z`BO^%!eB{I7Bj1~+C6W(o5;oBN$HL8GL#bhXiOa*K>_MR`Y)aa)!vK()z zF0vC@s>iKwo**#W$UpLAkiDOuqfYv|&G&bwWw=eRsaF0RY3XA4wAO=>G5N~sG{22j zb1;+8>kIx7^sjs|=gq;b9*&igoqj!2GoGA1+4b_;Q#$@Qwq*~F-3i38GgCdBhGjpT z?^#i8b?;pIolv2fspoS{34 zXT4hZw-ded&e%Rx&CaZnbX>r zj|SCt&+>b8`Q4&CMSA9f%@+*z+ILF2lFxp!b>4(!RVEj2Rb=B>@`d}#j*{$$wUbNV zze;}dOzZjOwZ)SOb@$^~n%@VV3D1--4!O0p+X1-FDB2nuxl(2+<#Ty$^{vmZBu6{j z<*n!df4WIrLzp($cH+v{_;Uf~&IQy=ZFWuq#+zlURv!*=?G*SyLTE{NJN z?!*bYi;H!wTLm<8kYvS#XN-$7FAuK%uyu!UB2qX%KdowCnxyls+RnKw%IZ#qj=RH$ z_fJbRO}Z0(3dbxhSHH79Y#BLM^0zCFJzcy|t-smK@S7!12Csf+@m~OrtuYTPGq*Df zJ~5`|Z`VLZsFiwxc-W3`KeJvgI?9F5x+#C^HuvD;EuCuZO9sz(UA{{GtgPG&3Wg*N;LfTn&DM4_vpMT#;DePUpQ9$F>%B2kEN#{ zs4ob0r_937wPa$!-hXY5KiM69s$}NXq{~wub$mB%+rib|XUXrF>LkArUBd&n{e9+j zKzaFsl4++&Th`orr@c8X&HIuleEW_I3vzvHw_kh?-N>YWr}O7(cDybaUSqNi$10}c zm~;8T?SAuj48MM2-0|aSXU?B{AnE)O$CmE7_4$RvZOoW5f zbe(tbU9w5o2HC)!g_37OGivU=wBK^##E!@O)APoOC83!L8ajN@oE;inWF}1*vcC2( zO_@_QVS!xDa=GBxl1Do#orXWQ6f-I>m28c= z%0C+Xar?qYd#ty;U%6(-!fms15|mFv`tXQb`$G>7eP(D~5?rDgUNxrA^U#RW6Qf4V zi(hyXynLJSd5CnGeeNu@3_5%(mQ=6(!NHOT6_Qh0wd7OaX+xu{-$3g>Ru@f(nTZN) z#4j|_Uc+PX;4T~Q4cF;ct?hS{h&GS*Xy^PY z=TA&^mR)u5an0#7%I_9TnPw3y@vG^BV4qLbu)x%)3`k1qiSi{adD(N%Ld$!`d z@#{)!91;%Cxckx9_;%50X#0hI<|cmfGfZCA`@H-U9~|5BZ|OgI`>+1BaoK5|u6Y4_ zwrfu8fZekZtscV-Dth}kdr#U*zfu`2N;hP8u{=MMwP58&{pu)&Lzg2vz38J(X^xBP zE1K=yt!S&Ka`k}$XD!t?>4GMrH2h+nZDK;+K0dnc(#;oJg97h=@Mb(RJ=xDGBFyvj zvsW`O%3?1qt8^#VJTi^AlQ2+vG>)q z9yK>CmPS`s;@JFW$yzh?YVTU#6pk+S?;7>!?c`HB>&}4Otw&B&WK>%4a(8~-l(&7x z(q(U)3paOoaBRaU@29bGk)x&^xlp1pq|lIVXQI&c>G*Z6O>R$JUTiy9 zCb*JuO!7XhL?Ax2!F2qYo3(MyICjG`df5jY>lCT5aq;=5de(2{M1bE&Lsc>4befY)CICgC8lL?t87?a_- z-G~lD$d=j>p9`-X8};DvKg=U$<|efsHzm25f5#tPT)Vp}vvT4y(}n+lvA_fles<%% zQkMPYa^P{zroZJXi>G`_tBJZl+uCadQ=xCiY4g@gmxVxTySF<(Cuy6Rsebah;6kpkf%sF<` z`yN&Q+{|2a^;*pZx8xz#uN?31jkei049Dy$rcX~Bzr9SkaN(VvSsVYF7!v=vpL%@H z^0gN`3baCZ&n=>S=Jwm8soJs6j>!W>>nMFJUOX?_G{k9|qg;IXx?3I}lQ2SB7DnSx*c4O~k#^Y-*Usi63hMS?5|BIBdn&s4JY z6y}UFnp0>$J$&Hf?DRo~S*dA>i@P0&{d~JCOF{%9|;yVfhM7+t&MddsWsMxq}{YGWKsi$5xD`o4h^e&)?fci zrW9gi(jokFLq0MY`cl!;dCqi{KH3N+j3n$bb~mzkApCbKI2al@;b{x;=_`Gnk4k<2 z%b4gHAq-J5L^uKAf$&rZnj9YIVH4qffe3Jm5KIL<)gtl(Lqi~s8sHfbLR?4q!_y(6 zeq?m*9pDv!u942@+CR{Dc3nPkJuPrrP}A#x&|u#$uDt^gKB@@uK^UP^rXv~S;7hzY z;2j=HJTZc6WB7JJ>F|Y8=s#;h|1C{j3JbIphVcET3(-3*BQMQK+?sLoB%z1z>=5MI zsO&dB2okftK;8`PT1*}Ng3MAs$2}y}atU;|n>he`9{HpKw zZ({xCzTf|czE8aK@ju?`x7a_j`Yo_FtVWU08zyyDrx{@Bm@KJ$Nt@844Sq7@F!h&!YHsg``m@42~MM}`ysH-qgRcPMoxOUV z#=C5{{~gx0`oh|Tg}yx9gi9D{fIDN@xw`m{d>_M}5{XZdwXvsPSsScOe16*6ba=%8 z-Z^S&X>`x0!;lxf4Tr`yVMHr5T9{ag|DPYrr+(kUBzU*rdj|`=jSb be~3v81KG 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ð-5{r2Q}d$vK6&~ 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

w4ET5TrP&TK@4pyYf-Rbec-prw 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 literal 0 HcmV?d00001 diff --git a/TerraForgedMod/psd/terraforged.psd b/TerraForgedMod/psd/terraforged.psd new file mode 100644 index 0000000000000000000000000000000000000000..8034e6d52abb0b2b623c6946c4f156d2527fedde GIT binary patch literal 307313 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 B73BZ` literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/Log.java b/TerraForgedMod/src/main/java/com/terraforged/mod/Log.java new file mode 100644 index 0000000..dd5af8d --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/Log.java @@ -0,0 +1,46 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class Log { + + private static final Logger logger = LogManager.getLogger("TerraForged"); + + public static void info(String message, Object... args) { + logger.info(message, args); + } + + public static void debug(String message, Object... args) { + logger.debug(message, args); + } + + public static void err(String message, Object... args) { + logger.error(message, args); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/TerraForgedMod.java b/TerraForgedMod/src/main/java/com/terraforged/mod/TerraForgedMod.java new file mode 100644 index 0000000..eb81041 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/TerraForgedMod.java @@ -0,0 +1,81 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod; + +import com.terraforged.api.material.MaterialTags; +import com.terraforged.core.util.concurrent.ThreadPool; +import com.terraforged.feature.FeatureManager; +import com.terraforged.mod.data.DataGen; +import com.terraforged.mod.feature.tree.SaplingManager; +import com.terraforged.mod.util.Environment; +import net.minecraft.world.gen.feature.Feature; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; +import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; + +/** + * Author + */ +@Mod("terraforged") +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class TerraForgedMod { + + @SubscribeEvent + public static void setup(FMLCommonSetupEvent event) { + Log.info("Common setup"); + MinecraftForge.EVENT_BUS.addListener(TerraForgedMod::onShutdown); + MaterialTags.init(); + TerraWorld.init(); + SaplingManager.init(); + } + + @SubscribeEvent + public static void complete(FMLLoadCompleteEvent event) { + if (Environment.isDev()) { + DataGen.dumpData(); + } + } + + @SubscribeEvent + public static void server(FMLDedicatedServerSetupEvent event) { + Log.info("Setting dedicated server"); + TerraWorld.setDedicatedServer(); + } + + @SubscribeEvent + public static void registerFeatures(RegistryEvent.Register> event) { + FeatureManager.registerTemplates(event); + } + + private static void onShutdown(FMLServerStoppingEvent event) { + ThreadPool.shutdownCurrent(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/TerraWorld.java b/TerraForgedMod/src/main/java/com/terraforged/mod/TerraWorld.java new file mode 100644 index 0000000..0394335 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/TerraWorld.java @@ -0,0 +1,157 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod; + +import com.google.gson.Gson; +import com.terraforged.core.world.terrain.Terrains; +import com.terraforged.mod.biome.provider.BiomeProvider; +import com.terraforged.mod.chunk.ChunkGeneratorFactory; +import com.terraforged.mod.chunk.TerraChunkGenerator; +import com.terraforged.mod.chunk.TerraContext; +import com.terraforged.mod.chunk.TerraGenSettings; +import com.terraforged.mod.chunk.test.TestChunkGenerator; +import com.terraforged.mod.gui.SettingsScreen; +import com.terraforged.mod.settings.TerraSettings; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screen.CreateWorldScreen; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.world.IWorld; +import net.minecraft.world.World; +import net.minecraft.world.WorldType; +import net.minecraft.world.biome.provider.OverworldBiomeProviderSettings; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.OverworldGenSettings; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.Reader; +import java.util.HashSet; +import java.util.Set; + +public class TerraWorld extends WorldType { + + public static final String SETTINGS_FILE_NAME = "terraforged-generator.json"; + + private static final Set types = new HashSet<>(); + public static final TerraWorld TERRA = new TerraWorld("terraforged", TerraChunkGenerator::new); + public static final TerraWorld TEST = new TerraWorld("terratest", TestChunkGenerator::new); + + private static boolean dedicated = false; + + private final ChunkGeneratorFactory factory; + + public TerraWorld(String name, ChunkGeneratorFactory factory) { + super(name); + this.factory = factory; + setCustomOptions(true); + TerraWorld.types.add(this); + } + + @Override + public double getHorizon(World world) { + return 0; + } + + @Override + public float getCloudHeight() { + return 260.0F; + } + + @Override + public ChunkGenerator createChunkGenerator(World world) { + if (world.getDimension().getType() != DimensionType.OVERWORLD) { + return world.getDimension().createChunkGenerator(); + } + + Log.debug("Creating {} generator", world.getDimension().getType()); + + TerraSettings settings = getSettings(world); + settings.generator.seed = world.getSeed(); + + Terrains terrains = Terrains.create(settings); + + OverworldGenSettings genSettings = new TerraGenSettings(settings.structures); + + OverworldBiomeProviderSettings biomeSettings = new OverworldBiomeProviderSettings(world.getWorldInfo()); + biomeSettings.setGeneratorSettings(genSettings); + world.getWorldInfo().setGeneratorOptions(NBTHelper.serializeCompact(settings)); + + TerraContext context = new TerraContext(world, terrains, settings); + BiomeProvider biomeProvider = new BiomeProvider(context); + + return getGeneratorFactory().create(context, biomeProvider, genSettings); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void onCustomizeButton(Minecraft mc, CreateWorldScreen gui) { + mc.displayGuiScreen(new SettingsScreen(gui)); + } + + public ChunkGeneratorFactory getGeneratorFactory() { + return factory; + } + + private static TerraSettings getSettings(IWorld world) { + if (dedicated) { + try (Reader reader = new BufferedReader(new FileReader(new File("config", SETTINGS_FILE_NAME)))) { + Log.info("Loading generator settings from json"); + return new Gson().fromJson(reader, TerraSettings.class); + } catch (Throwable ignored) { + return getSettings(world.getWorldInfo().getGeneratorOptions()); + } + } + return getSettings(world.getWorldInfo().getGeneratorOptions()); + } + + private static TerraSettings getSettings(CompoundNBT root) { + TerraSettings settings = new TerraSettings(); + if (!root.isEmpty()) { + NBTHelper.deserialize(root, settings); + } + return settings; + } + + public static void init() { + Log.info("Registered world type(s)"); + } + + public static void setDedicatedServer() { + dedicated = true; + } + + public static boolean isTerraWorld(IWorld world) { + if (world instanceof World) { + return types.contains(((World) world).getWorldType()); + } + return false; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ColdSteppe.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ColdSteppe.java new file mode 100644 index 0000000..a164a0c --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ColdSteppe.java @@ -0,0 +1,93 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome; + +import com.terraforged.api.biome.BiomeVariant; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IWorldReader; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.biome.DefaultBiomeFeatures; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; + +public class ColdSteppe extends BiomeVariant { + + public ColdSteppe() { + super((new Biome.Builder()).surfaceBuilder(SurfaceBuilder.GIANT_TREE_TAIGA, SurfaceBuilder.GRASS_DIRT_GRAVEL_CONFIG).precipitation(RainType.SNOW).category(Biome.Category.TAIGA).depth(0.2F).scale(0.25F).temperature(0.2F).downfall(0.1F).waterColor(4159204).waterFogColor(329011).parent((String) null)); + DefaultBiomeFeatures.addCarvers(this); + DefaultBiomeFeatures.addStructures(this); +// DefaultBiomeFeatures.addLakes(this); + DefaultBiomeFeatures.addMonsterRooms(this); + DefaultBiomeFeatures.addTaigaLargeFerns(this); + DefaultBiomeFeatures.addStoneVariants(this); + DefaultBiomeFeatures.addOres(this); + DefaultBiomeFeatures.addSedimentDisks(this); +// DefaultBiomeFeatures.addTaigaConifers(this); +// DefaultBiomeFeatures.addDefaultFlowers(this); + DefaultBiomeFeatures.addGrass(this); + DefaultBiomeFeatures.addVeryDenseGrass(this); +// DefaultBiomeFeatures.addMushrooms(this); +// DefaultBiomeFeatures.addReedsAndPumpkins(this); +// DefaultBiomeFeatures.addSprings(this); + DefaultBiomeFeatures.addBerryBushes(this); + DefaultBiomeFeatures.addFreezeTopLayer(this); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.SHEEP, 12, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.PIG, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.CHICKEN, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.COW, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.WOLF, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.RABBIT, 4, 2, 3)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.FOX, 8, 2, 4)); + this.addSpawn(EntityClassification.AMBIENT, new Biome.SpawnListEntry(EntityType.BAT, 10, 8, 8)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SPIDER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE, 95, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE_VILLAGER, 5, 1, 1)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SKELETON, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.CREEPER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SLIME, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ENDERMAN, 10, 1, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.WITCH, 5, 1, 1)); + + setRegistryName("terraforged", "cold_steppe"); + } + + @Override + public boolean doesSnowGenerate(IWorldReader worldIn, BlockPos pos) { + return false; + } + + @Override + public boolean doesWaterFreeze(IWorldReader worldIn, BlockPos pos) { + return false; + } + + @Override + public Biome getBase() { + return Biomes.TAIGA; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ModBiomes.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ModBiomes.java new file mode 100644 index 0000000..70bcc26 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ModBiomes.java @@ -0,0 +1,60 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome; + +import com.terraforged.api.biome.BiomeVariant; +import net.minecraft.world.biome.Biome; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import java.util.ArrayList; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class ModBiomes { + + private static final ArrayList biomes = new ArrayList<>(); + + public static final Biome COLD_STEPPE = register(new ColdSteppe()); + public static final Biome SAVANNA_SCRUB = register(new SavannaScrub()); + public static final Biome SHATTERED_SAVANNA_SCRUB = register(new ShatteredSavannaScrub()); + public static final Biome SNOWY_TAIGA_SCRUB = register(new SnowyTaigaScrub()); + public static final Biome STEPPE = register(new Steppe()); + public static final Biome TAIGA_SCRUB = register(new TaigaScrub()); + public static final Biome WARM_BEACH = register(new WarmBeach()); + + private static Biome register(BiomeVariant biome) { + biomes.add(biome); + return biome; + } + + @SubscribeEvent + public static void register(RegistryEvent.Register event) { + biomes.forEach(event.getRegistry()::register); + biomes.clear(); + biomes.trimToSize(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/SavannaScrub.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/SavannaScrub.java new file mode 100644 index 0000000..854d14d --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/SavannaScrub.java @@ -0,0 +1,111 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome; + +import com.terraforged.api.biome.BiomeVariant; +import net.minecraft.block.BlockState; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.biome.DefaultBiomeFeatures; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.surfacebuilders.DefaultSurfaceBuilder; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilderConfig; + +import java.util.Random; + +public class SavannaScrub extends BiomeVariant { + + public SavannaScrub() { + super((new Biome.Builder()).surfaceBuilder(new Builder(), SurfaceBuilder.GRASS_DIRT_GRAVEL_CONFIG).precipitation(Biome.RainType.NONE).category(Biome.Category.SAVANNA).depth(0.125F).scale(0.05F).temperature(1.2F).downfall(0.0F).waterColor(4159204).waterFogColor(329011).parent((String) null)); + this.setRegistryName("terraforged", "savanna_scrub"); + DefaultBiomeFeatures.addCarvers(this); + DefaultBiomeFeatures.addStructures(this); +// DefaultBiomeFeatures.addLakes(this); + DefaultBiomeFeatures.addMonsterRooms(this); + DefaultBiomeFeatures.addTallGrass(this); + DefaultBiomeFeatures.addStoneVariants(this); + DefaultBiomeFeatures.addOres(this); + DefaultBiomeFeatures.addSedimentDisks(this); +// DefaultBiomeFeatures.addSavannaTrees(this); + DefaultBiomeFeatures.addExtraDefaultFlowers(this); + + // func_222339_L - add grasses - add this a few times since there are no trees + DefaultBiomeFeatures.addGrass(this); + DefaultBiomeFeatures.addVeryDenseGrass(this); + + DefaultBiomeFeatures.addMushrooms(this); + DefaultBiomeFeatures.addReedsAndPumpkins(this); +// DefaultBiomeFeatures.addSprings(this); +// DefaultBiomeFeatures.addFreezeTopLayer(this); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.SHEEP, 12, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.PIG, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.CHICKEN, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.COW, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.HORSE, 1, 2, 6)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.DONKEY, 1, 1, 1)); + this.addSpawn(EntityClassification.AMBIENT, new Biome.SpawnListEntry(EntityType.BAT, 10, 8, 8)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SPIDER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE, 95, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE_VILLAGER, 5, 1, 1)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SKELETON, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.CREEPER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SLIME, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ENDERMAN, 10, 1, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.WITCH, 5, 1, 1)); + } + + @Override + public Biome getBase() { + return Biomes.SAVANNA; + } + + private static class Builder extends DefaultSurfaceBuilder { + + private Builder() { + super(SurfaceBuilderConfig::deserialize); + } + + @Override + public void buildSurface(Random random, IChunk chunkIn, Biome biomeIn, int x, int z, int startHeight, double noise, BlockState defaultBlock, BlockState defaultFluid, int seaLevel, long seed, SurfaceBuilderConfig config) { + super.buildSurface(random, chunkIn, biomeIn, x, z, startHeight, noise, defaultBlock, defaultFluid, seaLevel, seed, config); + BlockPos.Mutable pos = new BlockPos.Mutable(); + if (random.nextInt(5) > 0) { + int dx = x & 15; + int dz = z & 15; + int y = chunkIn.getTopBlockY(Heightmap.Type.MOTION_BLOCKING, dx, dz); + if (y > seaLevel) { + pos.setPos(dx, y, dz); +// chunkIn.setBlockState(pos, Blocks.SAND.getDefaultState(), false); + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ShatteredSavannaScrub.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ShatteredSavannaScrub.java new file mode 100644 index 0000000..d39ca20 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/ShatteredSavannaScrub.java @@ -0,0 +1,114 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome; + +import com.terraforged.api.biome.BiomeVariant; +import net.minecraft.block.BlockState; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.biome.DefaultBiomeFeatures; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.surfacebuilders.DefaultSurfaceBuilder; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilderConfig; + +import java.util.Random; + +public class ShatteredSavannaScrub extends BiomeVariant { + public ShatteredSavannaScrub() { + super((new Biome.Builder()).surfaceBuilder(new Builder(), SurfaceBuilder.GRASS_DIRT_GRAVEL_CONFIG).precipitation(Biome.RainType.NONE).category(Biome.Category.SAVANNA).depth(0.3625F).scale(1.225F).temperature(1.1F).downfall(0.0F).waterColor(4159204).waterFogColor(329011).parent("terraforged:savanna_scrub")); + this.setRegistryName("terraforged", "shattered_savanna_scrub"); + DefaultBiomeFeatures.addCarvers(this); + DefaultBiomeFeatures.addStructures(this); +// DefaultBiomeFeatures.addLakes(this); + DefaultBiomeFeatures.addMonsterRooms(this); + DefaultBiomeFeatures.addStoneVariants(this); + DefaultBiomeFeatures.addOres(this); + DefaultBiomeFeatures.addSedimentDisks(this); +// DefaultBiomeFeatures.addShatteredSavannaTrees(this); + DefaultBiomeFeatures.addDefaultFlowers(this); + + // func_222314_K - addGrasses - add this a few times since there are no trees + DefaultBiomeFeatures.addGrass(this); + DefaultBiomeFeatures.addVeryDenseGrass(this); + + DefaultBiomeFeatures.addMushrooms(this); + DefaultBiomeFeatures.addReedsAndPumpkins(this); +// DefaultBiomeFeatures.addSprings(this); + DefaultBiomeFeatures.addFreezeTopLayer(this); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.SHEEP, 12, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.PIG, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.CHICKEN, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.COW, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.HORSE, 1, 2, 6)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.DONKEY, 1, 1, 1)); + this.addSpawn(EntityClassification.AMBIENT, new Biome.SpawnListEntry(EntityType.BAT, 10, 8, 8)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SPIDER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE, 95, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE_VILLAGER, 5, 1, 1)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SKELETON, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.CREEPER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SLIME, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ENDERMAN, 10, 1, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.WITCH, 5, 1, 1)); + } + + @Override + public boolean isMutation() { + return true; + } + + @Override + public Biome getBase() { + return Biomes.SHATTERED_SAVANNA; + } + + private static class Builder extends DefaultSurfaceBuilder { + + private Builder() { + super(SurfaceBuilderConfig::deserialize); + } + + @Override + public void buildSurface(Random random, IChunk chunkIn, Biome biomeIn, int x, int z, int startHeight, double noise, BlockState defaultBlock, BlockState defaultFluid, int seaLevel, long seed, SurfaceBuilderConfig config) { + SurfaceBuilder.SHATTERED_SAVANNA.buildSurface(random, chunkIn, biomeIn, x, z, startHeight, noise, defaultBlock, defaultFluid, seaLevel, seed, config); + BlockPos.Mutable pos = new BlockPos.Mutable(); + if (random.nextInt(5) > 0) { + int dx = x & 15; + int dz = z & 15; + int y = chunkIn.getTopBlockY(Heightmap.Type.MOTION_BLOCKING, dx, dz); + if (y > seaLevel) { + pos.setPos(dx, y, dz); +// chunkIn.setBlockState(pos, Blocks.SAND.getDefaultState(), false); + } + } + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/SnowyTaigaScrub.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/SnowyTaigaScrub.java new file mode 100644 index 0000000..7f23b7a --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/SnowyTaigaScrub.java @@ -0,0 +1,79 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome; + +import com.terraforged.api.biome.BiomeVariant; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.biome.DefaultBiomeFeatures; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; + +public class SnowyTaigaScrub extends BiomeVariant { + public SnowyTaigaScrub() { + super((new Builder()).surfaceBuilder(SurfaceBuilder.DEFAULT, SurfaceBuilder.GRASS_DIRT_GRAVEL_CONFIG).precipitation(RainType.SNOW).category(Category.TAIGA).depth(0.2F).scale(0.2F).temperature(-0.5F).downfall(0.4F).waterColor(4020182).waterFogColor(329011).parent((String) null)); + this.setRegistryName("terraforged", "snowy_taiga_scrub"); + DefaultBiomeFeatures.addCarvers(this); + DefaultBiomeFeatures.addStructures(this); +// DefaultBiomeFeatures.addLakes(this); + DefaultBiomeFeatures.addMonsterRooms(this); + DefaultBiomeFeatures.addTaigaLargeFerns(this); + DefaultBiomeFeatures.addStoneVariants(this); + DefaultBiomeFeatures.addOres(this); + DefaultBiomeFeatures.addSedimentDisks(this); +// DefaultBiomeFeatures.addTaigaConifers(this); + DefaultBiomeFeatures.addDefaultFlowers(this); + DefaultBiomeFeatures.addTaigaGrassAndMushrooms(this); + DefaultBiomeFeatures.addGrass(this); + DefaultBiomeFeatures.addMushrooms(this); + DefaultBiomeFeatures.addReedsAndPumpkins(this); +// DefaultBiomeFeatures.addSprings(this); + DefaultBiomeFeatures.addSparseBerryBushes(this); + DefaultBiomeFeatures.addFreezeTopLayer(this); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.SHEEP, 12, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.PIG, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.CHICKEN, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.COW, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.WOLF, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.RABBIT, 4, 2, 3)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.FOX, 8, 2, 4)); + this.addSpawn(EntityClassification.AMBIENT, new SpawnListEntry(EntityType.BAT, 10, 8, 8)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SPIDER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ZOMBIE, 95, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ZOMBIE_VILLAGER, 5, 1, 1)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SKELETON, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.CREEPER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SLIME, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ENDERMAN, 10, 1, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.WITCH, 5, 1, 1)); + } + + @Override + public Biome getBase() { + return Biomes.SNOWY_TAIGA; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/Steppe.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/Steppe.java new file mode 100644 index 0000000..a6bbc55 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/Steppe.java @@ -0,0 +1,93 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome; + +import com.terraforged.api.biome.BiomeVariant; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IWorldReader; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.biome.DefaultBiomeFeatures; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; + +public class Steppe extends BiomeVariant { + + protected Steppe() { + super((new Biome.Builder()).surfaceBuilder(SurfaceBuilder.GIANT_TREE_TAIGA, SurfaceBuilder.GRASS_DIRT_GRAVEL_CONFIG).precipitation(RainType.SNOW).category(Category.SAVANNA).depth(0.2F).scale(0.2F).temperature(0.7F).downfall(0.1F).waterColor(4159204).waterFogColor(329011).parent((String) null)); + DefaultBiomeFeatures.addCarvers(this); + DefaultBiomeFeatures.addStructures(this); +// DefaultBiomeFeatures.addLakes(this); + DefaultBiomeFeatures.addMonsterRooms(this); +// DefaultBiomeFeatures.addTaigaRocks(this); +// DefaultBiomeFeatures.addTaigaLargeFerns(this); + DefaultBiomeFeatures.addStoneVariants(this); + DefaultBiomeFeatures.addOres(this); + DefaultBiomeFeatures.addSedimentDisks(this); + // extra grasses as no trees/ferns + DefaultBiomeFeatures.addGrass(this); + DefaultBiomeFeatures.addVeryDenseGrass(this); +// DefaultBiomeFeatures.func_222285_H(this); +// DefaultBiomeFeatures.addDefaultFlowers(this); +// DefaultBiomeFeatures.addMushrooms(this); +// DefaultBiomeFeatures.addReedsAndPumpkins(this); +// DefaultBiomeFeatures.addSprings(this); + DefaultBiomeFeatures.addFreezeTopLayer(this); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.SHEEP, 12, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.PIG, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.CHICKEN, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.COW, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.WOLF, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.RABBIT, 4, 2, 3)); + this.addSpawn(EntityClassification.AMBIENT, new Biome.SpawnListEntry(EntityType.BAT, 10, 8, 8)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SPIDER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SKELETON, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE_VILLAGER, 25, 1, 1)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.CREEPER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SLIME, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ENDERMAN, 10, 1, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.WITCH, 5, 1, 1)); + + setRegistryName("terraforged", "steppe"); + } + + @Override + public boolean doesSnowGenerate(IWorldReader worldIn, BlockPos pos) { + return false; + } + + @Override + public boolean doesWaterFreeze(IWorldReader worldIn, BlockPos pos) { + return false; + } + + @Override + public Biome getBase() { + return Biomes.SAVANNA; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/TaigaScrub.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/TaigaScrub.java new file mode 100644 index 0000000..b735217 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/TaigaScrub.java @@ -0,0 +1,83 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome; + +import com.terraforged.api.biome.BiomeVariant; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.biome.DefaultBiomeFeatures; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; + +public class TaigaScrub extends BiomeVariant { + public TaigaScrub() { + super((new Builder()).surfaceBuilder(SurfaceBuilder.DEFAULT, SurfaceBuilder.GRASS_DIRT_GRAVEL_CONFIG).precipitation(RainType.RAIN).category(Category.TAIGA).depth(0.2F).scale(0.2F).temperature(0.25F).downfall(0.8F).waterColor(4159204).waterFogColor(329011).parent((String) null)); + this.setRegistryName("terraforged", "taiga_scrub"); + DefaultBiomeFeatures.addCarvers(this); + DefaultBiomeFeatures.addStructures(this); +// DefaultBiomeFeatures.addLakes(this); + DefaultBiomeFeatures.addMonsterRooms(this); + DefaultBiomeFeatures.addTaigaLargeFerns(this); + DefaultBiomeFeatures.addStoneVariants(this); + DefaultBiomeFeatures.addOres(this); + DefaultBiomeFeatures.addSedimentDisks(this); +// DefaultBiomeFeatures.addTaigaConifers(this); + DefaultBiomeFeatures.addDefaultFlowers(this); + DefaultBiomeFeatures.addGrass(this); + DefaultBiomeFeatures.addVeryDenseGrass(this); + + // extra grass since there are no trees + DefaultBiomeFeatures.addGrass(this); + DefaultBiomeFeatures.addGrass(this); + + DefaultBiomeFeatures.addReedsAndPumpkins(this); +// DefaultBiomeFeatures.addSprings(this); + DefaultBiomeFeatures.addBerryBushes(this); + DefaultBiomeFeatures.addFreezeTopLayer(this); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.SHEEP, 12, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.PIG, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.CHICKEN, 10, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.COW, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.WOLF, 8, 4, 4)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.RABBIT, 4, 2, 3)); + this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.FOX, 8, 2, 4)); + this.addSpawn(EntityClassification.AMBIENT, new SpawnListEntry(EntityType.BAT, 10, 8, 8)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SPIDER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ZOMBIE, 95, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ZOMBIE_VILLAGER, 5, 1, 1)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SKELETON, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.CREEPER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SLIME, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ENDERMAN, 10, 1, 4)); + this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.WITCH, 5, 1, 1)); + } + + @Override + public Biome getBase() { + return Biomes.TAIGA; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/WarmBeach.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/WarmBeach.java new file mode 100644 index 0000000..3aa0ebb --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/WarmBeach.java @@ -0,0 +1,78 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome; + +import com.terraforged.api.biome.BiomeVariant; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.biome.DefaultBiomeFeatures; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.structure.BuriedTreasureConfig; +import net.minecraft.world.gen.feature.structure.MineshaftConfig; +import net.minecraft.world.gen.feature.structure.MineshaftStructure; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; + +public class WarmBeach extends BiomeVariant { + + public WarmBeach() { + super((new Biome.Builder()).surfaceBuilder(SurfaceBuilder.DEFAULT, SurfaceBuilder.SAND_CONFIG).precipitation(Biome.RainType.RAIN).category(Category.BEACH).depth(-0.5F).scale(0.1F).temperature(1.5F).downfall(0.5F).waterColor(4445678).waterFogColor(270131).parent((String)null)); + this.addStructure(Feature.MINESHAFT.func_225566_b_(new MineshaftConfig(0.004D, MineshaftStructure.Type.NORMAL))); + this.addStructure(Feature.BURIED_TREASURE.func_225566_b_(new BuriedTreasureConfig(0.01F))); + DefaultBiomeFeatures.addCarvers(this); + DefaultBiomeFeatures.addStructures(this); + DefaultBiomeFeatures.addMonsterRooms(this); + DefaultBiomeFeatures.addStoneVariants(this); + DefaultBiomeFeatures.addOres(this); + DefaultBiomeFeatures.addDefaultFlowers(this); + DefaultBiomeFeatures.addSparseGrass(this); + DefaultBiomeFeatures.addMushrooms(this); + DefaultBiomeFeatures.addReedsAndPumpkins(this); + DefaultBiomeFeatures.addFreezeTopLayer(this); + this.addSpawn(EntityClassification.CREATURE, new Biome.SpawnListEntry(EntityType.TURTLE, 5, 2, 5)); + this.addSpawn(EntityClassification.AMBIENT, new Biome.SpawnListEntry(EntityType.BAT, 10, 8, 8)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SPIDER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE, 95, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ZOMBIE_VILLAGER, 5, 1, 1)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SKELETON, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.CREEPER, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.SLIME, 100, 4, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.ENDERMAN, 10, 1, 4)); + this.addSpawn(EntityClassification.MONSTER, new Biome.SpawnListEntry(EntityType.WITCH, 5, 1, 1)); + setRegistryName("terraforged", "warm_beach"); + } + + @Override + public Biome.TempCategory getTempCategory() { + return TempCategory.WARM; + } + + @Override + public Biome getBase() { + return Biomes.WARM_OCEAN; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/AbstractBiomeMap.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/AbstractBiomeMap.java new file mode 100644 index 0000000..3ebd34a --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/AbstractBiomeMap.java @@ -0,0 +1,198 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.map; + +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.terraforged.core.world.biome.BiomeType; +import com.terraforged.mod.biome.ModBiomes; +import com.terraforged.mod.biome.provider.BiomeHelper; +import me.dags.noise.util.NoiseUtil; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public abstract class AbstractBiomeMap implements BiomeMap { + + private final Biome[][] beach; + private final Biome[][] river; + private final Biome[][] ocean; + private final Biome[][] deepOcean; + + protected AbstractBiomeMap(BiomeMapBuilder builder) { + river = builder.rivers(); + beach = builder.beaches(); + ocean = builder.oceans(); + deepOcean = builder.deepOceans(); + } + + @Override + public Biome getBeach(float temperature, float moisture, float shape) { + return get(beach, getCategory(temperature), shape, defaultBeach(temperature)); + } + + @Override + public Biome getRiver(float temperature, float moisture, float shape) { + return get(river, getCategory(temperature), shape, defaultRiver(temperature)); + } + + @Override + public Biome getOcean(float temperature, float moisture, float shape) { + return get(ocean, getCategory(temperature), shape, defaultOcean(temperature)); + } + + @Override + public Biome getDeepOcean(float temperature, float moisture, float shape) { + return get(deepOcean, getCategory(temperature), shape, defaultDeepOcean(temperature)); + } + + @Override + public Set getOceanBiomes(Biome.TempCategory temp) { + return Sets.newHashSet(ocean[temp.ordinal() - 1]); + } + + @Override + public Set getDeepOceanBiomes(Biome.TempCategory temp) { + return Sets.newHashSet(deepOcean[temp.ordinal() - 1]); + } + + @Override + public Set getRivers(Biome.TempCategory temp) { + return Sets.newHashSet(river[temp.ordinal() - 1]); + } + + @Override + public JsonObject toJson() { + JsonObject root = new JsonObject(); + root.add("rivers", collect(river)); + root.add("beaches", collect(river)); + root.add("oceans", collect(ocean)); + root.add("deepOceans", collect(deepOcean)); + return root; + } + + private JsonObject collect(Biome[][] biomes) { + JsonObject root = new JsonObject(); + for (Biome.TempCategory temp : Biome.TempCategory.values()) { + if (temp == Biome.TempCategory.OCEAN) { + continue; + } + JsonArray array = new JsonArray(); + Biome[] group = biomes[temp.ordinal() - 1]; + if (group != null) { + Set set = new HashSet<>(); + Collections.addAll(set, group); + set.stream().map(BiomeHelper::getId).sorted().forEach(array::add); + } + root.add(temp.name(), array); + } + return root; + } + + + protected Biome.TempCategory getCategory(float value) { + if (value < 0.25) { + return Biome.TempCategory.COLD; + } + if (value > 0.75) { + return Biome.TempCategory.WARM; + } + return Biome.TempCategory.MEDIUM; + } + + protected Biome defaultBeach(float temperature) { + if (temperature < 0.25) { + return Biomes.SNOWY_BEACH; + } + if (temperature > 0.75) { + return ModBiomes.WARM_BEACH; + } + return Biomes.BEACH; + } + + protected Biome defaultRiver(float temperature) { + if (temperature < 0.15) { + return Biomes.FROZEN_RIVER; + } + return Biomes.RIVER; + } + + protected Biome defaultOcean(float temperature) { + if (temperature < 0.3) { + return Biomes.FROZEN_OCEAN; + } + if (temperature > 0.7) { + return Biomes.WARM_OCEAN; + } + return Biomes.OCEAN; + } + + protected Biome defaultDeepOcean(float temperature) { + if (temperature < 0.3) { + return Biomes.DEEP_FROZEN_OCEAN; + } + if (temperature > 0.7) { + return Biomes.DEEP_WARM_OCEAN; + } + return Biomes.DEEP_OCEAN; + } + + protected Biome defaultBiome(float temperature, float moisture) { + if (temperature < 0.3) { + return ModBiomes.TAIGA_SCRUB; + } + if (temperature > 0.7) { + return ModBiomes.SAVANNA_SCRUB; + } + return Biomes.PLAINS; + } + + protected Biome get(Biome[][] group, Biome.TempCategory category, float shape, Biome def) { + return get(group, category.ordinal() - 1, shape, def); + } + + protected Biome get(Biome[][] group, BiomeType type, float shape, Biome def) { + return get(group, type.ordinal(), shape, def); + } + + protected Biome get(Biome[][] group, int ordinal, float shape, Biome def) { + if (ordinal >= group.length) { + return def; + } + + Biome[] biomes = group[ordinal]; + if (biomes == null || biomes.length == 0) { + return def; + } + + int index = NoiseUtil.round((biomes.length - 1) * shape); + return biomes[index]; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BasicBiomeMap.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BasicBiomeMap.java new file mode 100644 index 0000000..49b0a72 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BasicBiomeMap.java @@ -0,0 +1,82 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.map; + +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.terraforged.core.world.biome.BiomeType; +import com.terraforged.mod.biome.provider.BiomeHelper; +import net.minecraft.world.biome.Biome; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class BasicBiomeMap extends AbstractBiomeMap { + + private final Biome[][] biomeTypes; + + public BasicBiomeMap(BiomeMapBuilder builder) { + super(builder); + biomeTypes = builder.biomeList(); + } + + @Override + public List getAllBiomes(BiomeType type) { + if (type.ordinal() >= biomeTypes.length) { + return Collections.emptyList(); + } + return Arrays.asList(biomeTypes[type.ordinal()]); + } + + @Override + public Set getBiomes(BiomeType type) { + if (type.ordinal() >= biomeTypes.length) { + return Collections.emptySet(); + } + return Sets.newHashSet(biomeTypes[type.ordinal()]); + } + + @Override + public Biome getBiome(BiomeType type, float temperature, float moisture, float shape) { + return get(biomeTypes, type, shape, defaultBiome(temperature, moisture)); + } + + @Override + public JsonObject toJson() { + JsonObject groups = new JsonObject(); + for (BiomeType type : BiomeType.values()) { + JsonArray group = new JsonArray(); + getBiomes(type).stream().map(BiomeHelper::getId).sorted().forEach(group::add); + groups.add(type.name(), group); + } + JsonObject root = super.toJson(); + root.add("biomes", groups); + return root; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeGroup.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeGroup.java new file mode 100644 index 0000000..98fd7cc --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeGroup.java @@ -0,0 +1,38 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.map; + +import com.terraforged.core.util.grid.FixedGrid; +import net.minecraft.world.biome.Biome; + +public class BiomeGroup { + + public final FixedGrid biomes; + + public BiomeGroup(FixedGrid biomes) { + this.biomes = biomes; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeMap.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeMap.java new file mode 100644 index 0000000..db2ae58 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeMap.java @@ -0,0 +1,71 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.map; + +import com.google.gson.JsonElement; +import com.terraforged.core.world.biome.BiomeType; +import net.minecraft.world.biome.Biome; + +import java.util.List; +import java.util.Set; + +public interface BiomeMap { + + Biome getBeach(float temperature, float moisture, float shape); + + Biome getRiver(float temperature, float moisture, float shape); + + Biome getOcean(float temperature, float moisture, float shape); + + Biome getDeepOcean(float temperature, float moisture, float shape); + + Biome getBiome(BiomeType type, float temperature, float moisture, float shape); + + List getAllBiomes(BiomeType type); + + Set getBiomes(BiomeType type); + + Set getRivers(Biome.TempCategory temp); + + Set getOceanBiomes(Biome.TempCategory temp); + + Set getDeepOceanBiomes(Biome.TempCategory temp); + + JsonElement toJson(); + + interface Builder { + + Builder addBeach(Biome biome, int count); + + Builder addRiver(Biome biome, int count); + + Builder addOcean(Biome biome, int count); + + Builder addBiome(BiomeType type, Biome biome, int count); + + BiomeMap build(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeMapBuilder.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeMapBuilder.java new file mode 100644 index 0000000..281e9b5 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomeMapBuilder.java @@ -0,0 +1,176 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.map; + +import com.terraforged.core.util.grid.FixedGrid; +import com.terraforged.core.world.biome.BiomeData; +import com.terraforged.core.world.biome.BiomeType; +import com.terraforged.mod.biome.provider.BiomeHelper; +import com.terraforged.mod.util.ListUtils; +import net.minecraft.world.biome.Biome; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public class BiomeMapBuilder implements BiomeMap.Builder { + + private final Map> rivers = new HashMap<>(); + private final Map> beaches = new HashMap<>(); + private final Map> oceans = new HashMap<>(); + private final Map> deepOceans = new HashMap<>(); + private final Map> map = new EnumMap<>(BiomeType.class); + private final Map dataMap = new HashMap<>(); + + private final int gridSize; + private final Function constructor; + + BiomeMapBuilder(Function constructor, int gridSize, List biomes) { + this.constructor = constructor; + this.gridSize = gridSize; + + for (BiomeData data : biomes) { + dataMap.put((Biome) data.reference, data); + } + } + + @Override + public BiomeMapBuilder addOcean(Biome biome, int count) { + Biome.TempCategory category = BiomeHelper.getTempCategory(biome); + if (biome.getDepth() < -1) { + add(deepOceans.computeIfAbsent(category, c -> new ArrayList<>()), biome, count); + } else { + add(oceans.computeIfAbsent(category, c -> new ArrayList<>()), biome, count); + } + return this; + } + + @Override + public BiomeMap.Builder addBeach(Biome biome, int count) { + Biome.TempCategory category = BiomeHelper.getTempCategory(biome); + add(beaches.computeIfAbsent(category, c -> new ArrayList<>()), biome, count); + return this; + } + + @Override + public BiomeMapBuilder addRiver(Biome biome, int count) { + Biome.TempCategory category = BiomeHelper.getTempCategory(biome); + add(rivers.computeIfAbsent(category, c -> new ArrayList<>()), biome, count); + return this; + } + + @Override + public BiomeMapBuilder addBiome(BiomeType type, Biome biome, int count) { + add(map.computeIfAbsent(type, t -> new ArrayList<>()), biome, count); + return this; + } + + @Override + public BiomeMap build() { + return constructor.apply(this); + } + + Biome[][] rivers() { + return collectTemps(rivers); + } + + Biome[][] beaches() { + return collectTemps(beaches); + } + + Biome[][] oceans() { + return collectTemps(oceans); + } + + Biome[][] deepOceans() { + return collectTemps(deepOceans); + } + + Biome[][] biomeList() { + return collectTypes(map); + } + + BiomeGroup[] biomeGroups() { + BiomeGroup[] biomes = new BiomeGroup[BiomeType.values().length]; + + Function moisture = b -> dataMap.get(b).rainfall; + Function temperature = b -> dataMap.get(b).temperature; + for (BiomeType type : BiomeType.values()) { + List list = map.getOrDefault(type, Collections.emptyList()); + if (list.isEmpty()) { + continue; + } + FixedGrid grid = FixedGrid.generate(gridSize, list, moisture, temperature); + biomes[type.ordinal()] = new BiomeGroup(grid); + } + + return biomes; + } + + private void add(List list, Biome biome, int count) { + for (int i = 0; i < count; i++) { + list.add(biome); + } + } + + private Biome[][] collectTemps(Map> map) { + Biome[][] biomes = new Biome[3][]; + for (Biome.TempCategory category : Biome.TempCategory.values()) { + if (category == Biome.TempCategory.OCEAN) { + continue; + } + List list = map.getOrDefault(category, Collections.emptyList()); + list = ListUtils.minimize(list); + list.sort(Comparator.comparing(BiomeHelper::getId)); + biomes[category.ordinal() - 1] = list.toArray(new Biome[0]); + } + return biomes; + } + + private Biome[][] collectTypes(Map> map) { + Biome[][] biomes = new Biome[BiomeType.values().length][]; + for (BiomeType type : BiomeType.values()) { + List list = map.getOrDefault(type, Collections.emptyList()); + list = ListUtils.minimize(list); + list.sort(Comparator.comparing(BiomeHelper::getId)); + biomes[type.ordinal()] = list.toArray(new Biome[0]); + } + return biomes; + } + + public static BiomeMap.Builder basic(List biomes) { + return new BiomeMapBuilder(BasicBiomeMap::new, 0, biomes); + } + + public static BiomeMap.Builder grid(int size, List biomes) { + return new BiomeMapBuilder(GridBiomeMap::new, size, biomes); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomePredicate.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomePredicate.java new file mode 100644 index 0000000..8b58fcd --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/BiomePredicate.java @@ -0,0 +1,101 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.map; + +import com.terraforged.core.world.biome.BiomeData; +import com.terraforged.mod.biome.provider.BiomeHelper; +import net.minecraft.world.biome.Biome; + +import java.util.function.BiPredicate; + +public interface BiomePredicate { + + boolean test(BiomeData data, Biome biome); + + default boolean test(BiomeData data) { + return test(data, (Biome) data.reference); + } + + default BiomePredicate and(BiomePredicate other) { + return (d, b) -> this.test(d, b) && other.test(d, b); + } + + default BiomePredicate not(BiomePredicate other) { + return (d, b) -> this.test(d, b) && !other.test(d, b); + } + + default BiomePredicate or(BiomePredicate other) { + return (d, b) -> this.test(d, b) || other.test(d, b); + } + + static BiomePredicate name(String... name) { + return (d, b) -> anyMatch(BiomeHelper.getId(b), name, String::contains); + } + + static BiomePredicate type(Biome.Category... categories) { + return (d, b) -> anyMatch(b.getCategory(), categories, (c1, c2) -> c1 == c2); + } + + static BiomePredicate rain(double min, double max) { + return (d, b) -> d.rainfall >= min && d.rainfall <= max; + } + + static BiomePredicate rainType(Biome.RainType... rainTypes) { + return (d, b) -> anyMatch(b.getPrecipitation(), rainTypes, (c1, c2) -> c1 == c2); + } + + static BiomePredicate temp(double min, double max) { + return (d, b) -> d.temperature >= min && d.temperature <= max; + } + + static BiomePredicate depth(double min, double max) { + return (d, b) -> b.getDepth() >= min && b.getDepth() <= max; + } + + static boolean anyMatch(T value, T[] test, BiPredicate tester) { + for (T t : test) { + if (tester.test(value, t)) { + return true; + } + } + return false; + } + + BiomePredicate COAST = type(Biome.Category.BEACH); + BiomePredicate WETLAND = type(Biome.Category.SWAMP); + BiomePredicate DESERT = type(Biome.Category.DESERT).or(temp(0.9, 2).and(rain(-1, 0.2))); + BiomePredicate SAVANNA = type(Biome.Category.SAVANNA).or(temp(0.8, 2).and(rain(-1, 0.4))); + BiomePredicate MESA = type(Biome.Category.MESA); + BiomePredicate STEPPE = name("steppe").and(temp(0.3, 1)); + BiomePredicate COLD_STEPPE = name("steppe").and(temp(-1, 0.3)); + BiomePredicate GRASSLAND = type(Biome.Category.PLAINS); + BiomePredicate TEMPERATE_FOREST = type(Biome.Category.FOREST).and(rain(-1, 0.81)); + BiomePredicate TEMPERATE_RAINFOREST = type(Biome.Category.FOREST).and(rain(0.8, 2)); + BiomePredicate TROPICAL_RAINFOREST = type(Biome.Category.JUNGLE); + BiomePredicate TAIGA = type(Biome.Category.TAIGA).or(temp(0.19, 0.35)).not(rainType(Biome.RainType.SNOW)); + BiomePredicate TUNDRA = type(Biome.Category.ICY).or(temp(-1, 0.21).and(rainType(Biome.RainType.SNOW))); + BiomePredicate MOUNTAIN = type(Biome.Category.EXTREME_HILLS).or(name("mountain")); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/GridBiomeMap.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/GridBiomeMap.java new file mode 100644 index 0000000..eb0a02f --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/map/GridBiomeMap.java @@ -0,0 +1,127 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.map; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.terraforged.core.util.grid.FixedList; +import com.terraforged.core.util.grid.MappedList; +import com.terraforged.core.world.biome.BiomeType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class GridBiomeMap extends AbstractBiomeMap { + + private final BiomeGroup[] biomeTypes; + + GridBiomeMap(BiomeMapBuilder builder) { + super(builder); + biomeTypes = builder.biomeGroups(); + } + + @Override + public Biome getBiome(BiomeType type, float temperature, float moisture, float shape) { + BiomeGroup group = biomeTypes[type.ordinal()]; + if (group == null) { + return Biomes.NETHER; + } + return group.biomes.get(moisture, temperature, shape); + } + + @Override + public List getAllBiomes(BiomeType type) { + BiomeGroup group = biomeTypes[type.ordinal()]; + if (group == null) { + return Collections.emptyList(); + } + List biomes = new LinkedList<>(); + for (MappedList> row : group.biomes) { + for (FixedList cell : row) { + for (Biome biome : cell) { + biomes.add(biome); + } + } + } + return biomes; + } + + @Override + public Set getBiomes(BiomeType type) { + BiomeGroup group = biomeTypes[type.ordinal()]; + if (group == null) { + return Collections.emptySet(); + } + Set biomes = new HashSet<>(); + for (MappedList> row : group.biomes) { + for (FixedList cell : row) { + for (Biome biome : cell) { + biomes.add(biome); + } + } + } + return biomes; + } + + @Override + public JsonObject toJson() { + JsonObject root = new JsonObject(); + for (BiomeType type : BiomeType.values()) { + BiomeGroup group = biomeTypes[type.ordinal()]; + JsonObject grid = new JsonObject(); + if (group != null) { + int rowCount = 0; + float maxRow = group.biomes.size() - 1; + for (MappedList> row : group.biomes) { + int colCount = 0; + float maxCol = row.size() - 1; + + JsonObject rowJson = new JsonObject(); + for (FixedList cell : row) { + JsonArray colJson = new JsonArray(); + for (Biome biome : cell.uniqueValues()) { + colJson.add(biome.getRegistryName() + ""); + } + float colId = colCount / maxCol; + rowJson.add("" + colId, colJson); + colCount++; + } + + float rowId = rowCount / maxRow; + grid.add("" + rowId, rowJson); + rowCount++; + } + } + root.add(type.name(), grid); + } + return root; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/AbstractMaxHeightModifier.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/AbstractMaxHeightModifier.java new file mode 100644 index 0000000..8915c6d --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/AbstractMaxHeightModifier.java @@ -0,0 +1,67 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.modifier; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.util.Seed; +import com.terraforged.core.world.climate.Climate; +import com.terraforged.core.world.terrain.Terrain; +import me.dags.noise.Module; +import me.dags.noise.Source; +import net.minecraft.world.biome.Biome; + +public abstract class AbstractMaxHeightModifier extends AbstractOffsetModifier { + + private final float minHeight; + private final float maxHeight; + private final float range; + private final Module variance; + + public AbstractMaxHeightModifier(Seed seed, Climate climate, int scale, int octaves, float variance, float minHeight, float maxHeight) { + super(climate); + this.minHeight = minHeight; + this.maxHeight = maxHeight; + this.range = maxHeight - minHeight; + this.variance = Source.perlin(seed.next(), scale, octaves).scale(variance); + } + + @Override + protected final Biome modify(Biome in, Cell cell, int x, int z, float ox, float oz) { + float var = variance.getValue(x, z); + float value = cell.value + var; + if (value < minHeight) { + return in; + } + if (value > maxHeight) { + return getModifiedBiome(in, cell, x, z, ox, oz); + } + float alpha = (value - minHeight) / range; + cell.biomeEdge *= alpha; + return in; + } + + protected abstract Biome getModifiedBiome(Biome in, Cell cell, int x, int z, float ox, float oz); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/AbstractOffsetModifier.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/AbstractOffsetModifier.java new file mode 100644 index 0000000..0f9031a --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/AbstractOffsetModifier.java @@ -0,0 +1,50 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.modifier; + +import com.terraforged.api.biome.modifier.BiomeModifier; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.climate.Climate; +import com.terraforged.core.world.terrain.Terrain; +import net.minecraft.world.biome.Biome; + +public abstract class AbstractOffsetModifier implements BiomeModifier { + + private final Climate climate; + + public AbstractOffsetModifier(Climate climate) { + this.climate = climate; + } + + @Override + public Biome modify(Biome in, Cell cell, int x, int z) { + float dx = climate.getOffsetX(x, z, 50); + float dz = climate.getOffsetX(x, z, 50); + return modify(in, cell, x, z, x + dx, z + dz); + } + + protected abstract Biome modify(Biome in, Cell cell, int x, int z, float ox, float oz); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/BeachModifier.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/BeachModifier.java new file mode 100644 index 0000000..31abce2 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/BeachModifier.java @@ -0,0 +1,62 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.modifier; + +import com.terraforged.api.biome.modifier.BiomeModifier; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.core.world.terrain.Terrains; +import com.terraforged.mod.biome.map.BiomeMap; +import net.minecraft.world.biome.Biome; + +public class BeachModifier implements BiomeModifier { + + private final Terrains terrain; + private final BiomeMap biomeMap; + + public BeachModifier(BiomeMap biomeMap, Terrains terrain) { + this.terrain = terrain; + this.biomeMap = biomeMap; + } + + @Override + public int priority() { + return 0; + } + + @Override + public boolean test(Biome biome) { + return true; + } + + @Override + public Biome modify(Biome in, Cell cell, int x, int z) { + if (cell.tag == terrain.beach) { + return biomeMap.getBeach(cell.temperature, cell.moisture, cell.biome); + } + return in; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/BiomeModifierManager.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/BiomeModifierManager.java new file mode 100644 index 0000000..86a0300 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/BiomeModifierManager.java @@ -0,0 +1,95 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.modifier; + +import com.terraforged.api.biome.modifier.BiomeModifier; +import com.terraforged.api.biome.modifier.ModifierManager; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.biome.BiomeType; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.biome.map.BiomeMap; +import com.terraforged.mod.biome.provider.DesertBiomes; +import com.terraforged.mod.chunk.TerraContext; +import net.minecraft.world.biome.Biome; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class BiomeModifierManager implements BiomeModifier, ModifierManager { + + private final DesertBiomes desertBiomes; + private final List biomeModifiers; + + public BiomeModifierManager(TerraContext context, BiomeMap biomes) { + desertBiomes = new DesertBiomes(context.materials, biomes.getAllBiomes(BiomeType.DESERT)); + List modifiers = new ArrayList<>(); + modifiers.add(new BeachModifier(biomes, context.terrain)); + modifiers.add(new DesertColorModifier(desertBiomes)); + modifiers.add(new SandBiomeModifier( + context.seed, + context.factory.getClimate(), + context.levels + )); + modifiers.add(new SwampModifier( + context.seed, + context.factory.getClimate(), + context.levels + )); + Collections.sort(modifiers); + this.biomeModifiers = modifiers; + } + + @Override + public void register(BiomeModifier modifier) { + biomeModifiers.add(modifier); + Collections.sort(biomeModifiers); + } + + public DesertBiomes getDesertBiomes() { + return desertBiomes; + } + + @Override + public int priority() { + return -1; + } + + @Override + public boolean test(Biome biome) { + return true; + } + + @Override + public Biome modify(Biome biome, Cell cell, int x, int z) { + for (BiomeModifier modifier : biomeModifiers) { + if (modifier.test(biome)) { + biome = modifier.modify(biome, cell, x, z); + } + } + return biome; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/DesertColorModifier.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/DesertColorModifier.java new file mode 100644 index 0000000..d941f23 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/DesertColorModifier.java @@ -0,0 +1,63 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.modifier; + +import com.terraforged.api.biome.modifier.BiomeModifier; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.biome.provider.DesertBiomes; +import net.minecraft.world.biome.Biome; + +public class DesertColorModifier implements BiomeModifier { + + private final DesertBiomes biomes; + + public DesertColorModifier(DesertBiomes biomes) { + this.biomes = biomes; + } + + @Override + public int priority() { + return 0; + } + + @Override + public boolean test(Biome biome) { + return biome.getCategory() == Biome.Category.DESERT; + } + + @Override + public Biome modify(Biome in, Cell cell, int x, int z) { + if (biomes.isRedDesert(in)) { + if (cell.continent <= 0.5F) { + return biomes.getWhiteDesert(cell.biome); + } + } else if (cell.continent > 0.5F) { + return biomes.getRedDesert(cell.biome); + } + return in; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/SandBiomeModifier.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/SandBiomeModifier.java new file mode 100644 index 0000000..bd5279b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/SandBiomeModifier.java @@ -0,0 +1,67 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.modifier; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.util.Seed; +import com.terraforged.core.world.climate.Climate; +import com.terraforged.core.world.heightmap.Levels; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.material.MaterialHelper; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Set; +import java.util.stream.Collectors; + +// prevents deserts forming at high levels +public class SandBiomeModifier extends AbstractMaxHeightModifier { + + private final Set biomes; + + public SandBiomeModifier(Seed seed, Climate climate, Levels levels) { + super(seed, climate, 50, 2, levels.scale(8), levels.ground(5), levels.ground(25)); + this.biomes = ForgeRegistries.BIOMES.getValues().stream() + .filter(biome -> MaterialHelper.isSand(biome.getSurfaceBuilderConfig().getTop().getBlock())) + .collect(Collectors.toSet()); + } + + @Override + public int priority() { + return 1; + } + + @Override + public boolean test(Biome biome) { + return biome.getCategory() == Biome.Category.DESERT || biomes.contains(biome); + } + + @Override + protected Biome getModifiedBiome(Biome in, Cell cell, int x, int z, float ox, float oz) { + return Biomes.BADLANDS; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/SwampModifier.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/SwampModifier.java new file mode 100644 index 0000000..2cd031b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/modifier/SwampModifier.java @@ -0,0 +1,57 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.modifier; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.util.Seed; +import com.terraforged.core.world.climate.Climate; +import com.terraforged.core.world.heightmap.Levels; +import com.terraforged.core.world.terrain.Terrain; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; + +// prevents swamps forming at high levels +public class SwampModifier extends AbstractMaxHeightModifier { + + public SwampModifier(Seed seed, Climate climate, Levels levels) { + super(seed, climate, 60, 1, levels.scale(10), levels.ground(4), levels.ground(12)); + } + + @Override + protected Biome getModifiedBiome(Biome in, Cell cell, int x, int z, float ox, float oz) { + return Biomes.PLAINS; + } + + @Override + public int priority() { + return 0; + } + + @Override + public boolean test(Biome biome) { + return biome.getCategory() == Biome.Category.SWAMP; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/AbstractBiomeProvider.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/AbstractBiomeProvider.java new file mode 100644 index 0000000..75bd5c3 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/AbstractBiomeProvider.java @@ -0,0 +1,65 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.provider; + +import com.google.common.collect.ImmutableSet; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; + +import java.util.List; +import java.util.Random; +import java.util.Set; + +public abstract class AbstractBiomeProvider extends net.minecraft.world.biome.provider.BiomeProvider { + + protected static final Set defaultBiomes = ImmutableSet.of(Biomes.OCEAN, Biomes.PLAINS, Biomes.DESERT, Biomes.MOUNTAINS, Biomes.FOREST, Biomes.TAIGA, Biomes.SWAMP, Biomes.RIVER, Biomes.FROZEN_OCEAN, Biomes.FROZEN_RIVER, Biomes.SNOWY_TUNDRA, Biomes.SNOWY_MOUNTAINS, Biomes.MUSHROOM_FIELDS, Biomes.MUSHROOM_FIELD_SHORE, Biomes.BEACH, Biomes.DESERT_HILLS, Biomes.WOODED_HILLS, Biomes.TAIGA_HILLS, Biomes.MOUNTAIN_EDGE, Biomes.JUNGLE, Biomes.JUNGLE_HILLS, Biomes.JUNGLE_EDGE, Biomes.DEEP_OCEAN, Biomes.STONE_SHORE, Biomes.SNOWY_BEACH, Biomes.BIRCH_FOREST, Biomes.BIRCH_FOREST_HILLS, Biomes.DARK_FOREST, Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS, Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS, Biomes.WOODED_MOUNTAINS, Biomes.SAVANNA, Biomes.SAVANNA_PLATEAU, Biomes.BADLANDS, Biomes.WOODED_BADLANDS_PLATEAU, Biomes.BADLANDS_PLATEAU, Biomes.WARM_OCEAN, Biomes.LUKEWARM_OCEAN, Biomes.COLD_OCEAN, Biomes.DEEP_WARM_OCEAN, Biomes.DEEP_LUKEWARM_OCEAN, Biomes.DEEP_COLD_OCEAN, Biomes.DEEP_FROZEN_OCEAN, Biomes.SUNFLOWER_PLAINS, Biomes.DESERT_LAKES, Biomes.GRAVELLY_MOUNTAINS, Biomes.FLOWER_FOREST, Biomes.TAIGA_MOUNTAINS, Biomes.SWAMP_HILLS, Biomes.ICE_SPIKES, Biomes.MODIFIED_JUNGLE, Biomes.MODIFIED_JUNGLE_EDGE, Biomes.TALL_BIRCH_FOREST, Biomes.TALL_BIRCH_HILLS, Biomes.DARK_FOREST_HILLS, Biomes.SNOWY_TAIGA_MOUNTAINS, Biomes.GIANT_SPRUCE_TAIGA, Biomes.GIANT_SPRUCE_TAIGA_HILLS, Biomes.MODIFIED_GRAVELLY_MOUNTAINS, Biomes.SHATTERED_SAVANNA, Biomes.SHATTERED_SAVANNA_PLATEAU, Biomes.ERODED_BADLANDS, Biomes.MODIFIED_WOODED_BADLANDS_PLATEAU, Biomes.MODIFIED_BADLANDS_PLATEAU); + + public AbstractBiomeProvider() { + super(defaultBiomes); + } + + @Override + public final Biome getNoiseBiome(int x, int y, int z) { + return getBiome(x, y, z); + } + + @Override + public final Set func_225530_a_(int x, int y, int z, int size) { + return getBiomesInSquare(x, y, z, size); + } + + @Override + public final BlockPos func_225531_a_(int centerX, int centerY, int centerZ, int range, List biomes, Random random) { + return findBiomePosition(centerX, centerY, centerZ, range, biomes, random); + } + + public abstract Biome getBiome(int x, int y, int z); + + public abstract Set getBiomesInSquare(int x, int y, int z, int size); + + public abstract BlockPos findBiomePosition(int centerX, int centerY, int centerZ, int range, List biomes, Random random); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeHelper.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeHelper.java new file mode 100644 index 0000000..75fbe47 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeHelper.java @@ -0,0 +1,243 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.provider; + +import com.google.common.collect.Sets; +import com.terraforged.core.settings.BiomeSettings; +import com.terraforged.core.world.biome.BiomeData; +import com.terraforged.core.world.biome.BiomeType; +import com.terraforged.mod.biome.ModBiomes; +import com.terraforged.mod.biome.map.BiomeMap; +import com.terraforged.mod.biome.map.BiomeMapBuilder; +import com.terraforged.mod.biome.map.BiomePredicate; +import me.dags.noise.util.Vec2f; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.structure.OceanRuinConfig; +import net.minecraft.world.gen.feature.structure.OceanRuinStructure; +import net.minecraftforge.common.BiomeDictionary; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class BiomeHelper { + + private static final Set IGNORE_BIOMES = Sets.newHashSet( + Biomes.THE_END, + Biomes.END_BARRENS, + Biomes.END_HIGHLANDS, + Biomes.END_MIDLANDS, + Biomes.SMALL_END_ISLANDS, + Biomes.NETHER, + Biomes.THE_VOID, + Biomes.JUNGLE_EDGE, + Biomes.MODIFIED_JUNGLE_EDGE, + Biomes.MOUNTAIN_EDGE, + Biomes.STONE_SHORE + ); + + private static final Map PREDICATES = new HashMap() {{ + put(BiomeType.TROPICAL_RAINFOREST, BiomePredicate.TROPICAL_RAINFOREST); + put(BiomeType.SAVANNA, BiomePredicate.SAVANNA.or(BiomePredicate.MESA).not(BiomePredicate.DESERT).not(BiomePredicate.STEPPE).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN).not(BiomePredicate.WETLAND)); + put(BiomeType.DESERT, BiomePredicate.DESERT.or(BiomePredicate.MESA).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN).not(BiomePredicate.WETLAND)); + put(BiomeType.TEMPERATE_RAINFOREST, BiomePredicate.TEMPERATE_RAINFOREST.not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN)); + put(BiomeType.TEMPERATE_FOREST, BiomePredicate.TEMPERATE_FOREST.not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN).not(BiomePredicate.WETLAND)); + put(BiomeType.GRASSLAND, BiomePredicate.GRASSLAND.or(BiomePredicate.WETLAND).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN)); + put(BiomeType.COLD_STEPPE, BiomePredicate.COLD_STEPPE.not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN)); + put(BiomeType.STEPPE, BiomePredicate.STEPPE.not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN)); + put(BiomeType.TAIGA, BiomePredicate.TAIGA.not(BiomePredicate.TUNDRA).not(BiomePredicate.COLD_STEPPE).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN)); + put(BiomeType.TUNDRA, BiomePredicate.TUNDRA.not(BiomePredicate.TAIGA).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN)); + put(BiomeType.ALPINE, BiomePredicate.MOUNTAIN); + }}; + + public static BiomeMap.Builder getBuilder(List biomes) { + return BiomeMapBuilder.basic(biomes); + } + + public static BiomeMap getDefaultBiomeMap() { + List biomes = getAllBiomeData(); + BiomeMap.Builder builder = getBuilder(biomes); + for (BiomeData data : biomes) { + int weight = 10; + Biome biome = (Biome) data.reference; + + if (biome.isMutation() && getId(biome).contains("hills")) { + continue; + } + + if (biome.getCategory() == Biome.Category.FOREST) { + weight = 5; + } + + if (biome.getCategory() == Biome.Category.MUSHROOM) { + weight = 1; + } + + // don't use BiomeDictionary with transient biomes + if (ForgeRegistries.BIOMES.containsKey(biome.getRegistryName())) { + if (BiomeDictionary.getTypes(biome).contains(BiomeDictionary.Type.RARE)) { + weight = 1; + } + } + + if (biome.getCategory() == Biome.Category.OCEAN) { + builder.addOcean(biome, weight); + } else if (biome.getCategory() == Biome.Category.RIVER) { + builder.addRiver(biome, weight); + } else if (biome.getCategory() == Biome.Category.BEACH || biome == Biomes.STONE_SHORE) { + builder.addBeach(biome, weight); + } else { + Collection types = getTypes(data, biome); + for (BiomeType type : types) { + builder.addBiome(type, biome, weight); + } + } + } + + builder.addBiome(BiomeType.TEMPERATE_RAINFOREST, Biomes.PLAINS, 10); + builder.addBiome(BiomeType.TEMPERATE_FOREST, Biomes.FLOWER_FOREST, 3); + builder.addBiome(BiomeType.TEMPERATE_FOREST, Biomes.PLAINS, 10); + builder.addBiome(BiomeType.TUNDRA, ModBiomes.SNOWY_TAIGA_SCRUB, 5); + builder.addBiome(BiomeType.TAIGA, ModBiomes.TAIGA_SCRUB, 5); + + return builder.build(); + } + + public static BiomeMap getBiomeMap(BiomeSettings settings) { + List biomes = getAllBiomeData(); + BiomeMap.Builder builder = getBuilder(biomes); + Map biomeMap = biomes.stream().collect(Collectors.toMap(d -> d.name, d -> d)); + Map groupMap = settings.asMap(); + for (Map.Entry e : groupMap.entrySet()) { + for (BiomeSettings.BiomeWeight biomeWeight : e.getValue().biomes) { + BiomeData data = biomeMap.get(biomeWeight.id); + if (data == null) { + continue; + } + builder.addBiome(e.getKey(), (Biome) data.reference, biomeWeight.weight); + } + } + for (BiomeData data : biomes) { + Biome biome = (Biome) data.reference; + if (biome.getCategory() == Biome.Category.OCEAN) { + builder.addOcean(biome, 10); + } else if (biome.getCategory() == Biome.Category.RIVER) { + builder.addRiver(biome, 10); + } + } + return builder.build(); + } + + public static Biome.TempCategory getTempCategory(Biome biome) { + // vanilla ocean biome properties are not at all helpful for determining temperature + if (biome.getCategory() == Biome.Category.OCEAN) { + // warm & luke_warm oceans get OceanRuinStructure.Type.WARM + OceanRuinConfig config = biome.getStructureConfig(Feature.OCEAN_RUIN); + if (config != null) { + if (config.field_204031_a == OceanRuinStructure.Type.WARM) { + return Biome.TempCategory.WARM; + } + } + + // if the id contains the world cold or frozen, assume it's cold + if (getId(biome).contains("cold") || getId(biome).contains("frozen")) { + return Biome.TempCategory.COLD; + } + + // the rest we categorize as medium + return Biome.TempCategory.MEDIUM; + } + // hopefully biomes otherwise have a sensible category + return biome.getTempCategory(); + } + + public static String getId(Biome biome) { + ResourceLocation name = biome.getRegistryName(); + if (name == null) { + return "unknown"; + } + return name.toString(); + } + + public static Collection getTypes(BiomeData data, Biome biome) { + Set types = new HashSet<>(); + for (Map.Entry entry : PREDICATES.entrySet()) { + if (entry.getValue().test(data, biome)) { + types.add(entry.getKey()); + } + } + return types; + } + + public static List getAllBiomeData() { + Collection biomes = TerraBiomeRegistry.getInstance().getAll(BiomeHelper::filter); + Vec2f tempRange = getRange(biomes, Biome::getDefaultTemperature); + Vec2f moistRange = getRange(biomes, Biome::getDownfall); + List list = new LinkedList<>(); + for (Biome biome : biomes) { + String name = getId(biome); + float moisture = (biome.getDownfall() - moistRange.x) / (moistRange.y - moistRange.x); + float temperature = (biome.getDefaultTemperature() - tempRange.x) / (tempRange.y - tempRange.x); + int color = biome.getSurfaceBuilderConfig().getTop().getMaterial().getColor().colorValue; + list.add(new BiomeData(name, biome, color, moisture, temperature)); + } + return list; + } + + private static boolean filter(Biome biome) { + if (biome.getCategory() == Biome.Category.NONE) { + return true; + } + if (biome.getCategory() == Biome.Category.THEEND) { + return true; + } + if (biome.getCategory() == Biome.Category.NETHER) { + return true; + } + return IGNORE_BIOMES.contains(biome); + } + + private static Vec2f getRange(Collection biomes, Function getter) { + float min = Float.MAX_VALUE; + float max = Float.MIN_VALUE; + for (Biome biome : biomes) { + float value = getter.apply(biome); + min = Math.min(min, value); + max = Math.max(max, value); + } + return new Vec2f(min, max); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeProvider.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeProvider.java new file mode 100644 index 0000000..f7372af --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/BiomeProvider.java @@ -0,0 +1,165 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.provider; + +import com.google.common.collect.Sets; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.region.chunk.ChunkReader; +import com.terraforged.core.util.concurrent.ObjectPool; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.biome.map.BiomeMap; +import com.terraforged.mod.biome.modifier.BiomeModifierManager; +import com.terraforged.mod.chunk.TerraContainer; +import com.terraforged.mod.chunk.TerraContext; +import com.terraforged.mod.util.setup.SetupHooks; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.feature.structure.Structure; + +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +public class BiomeProvider extends AbstractBiomeProvider { + + private final BiomeMap biomeMap; + private final TerraContext context; + private final BiomeModifierManager modifierManager; + + public BiomeProvider(TerraContext context) { + this.context = context; + this.biomeMap = BiomeHelper.getDefaultBiomeMap(); + this.modifierManager = SetupHooks.setup(new BiomeModifierManager(context, biomeMap), context.copy()); + } + + public BiomeModifierManager getModifierManager() { + return modifierManager; + } + + public TerraContainer createBiomeContainer(ChunkReader chunkReader) { + TerraContainer.Builder builder = TerraContainer.builder(); + chunkReader.iterate((cell, dx, dz) -> { + Biome biome = getBiome(cell, chunkReader.getBlockX() + dx, chunkReader.getBlockZ() + dz); + builder.fill(dx, dz, biome); + }); + return builder.build(chunkReader); + } + + @Override + public Biome getBiome(int x, int y, int z) { + try (ObjectPool.Item> item = Cell.pooled()) { + context.heightmap.apply(item.getValue(), x, z); + return getBiome(item.getValue(), x, z); + } + } + + @Override + public Set getBiomesInSquare(int centerX, int centerY, int centerZ, int sideLength) { + int minX = centerX - (sideLength >> 2); + int minZ = centerZ - (sideLength >> 2); + int maxX = centerX + (sideLength >> 2); + int maxZ = centerZ + (sideLength >> 2); + Set biomes = Sets.newHashSet(); + context.heightmap.visit(minX, minZ, maxX, maxZ, (cell, x, z) -> { + Biome biome = getBiome(cell, minX + x, minZ + z); + biomes.add(biome); + }); + return biomes; + } + + @Override + public BlockPos findBiomePosition(int centerX, int centerY, int centerZ, int range, List biomes, Random random) { + int minX = centerX - (range >> 2); + int minZ = centerZ - (range >> 2); + int maxX = centerX + (range >> 2); + int maxZ = centerZ + (range >> 2); + Set matchBiomes = new HashSet<>(biomes); + SearchContext search = new SearchContext(); + context.heightmap.visit(minX, minZ, maxX, maxZ, (cell, x, z) -> { + Biome biome = getBiome(cell, minX + x, minZ + z); + if (matchBiomes.contains(biome)) { + if (search.first || random.nextInt(search.count + 1) == 0) { + search.first = false; + search.pos.setPos(minX + x, 0, minZ + z); + } + ++search.count; + } + }); + return search.pos; + } + + @Override + public boolean hasStructure(Structure structureIn) { + return this.hasStructureCache.computeIfAbsent(structureIn, (p_205006_1_) -> { + for (Biome biome : defaultBiomes) { + if (biome.hasStructure(p_205006_1_)) { + return true; + } + } + return false; + }); + } + + @Override + public Set getSurfaceBlocks() { + if (this.topBlocksCache.isEmpty()) { + for (Biome biome : defaultBiomes) { + this.topBlocksCache.add(biome.getSurfaceBuilderConfig().getTop()); + } + } + return this.topBlocksCache; + } + + public Biome getBiome(Cell cell, int x, int z) { + if (cell.value <= context.levels.water) { + if (cell.tag == context.terrain.river || cell.tag == context.terrain.riverBanks) { + return modifyBiome(biomeMap.getRiver(cell.temperature, cell.moisture, cell.biome), cell, x, z); + } else if (cell.tag == context.terrain.ocean) { + return biomeMap.getOcean(cell.temperature, cell.moisture, cell.biome); + } else if (cell.tag == context.terrain.deepOcean) { + return biomeMap.getDeepOcean(cell.temperature, cell.moisture, cell.biome); + } + } + return modifyBiome(getBiome(cell), cell, x, z); + } + + public Biome getBiome(Cell cell) { + return biomeMap.getBiome(cell.biomeType, cell.temperature, cell.moisture, cell.biome); + } + + public Biome modifyBiome(Biome biome, Cell cell, int x, int z) { + return modifierManager.modify(biome, cell, x, z); + } + + private static class SearchContext { + + private int count = 0; + private boolean first = true; + private final BlockPos.Mutable pos = new BlockPos.Mutable(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/DesertBiomes.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/DesertBiomes.java new file mode 100644 index 0000000..18941d9 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/DesertBiomes.java @@ -0,0 +1,117 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.provider; + +import com.terraforged.api.material.layer.LayerManager; +import com.terraforged.api.material.layer.LayerMaterial; +import com.terraforged.core.util.concurrent.ObjectPool; +import com.terraforged.mod.material.Materials; +import com.terraforged.mod.util.DummyBlockReader; +import com.terraforged.mod.util.ListUtils; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.material.MaterialColor; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class DesertBiomes { + + private final Set reds; + private final Set whites; + private final List redSand; + private final List whiteSand; + private final LayerManager layerManager; + + private final int maxRedIndex; + private final int maxWhiteIndex; + + public DesertBiomes(Materials materials, List deserts) { + List white = new LinkedList<>(); + List red = new LinkedList<>(); + try (ObjectPool.Item reader = DummyBlockReader.pooled()) { + for (Biome biome : deserts) { + BlockState top = biome.getSurfaceBuilderConfig().getTop(); + MaterialColor color = top.getMaterialColor(reader.getValue().set(top), BlockPos.ZERO); + int whiteDist2 = distance2(color, MaterialColor.SAND); + int redDist2 = distance2(color, MaterialColor.ADOBE); + if (whiteDist2 < redDist2) { + white.add(biome); + } else { + red.add(biome); + } + } + } + this.layerManager = materials.getLayerManager(); + this.whiteSand = new ArrayList<>(white); + this.redSand = new ArrayList<>(red); + this.whites = new HashSet<>(white); + this.reds = new HashSet<>(red); + this.whiteSand.sort(Comparator.comparing(BiomeHelper::getId)); + this.redSand.sort(Comparator.comparing(BiomeHelper::getId)); + this.maxRedIndex = red.size() - 1; + this.maxWhiteIndex = white.size() - 1; + } + + public boolean isRedDesert(Biome biome) { + return reds.contains(biome); + } + + public boolean isWhiteDesert(Biome biome) { + return whites.contains(biome); + } + + public Biome getRedDesert(float shape) { + return ListUtils.get(redSand, maxRedIndex, shape, Biomes.MODIFIED_BADLANDS_PLATEAU); + } + + public Biome getWhiteDesert(float shape) { + return ListUtils.get(whiteSand, maxWhiteIndex, shape, Biomes.DESERT); + } + + public LayerMaterial getSandLayers(Biome biome) { + Block top = biome.getSurfaceBuilderConfig().getTop().getBlock(); + return layerManager.getMaterial(Blocks.SAND); + } + + private static int distance2(MaterialColor mc1, MaterialColor mc2) { + Color c1 = new Color(mc1.colorValue); + Color c2 = new Color(mc2.colorValue); + int dr = c1.getRed() - c2.getRed(); + int dg = c1.getGreen() - c2.getGreen(); + int db = c1.getBlue() - c2.getBlue(); + return (dr * dr) + (dg * dg) + (db * db); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/TerraBiomeManager.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/TerraBiomeManager.java new file mode 100644 index 0000000..20c39c9 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/TerraBiomeManager.java @@ -0,0 +1,54 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.provider; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeManager; +import net.minecraft.world.biome.ColumnFuzzedBiomeMagnifier; +import net.minecraft.world.biome.provider.BiomeProvider; + +public class TerraBiomeManager extends BiomeManager { + + private final long seed; + private final IBiomeReader provider; + + public TerraBiomeManager(IBiomeReader reader, long seed) { + super(reader, seed, ColumnFuzzedBiomeMagnifier.INSTANCE); + this.provider = reader; + this.seed = seed; + } + + @Override + public BiomeManager func_226835_a_(BiomeProvider provider) { + return new TerraBiomeManager(provider, seed); + } + + @Override + public Biome func_226836_a_(BlockPos pos) { + return provider.getNoiseBiome(pos.getX(), pos.getY(), pos.getZ()); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/TerraBiomeRegistry.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/TerraBiomeRegistry.java new file mode 100644 index 0000000..68219be --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/provider/TerraBiomeRegistry.java @@ -0,0 +1,58 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.provider; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.biome.Biome; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +public class TerraBiomeRegistry { + + private static final TerraBiomeRegistry instance = new TerraBiomeRegistry(); + + public Optional getBiome(ResourceLocation name) { + if (ForgeRegistries.BIOMES.containsKey(name)) { + return Optional.ofNullable(ForgeRegistries.BIOMES.getValue(name)); + } + return Optional.empty(); + } + + public Collection getAll(Predicate filter) { + List combined = new LinkedList<>(ForgeRegistries.BIOMES.getValues()); + combined.removeIf(filter); + return combined; + } + + public static TerraBiomeRegistry getInstance() { + return instance; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/biome/tag/BiomeTagManager.java b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/tag/BiomeTagManager.java new file mode 100644 index 0000000..26e5ac8 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/biome/tag/BiomeTagManager.java @@ -0,0 +1,72 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.biome.tag; + +import com.terraforged.api.biome.BiomeTags; +import com.terraforged.mod.Log; +import net.minecraft.profiler.IProfiler; +import net.minecraft.resources.IFutureReloadListener; +import net.minecraft.resources.IResourceManager; +import net.minecraft.tags.TagCollection; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE) +public class BiomeTagManager implements IFutureReloadListener { + + private static final boolean RETAIN_ORDER = false; + + private final TagCollection collection = new TagCollection<>( + Registry.BIOME::getValue, + "tags/biomes", + BiomeTagManager.RETAIN_ORDER, + "biomes" + ); + + @Override + public CompletableFuture reload(IFutureReloadListener.IStage stage, IResourceManager manager, IProfiler preparationsProfiler, IProfiler reloadProfiler, Executor backgroundExecutor, Executor gameExecutor) { + Log.debug("Reloading biome tag collection"); + return collection.reload(manager, gameExecutor) + .thenCompose(stage::markCompleteAwaitingOthers) + .thenAccept(collection::registerAll) + .thenRun(() -> { + BiomeTags.setCollection(collection); + Log.debug("Biome tag reload complete"); + }); + } + + @SubscribeEvent + public static void init(FMLServerAboutToStartEvent event) { + Log.debug("Registering tag reload listener"); + event.getServer().getResourceManager().addReloadListener(new BiomeTagManager()); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/ChunkGeneratorFactory.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/ChunkGeneratorFactory.java new file mode 100644 index 0000000..9b4d1f6 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/ChunkGeneratorFactory.java @@ -0,0 +1,35 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk; + +import com.terraforged.mod.biome.provider.BiomeProvider; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.OverworldGenSettings; + +public interface ChunkGeneratorFactory> { + + T create(TerraContext context, BiomeProvider biomeProvider, OverworldGenSettings settings); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/ObfHelperChunkGenerator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/ObfHelperChunkGenerator.java new file mode 100644 index 0000000..2888d05 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/ObfHelperChunkGenerator.java @@ -0,0 +1,150 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk; + +import com.terraforged.mod.util.annotation.Name; +import com.terraforged.mod.util.annotation.Ref; +import net.minecraft.entity.EntityClassification; +import net.minecraft.util.SharedSeedRandom; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IWorld; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.provider.BiomeProvider; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.GenerationSettings; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.INoiseGenerator; +import net.minecraft.world.gen.NoiseChunkGenerator; +import net.minecraft.world.gen.OverworldChunkGenerator; +import net.minecraft.world.gen.PerlinNoiseGenerator; +import net.minecraft.world.gen.WorldGenRegion; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.server.ServerWorld; +import net.minecraft.world.spawner.CatSpawner; +import net.minecraft.world.spawner.PatrolSpawner; +import net.minecraft.world.spawner.PhantomSpawner; +import net.minecraft.world.spawner.WorldEntitySpawner; + +import java.util.List; + +@Ref({OverworldChunkGenerator.class, NoiseChunkGenerator.class, ChunkGenerator.class}) +public abstract class ObfHelperChunkGenerator extends ChunkGenerator { + + private final CatSpawner catSpawner = new CatSpawner(); + private final PatrolSpawner patrolSpawner = new PatrolSpawner(); + private final PhantomSpawner phantomSpawner = new PhantomSpawner(); + private final INoiseGenerator surfaceNoise; + + public ObfHelperChunkGenerator(IWorld world, BiomeProvider biomeProvider, T settings) { + super(world, biomeProvider, settings); + SharedSeedRandom random = new SharedSeedRandom(world.getSeed()); + this.surfaceNoise = new PerlinNoiseGenerator(random, 3, 0); + } + + @Override + public final void generateStructureStarts(IWorld world, IChunk chunk) { + try { + super.generateStructureStarts(world, chunk); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + @Override + public final void makeBase(IWorld world, IChunk chunk) { + generateBase(world, chunk); + } + + @Override + public final void func_225551_a_(WorldGenRegion world, IChunk chunk) { + generateSurface(world, chunk); + } + + @Override + public final List getPossibleCreatures(EntityClassification type, BlockPos pos) { + if (Feature.SWAMP_HUT.func_202383_b(this.world, pos)) { + if (type == EntityClassification.MONSTER) { + return Feature.SWAMP_HUT.getSpawnList(); + } + + if (type == EntityClassification.CREATURE) { + return Feature.SWAMP_HUT.getCreatureSpawnList(); + } + } else if (type == EntityClassification.MONSTER) { + if (Feature.PILLAGER_OUTPOST.isPositionInStructure(this.world, pos)) { + return Feature.PILLAGER_OUTPOST.getSpawnList(); + } + + if (Feature.OCEAN_MONUMENT.isPositionInStructure(this.world, pos)) { + return Feature.OCEAN_MONUMENT.getSpawnList(); + } + } + return super.getPossibleCreatures(type, pos); + } + + @Override + public final void spawnMobs(ServerWorld worldIn, boolean spawnHostileMobs, boolean spawnPeacefulMobs) { + phantomSpawner.tick(worldIn, spawnHostileMobs, spawnPeacefulMobs); + patrolSpawner.tick(worldIn, spawnHostileMobs, spawnPeacefulMobs); + catSpawner.tick(worldIn, spawnHostileMobs, spawnPeacefulMobs); + } + + @Override + public final void spawnMobs(WorldGenRegion region) { + int chunkX = region.getMainChunkX(); + int chunkZ = region.getMainChunkZ(); + Biome biome = region.getChunk(chunkX, chunkZ).getBiomes().getNoiseBiome(0, 0, 0); + SharedSeedRandom sharedseedrandom = new SharedSeedRandom(); + sharedseedrandom.setDecorationSeed(region.getSeed(), chunkX << 4, chunkZ << 4); + WorldEntitySpawner.performWorldGenSpawning(region, biome, chunkX, chunkZ, sharedseedrandom); + } + + @Override + @Name("getSurfaceLevel") + public final int func_222529_a(int x, int z, Heightmap.Type type) { + int level = getTopBlockY(x, z, type) + 1; + if (type == Heightmap.Type.OCEAN_FLOOR || type == Heightmap.Type.OCEAN_FLOOR_WG) { + return level; + } + return Math.max(getGroundHeight(), level); + } + + public final double getSurfaceNoise(int x, int z) { + double scale = 0.0625D; + double noiseX = x * scale; + double noiseZ = z * scale; + double unusedValue1 = scale; + double unusedValue2 = (x & 15) * scale; + return surfaceNoise.noiseAt(noiseX, noiseZ, unusedValue1, unusedValue2); + } + + public abstract int getTopBlockY(int x, int z, Heightmap.Type type); + + public abstract void generateBase(IWorld world, IChunk chunk); + + public abstract void generateSurface(WorldGenRegion world, IChunk chunk); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java new file mode 100644 index 0000000..76d04e9 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java @@ -0,0 +1,366 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.chunk.surface.ChunkSurfaceBuffer; +import com.terraforged.api.chunk.surface.SurfaceContext; +import com.terraforged.api.chunk.surface.SurfaceManager; +import com.terraforged.api.material.layer.LayerManager; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.region.RegionCache; +import com.terraforged.core.region.RegionGenerator; +import com.terraforged.core.region.Size; +import com.terraforged.core.region.chunk.ChunkReader; +import com.terraforged.core.util.concurrent.ThreadPool; +import com.terraforged.feature.FeatureManager; +import com.terraforged.feature.matcher.dynamic.DynamicMatcher; +import com.terraforged.feature.matcher.feature.FeatureMatcher; +import com.terraforged.feature.modifier.FeatureModifierLoader; +import com.terraforged.feature.modifier.FeatureModifiers; +import com.terraforged.feature.predicate.DeepWater; +import com.terraforged.feature.predicate.FeaturePredicate; +import com.terraforged.feature.predicate.MinHeight; +import com.terraforged.feature.template.type.FeatureTypes; +import com.terraforged.mod.Log; +import com.terraforged.mod.biome.provider.BiomeProvider; +import com.terraforged.mod.biome.provider.TerraBiomeManager; +import com.terraforged.mod.chunk.fix.ChunkCarverFix; +import com.terraforged.mod.chunk.fix.RegionFix; +import com.terraforged.mod.decorator.ChunkPopulator; +import com.terraforged.mod.decorator.base.BedrockDecorator; +import com.terraforged.mod.decorator.base.CoastDecorator; +import com.terraforged.mod.decorator.base.ErosionDecorator; +import com.terraforged.mod.decorator.base.GeologyDecorator; +import com.terraforged.mod.decorator.feature.LayerDecorator; +import com.terraforged.mod.decorator.feature.SnowEroder; +import com.terraforged.mod.decorator.surface.FrozenOcean; +import com.terraforged.mod.feature.Matchers; +import com.terraforged.mod.feature.TerrainHelper; +import com.terraforged.mod.feature.predicate.TreeLine; +import com.terraforged.mod.material.Materials; +import com.terraforged.mod.material.geology.GeoManager; +import com.terraforged.mod.util.setup.SetupHooks; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.IWorld; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeContainer; +import net.minecraft.world.biome.BiomeManager; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.chunk.ChunkPrimer; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.GenerationSettings; +import net.minecraft.world.gen.GenerationStage; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.WorldGenRegion; +import net.minecraft.world.gen.feature.AbstractTreeFeature; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.template.TemplateManager; + +import java.util.ArrayList; +import java.util.List; + +public class TerraChunkGenerator extends ObfHelperChunkGenerator { + + private final TerraContext context; + private final BiomeProvider biomeProvider; + private final TerrainHelper terrainHelper; + + private final BiomeManager biomeManager; + private final GeoManager geologyManager; + private final FeatureManager featureManager; + private final SurfaceManager surfaceManager; + private final List baseDecorators; + private final List postProcessors; + + private final RegionCache regionCache; + + public TerraChunkGenerator(TerraContext context, BiomeProvider biomeProvider, GenerationSettings settings) { + super(context.world, biomeProvider, settings); + this.context = context; + this.biomeProvider = biomeProvider; + this.surfaceManager = SetupHooks.setup(createSurfaceManager(), context.copy()); + this.geologyManager = SetupHooks.setup(createGeologyManager(context), context.copy()); + this.baseDecorators = createBaseDecorators(context); + this.postProcessors = createFeatureDecorators(context); + this.terrainHelper = new TerrainHelper((int) world.getSeed(), 0.8F); + this.biomeManager = new TerraBiomeManager(biomeProvider, context.world.getSeed()); + this.featureManager = createFeatureManager(context); + this.regionCache = createRegionCache(context); + SetupHooks.setup(getLayerManager(), context.copy()); + SetupHooks.setup(baseDecorators, postProcessors, context.copy()); + } + + @Override + public void generateStructures(BiomeManager unused, IChunk chunk, ChunkGenerator generator, TemplateManager templates) { + super.generateStructures(biomeManager, chunk, this, templates); + } + + @Override + public final void generateBiomes(IChunk chunk) { + ChunkPos pos = chunk.getPos(); + ChunkReader view = getChunkReader(pos.x, pos.z); + BiomeContainer container = getBiomeProvider().createBiomeContainer(view); + ((ChunkPrimer) chunk).func_225548_a_(container); + } + + @Override + public final void generateBase(IWorld world, IChunk chunk) { + DecoratorContext context = new DecoratorContext(chunk, getContext().levels, getContext().terrain, getContext().factory.getClimate()); + TerraContainer container = getBiomeContainer(chunk); + container.getChunkReader().iterate((cell, dx, dz) -> { + int px = context.blockX + dx; + int pz = context.blockZ + dz; + int py = (int) (cell.value * getMaxHeight()); + context.cell = cell; + context.biome = container.getBiome(dx, dz); + ChunkPopulator.INSTANCE.decorate(chunk, context, px, py, pz); + }); + + terrainHelper.flatten(world, chunk, context.blockX, context.blockZ); + } + + @Override + public final void generateSurface(WorldGenRegion world, IChunk chunk) { + ChunkSurfaceBuffer buffer = new ChunkSurfaceBuffer(chunk); + SurfaceContext context = getContext().surface(buffer, getSettings()); + TerraContainer container = getBiomeContainer(chunk); + container.getChunkReader().iterate((cell, dx, dz) -> { + int px = context.blockX + dx; + int pz = context.blockZ + dz; + int top = chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, dx, dz) + 1; + + buffer.setSurfaceLevel(top); + + context.cell = cell; + context.biome = container.getBiome(dx, dz); + context.noise = getSurfaceNoise(px, pz) * 15D; + + getSurfaceManager().getSurface(context).buildSurface(px, pz, top, context); + + int py = (int) (cell.value * getMaxHeight()); + for (ColumnDecorator processor : getBaseDecorators()) { + processor.decorate(buffer, context, px, py, pz); + } + }); + } + + @Override + public final void func_225550_a_(BiomeManager biomeManager, IChunk chunk, GenerationStage.Carving carving) { + // World carvers have hardcoded 'carvable' blocks which can be problematic with modded blocks + // AirCarverFix shims the actual blockstates to an equivalent carvable type + super.func_225550_a_(this.biomeManager, new ChunkCarverFix(chunk, context.materials), carving); + } + + @Override + public void decorate(WorldGenRegion region) { + int chunkX = region.getMainChunkX(); + int chunkZ = region.getMainChunkZ(); + IChunk chunk = region.getChunk(chunkX, chunkZ); + TerraContainer container = getBiomeContainer(chunk); + Biome biome = container.getFeatureBiome(); + DecoratorContext context = getContext().decorator(chunk); + + // overrides WorldGenRegion's getSeaLevel() to provide the actual sea level + IWorld regionFix = new RegionFix(region, container, this, biomeManager); + BlockPos pos = new BlockPos(context.blockX, 0, context.blockZ); + + // place biome features + featureManager.decorate(this, regionFix, chunk, biome, pos); + + // run post processors + container.getChunkReader().iterate((cell, dx, dz) -> { + int px = context.blockX + dx; + int pz = context.blockZ + dz; + int py = chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, dx, dz); + context.cell = cell; + context.biome = container.getBiome(dx, dz); + for (ColumnDecorator decorator : getPostProcessors()) { + decorator.decorate(chunk, context, px, py, pz); + } + }); + + ((ChunkPrimer) chunk).func_225548_a_(container.bakeBiomes()); + } + + @Override + public int getTopBlockY(int x, int z, Heightmap.Type type) { + int chunkX = Size.blockToChunk(x); + int chunkZ = Size.blockToChunk(z); + ChunkReader chunk = getChunkReader(chunkX, chunkZ); + Cell cell = chunk.getCell(x, z); + return (int) (cell.value * getMaxHeight()); + } + + @Override + public BiomeProvider getBiomeProvider() { + return biomeProvider; + } + + @Override + public final int getMaxHeight() { + return getContext().levels.worldHeight; + } + + @Override + public final int getSeaLevel() { + return getContext().levels.waterLevel; + } + + @Override + public final int getGroundHeight() { + return getContext().levels.groundLevel; + } + + public final TerraContext getContext() { + return context; + } + + public final Materials getMaterials() { + return context.materials; + } + + public final GeoManager getGeologyManager() { + return geologyManager; + } + + public final LayerManager getLayerManager() { + return context.materials.getLayerManager(); + } + + public final SurfaceManager getSurfaceManager() { + return surfaceManager; + } + + public final List getBaseDecorators() { + return baseDecorators; + } + + public final List getPostProcessors() { + return postProcessors; + } + + protected TerraContainer getBiomeContainer(IChunk chunk) { + if (chunk.getBiomes() instanceof TerraContainer) { + return (TerraContainer) chunk.getBiomes(); + } + + ChunkReader view = getChunkReader(chunk.getPos().x, chunk.getPos().z); + TerraContainer container = getBiomeProvider().createBiomeContainer(view); + if (chunk instanceof ChunkPrimer) { + ((ChunkPrimer) chunk).func_225548_a_(container); + } + + return container; + } + + protected FeatureManager createFeatureManager(TerraContext context) { + FeatureModifiers modifiers; + if (context.terraSettings.features.customBiomeFeatures) { + Log.info(" - Custom biome features enabled"); + modifiers = FeatureModifierLoader.load(); + } else { + modifiers = new FeatureModifiers(); + } + + // block ugly features + modifiers.getPredicates().add(Matchers.STONE_BLOBS, FeaturePredicate.DENY); + modifiers.getPredicates().add(FeatureMatcher.of(Feature.DISK), FeaturePredicate.DENY); + modifiers.getPredicates().add(FeatureMatcher.of(Feature.LAKE), FeaturePredicate.DENY); + modifiers.getPredicates().add(FeatureMatcher.of(Feature.SPRING_FEATURE), FeaturePredicate.DENY); + + // limit to deep oceans + modifiers.getPredicates().add(FeatureMatcher.of(Feature.SHIPWRECK), DeepWater.INSTANCE); + modifiers.getPredicates().add(FeatureMatcher.of(Feature.OCEAN_RUIN), DeepWater.INSTANCE); + modifiers.getPredicates().add(FeatureMatcher.of(Feature.OCEAN_MONUMENT), DeepWater.INSTANCE); + + // prevent mineshafts above ground + modifiers.getPredicates().add(FeatureMatcher.of(Feature.MINESHAFT), MinHeight.HEIGHT80); + + // prevent trees/bamboo growing too high up + TreeLine treeLine = new TreeLine(context); + modifiers.getPredicates().add(FeatureTypes.TREE.matcher(), treeLine); + modifiers.getPredicates().add(FeatureMatcher.of(Feature.BAMBOO), treeLine); + modifiers.getDynamic().add(DynamicMatcher.feature(AbstractTreeFeature.class), treeLine); + + return FeatureManager.create(context.world, SetupHooks.setup(modifiers, context.copy())); + } + + protected GeoManager createGeologyManager(TerraContext context) { + return new GeoManager(context); + } + + protected SurfaceManager createSurfaceManager() { + SurfaceManager manager = new SurfaceManager(); + manager.replace(Biomes.FROZEN_OCEAN, new FrozenOcean(context, 20, 15)); + manager.replace(Biomes.DEEP_FROZEN_OCEAN, new FrozenOcean(context, 30, 30)); + return manager; + } + + protected List createBaseDecorators(TerraContext context) { + List processors = new ArrayList<>(); + if (context.terraSettings.features.strataDecorator) { + Log.info(" - Geology decorator enabled"); + processors.add(new GeologyDecorator(geologyManager)); + } + if (context.terraSettings.features.erosionDecorator) { + Log.info(" - Erosion decorator enabled"); + processors.add(new ErosionDecorator(context)); + } + processors.add(new CoastDecorator(context)); + processors.add(new BedrockDecorator()); + return processors; + } + + protected List createFeatureDecorators(TerraContext context) { + List processors = new ArrayList<>(); + if (context.terraSettings.features.naturalSnowDecorator) { + Log.info(" - Natural snow decorator enabled"); + processors.add(new SnowEroder(context)); + } + if (context.terraSettings.features.smoothLayerDecorator) { + Log.info(" - Smooth layer decorator enabled"); + processors.add(new LayerDecorator(context.materials.getLayerManager())); + } + return processors; + } + + protected RegionCache createRegionCache(TerraContext context) { + return RegionGenerator.builder() + .pool(ThreadPool.getFixed()) + .factory(context.factory) + .size(3, 2) + .build() + .toCache(); + } + + public ChunkReader getChunkReader(int chunkX, int chunkZ) { + return regionCache.getChunk(chunkX, chunkZ); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraContainer.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraContainer.java new file mode 100644 index 0000000..37751f9 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraContainer.java @@ -0,0 +1,131 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk; + +import com.terraforged.api.biome.BiomeVariant; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.region.chunk.ChunkReader; +import com.terraforged.core.util.PosIterator; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.util.Environment; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeContainer; + +public class TerraContainer extends BiomeContainer { + + private static final int ZOOM_HORIZ = (int) Math.round(Math.log(16.0D) / Math.log(2.0D)) - 2; + private static final int ZOOM_VERT = (int) Math.round(Math.log(256.0D) / Math.log(2.0D)) - 2; + public static final int SIZE = 1 << ZOOM_HORIZ + ZOOM_HORIZ + ZOOM_VERT; + public static final int MASK_HORIZ = (1 << ZOOM_HORIZ) - 1; + public static final int MASK_VERT = (1 << ZOOM_VERT) - 1; + + private final Biome[] biomes; + private final Biome[] surface; + private final ChunkReader chunkReader; + + public TerraContainer(Builder builder, ChunkReader chunkReader) { + super(builder.biomes); + this.chunkReader = chunkReader; + this.biomes = builder.biomes; + this.surface = builder.surfaceBiomeCache; + } + + public Biome getBiome(int x, int z) { + return surface[indexOf(x, z)]; + } + + @Override + public Biome getNoiseBiome(int x, int y, int z) { + return super.getNoiseBiome(x, y, z); + } + + public Biome getFeatureBiome() { + PosIterator iterator = PosIterator.area(0, 0, 16, 16); + while (iterator.next()) { + Cell cell = chunkReader.getCell(iterator.x(), iterator.z()); + if (cell.biomeType.isExtreme()) { + return getBiome(iterator.x(), iterator.z()); + } + } + return getBiome(8, 8); + } + + public BiomeContainer bakeBiomes() { + if (Environment.isDev()) { + for (int i = 0; i < biomes.length; i++) { + Biome biome = biomes[i]; + if (biome instanceof BiomeVariant) { + biomes[i] = ((BiomeVariant) biome).getBase(); + } + } + } + return new BiomeContainer(biomes); + } + + public ChunkReader getChunkReader() { + return chunkReader; + } + + private static int indexOf(int x, int z) { + x &= 15; + z &= 15; + return (z << 4) + x; + } + + private static int indexOf(int x, int y, int z) { + int bx = x & MASK_HORIZ; + int by = MathHelper.clamp(y, 0, MASK_VERT); + int bz = z & MASK_HORIZ; + return by << ZOOM_HORIZ + ZOOM_HORIZ | bz << ZOOM_HORIZ | bx; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final Biome[] biomes = new Biome[SIZE]; + private final Biome[] surfaceBiomeCache = new Biome[256]; + + public void set(int x, int y, int z, Biome biome) { + biomes[indexOf(x, y, z)] = biome; + + surfaceBiomeCache[indexOf(x, z)] = biome; + } + + public void fill(int x, int z, Biome biome) { + for (int y = 0; y < 64; y++) { + set(x, y, z, biome); + } + } + + public TerraContainer build(ChunkReader chunkReader) { + return new TerraContainer(this, chunkReader); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraContext.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraContext.java new file mode 100644 index 0000000..1e5c41b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraContext.java @@ -0,0 +1,68 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk; + +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.chunk.surface.SurfaceContext; +import com.terraforged.core.world.GeneratorContext; +import com.terraforged.core.world.WorldGeneratorFactory; +import com.terraforged.core.world.heightmap.Heightmap; +import com.terraforged.core.world.terrain.Terrains; +import com.terraforged.mod.material.Materials; +import com.terraforged.mod.settings.TerraSettings; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.world.IWorld; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.GenerationSettings; + +public class TerraContext extends GeneratorContext { + + public final IWorld world; + public final Heightmap heightmap; + public final Materials materials; + public final WorldGeneratorFactory factory; + public final TerraSettings terraSettings; + + public TerraContext(IWorld world, Terrains terrain, TerraSettings settings) { + super(terrain, settings, TerraTerrainProvider::new); + this.world = world; + this.materials = new Materials(); + this.terraSettings = settings; + this.factory = new WorldGeneratorFactory(this); + this.heightmap = factory.getHeightmap(); + ItemStack stack = new ItemStack(Items.COBBLESTONE); + stack.getMaxStackSize(); + } + + public DecoratorContext decorator(IChunk chunk) { + return new DecoratorContext(chunk, levels, terrain, factory.getClimate()); + } + + public SurfaceContext surface(IChunk chunk, GenerationSettings settings) { + return new SurfaceContext(chunk, levels, terrain, factory.getClimate(), settings, world.getSeed()); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraGenSettings.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraGenSettings.java new file mode 100644 index 0000000..6782bc5 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraGenSettings.java @@ -0,0 +1,41 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk; + +import com.terraforged.mod.settings.StructureSettings; +import net.minecraft.world.gen.OverworldGenSettings; + +public class TerraGenSettings extends OverworldGenSettings { + + public TerraGenSettings(StructureSettings settings) { + super.villageDistance *= settings.villageDistance; + super.mansionDistance *= settings.mansionDistance; + super.strongholdDistance *= settings.strongholdDistance; + super.biomeFeatureDistance *= settings.biomeStructureDistance; + super.oceanMonumentSpacing *= settings.oceanMonumentSpacing; + super.oceanMonumentSeparation *= settings.oceanMonumentSeparation; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraTerrainProvider.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraTerrainProvider.java new file mode 100644 index 0000000..a6365df --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraTerrainProvider.java @@ -0,0 +1,45 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk; + +import com.terraforged.core.cell.Populator; +import com.terraforged.core.world.GeneratorContext; +import com.terraforged.core.world.heightmap.RegionConfig; +import com.terraforged.core.world.terrain.provider.StandardTerrainProvider; +import com.terraforged.mod.util.setup.SetupHooks; + +public class TerraTerrainProvider extends StandardTerrainProvider { + + public TerraTerrainProvider(GeneratorContext context, RegionConfig config, Populator defaultPopulator) { + super(context, config, defaultPopulator); + } + + @Override + public void init() { + super.init(); + SetupHooks.setup(this, getContext().copy()); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/fix/ChunkCarverFix.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/fix/ChunkCarverFix.java new file mode 100644 index 0000000..dc12300 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/fix/ChunkCarverFix.java @@ -0,0 +1,74 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk.fix; + +import com.terraforged.api.chunk.ChunkDelegate; +import com.terraforged.api.material.state.States; +import com.terraforged.mod.material.MaterialHelper; +import com.terraforged.mod.material.Materials; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.IChunk; + +public class ChunkCarverFix implements ChunkDelegate { + + private final IChunk delegate; + private final Materials materials; + + public ChunkCarverFix(IChunk chunk, Materials materials) { + this.delegate = chunk; + this.materials = materials; + } + + @Override + public IChunk getDelegate() { + return delegate; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + BlockState state = getDelegate().getBlockState(pos); + if (MaterialHelper.isAir(state.getBlock())) { + return state; + } + if (MaterialHelper.isGrass(state.getBlock())) { + return States.GRASS_BLOCK.get(); + } + if (materials.isStone(state.getBlock())) { + return States.STONE.get(); + } + if (materials.isEarth(state.getBlock())) { + return States.DIRT.get(); + } + if (materials.isClay(state.getBlock())) { + return States.DIRT.get(); + } + if (materials.isSediment(state.getBlock())) { + return States.SAND.get(); + } + return state; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/fix/RegionFix.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/fix/RegionFix.java new file mode 100644 index 0000000..9ba2e19 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/fix/RegionFix.java @@ -0,0 +1,109 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk.fix; + +import com.terraforged.feature.util.WorldDelegate; +import com.terraforged.mod.chunk.TerraContainer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeContainer; +import net.minecraft.world.biome.BiomeManager; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.WorldGenRegion; + +public class RegionFix extends WorldDelegate { + + private final WorldGenRegion region; + private final TerraContainer container; + private final BiomeManager biomeManager; + private final ChunkGenerator generator; + + public RegionFix(WorldGenRegion region, TerraContainer container, ChunkGenerator generator, BiomeManager biomeManager) { + super(region); + this.region = region; + this.container = container; + this.generator = generator; + this.biomeManager = biomeManager; + } + + @Override + public int getSeaLevel() { + return generator.getSeaLevel(); + } + + @Override + public int getMaxHeight() { + return generator.getMaxHeight(); + } + + @Override + public BiomeManager func_225523_d_() { + return biomeManager; + } + + @Override + public Biome getNoiseBiome(int x, int y, int z) { + return getBiome(x, y, z); + } + + @Override + public Biome getNoiseBiomeRaw(int x, int y, int z) { + return getBiome(x, y, z); + } + + @Override + public Biome getBiome(BlockPos pos) { + return getBiome(pos.getX(), pos.getY(), pos.getZ()); + } + + private Biome getBiome(int x, int y, int z) { + int chunkX = x >> 4; + int chunkZ = z >> 4; + if (chunkX == region.getMainChunkX() && chunkZ == region.getMainChunkZ()) { + return container.getBiome(x, z); + } + + TerraContainer container = getBiomes(chunkX, chunkZ); + if (container == null) { + return generator.getBiomeProvider().getNoiseBiome(x, y, z); + } + + return container.getBiome(x, z); + } + + private TerraContainer getBiomes(int chunkX, int chunkZ) { + IChunk chunk = getChunk(chunkX, chunkZ, ChunkStatus.BIOMES, false); + if (chunk != null) { + BiomeContainer container = chunk.getBiomes(); + if (container instanceof TerraContainer) { + return (TerraContainer) container; + } + } + return null; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/Test.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/Test.java new file mode 100644 index 0000000..9e2e018 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/Test.java @@ -0,0 +1,44 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk.test; + +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.core.world.terrain.Terrains; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; + +public class Test { + + public static boolean fixedBiome = true; + + public static Terrain getTerrainType(Terrains terrains) { + return terrains.steppe; + } + + public static Biome getBiome() { + return Biomes.DESERT; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/TestBiomeProvider.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/TestBiomeProvider.java new file mode 100644 index 0000000..c56de43 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/TestBiomeProvider.java @@ -0,0 +1,48 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk.test; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.biome.ModBiomes; +import com.terraforged.mod.biome.provider.BiomeProvider; +import com.terraforged.mod.chunk.TerraContext; +import net.minecraft.world.biome.Biome; + +public class TestBiomeProvider extends BiomeProvider { + + public TestBiomeProvider(TerraContext chunkContext) { + super(chunkContext); + } + + @Override + public Biome getBiome(Cell cell, int x, int z) { + if (cell.biome < 0.5F) { + return ModBiomes.TAIGA_SCRUB; + } + return Test.getBiome(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/TestChunkGenerator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/TestChunkGenerator.java new file mode 100644 index 0000000..f33a581 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/test/TestChunkGenerator.java @@ -0,0 +1,87 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.chunk.test; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.cell.Populator; +import com.terraforged.core.region.RegionCache; +import com.terraforged.core.region.RegionGenerator; +import com.terraforged.core.util.concurrent.ThreadPool; +import com.terraforged.core.world.GeneratorContext; +import com.terraforged.core.world.WorldGeneratorFactory; +import com.terraforged.core.world.heightmap.WorldHeightmap; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.biome.provider.BiomeProvider; +import com.terraforged.mod.chunk.TerraChunkGenerator; +import com.terraforged.mod.chunk.TerraContext; +import net.minecraft.world.gen.GenerationSettings; + +public class TestChunkGenerator extends TerraChunkGenerator { + + private final BiomeProvider biomeProvider; + + public TestChunkGenerator(TerraContext context, BiomeProvider biomeProvider, GenerationSettings settings) { + super(context, biomeProvider, settings); + this.biomeProvider = new TestBiomeProvider(context); + } + + @Override + protected RegionCache createRegionCache(TerraContext context) { + return RegionGenerator.builder() + .factory(new WorldGeneratorFactory(context, new TestHeightMap(context))) + .pool(ThreadPool.getFixed()) + .size(3, 2) + .build() + .toCache(true); + } + + @Override + public BiomeProvider getBiomeProvider() { + return biomeProvider; + } + + private static class TestHeightMap extends WorldHeightmap { + + private final Populator populator; + + public TestHeightMap(GeneratorContext context) { + super(context); + this.populator = getPopulator(Test.getTerrainType(context.terrain)); + } + + @Override + public void apply(Cell cell, float x, float y) { + super.apply(cell, x, y); + + populator.apply(cell, x, y); + } + + @Override + public void tag(Cell cell, float x, float y) { + populator.tag(cell, x, y); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/TerraCommand.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/TerraCommand.java new file mode 100644 index 0000000..3065f29 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/TerraCommand.java @@ -0,0 +1,256 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.WorldGenerator; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.core.world.terrain.Terrains; +import com.terraforged.mod.Log; +import com.terraforged.mod.biome.provider.BiomeProvider; +import com.terraforged.mod.chunk.TerraChunkGenerator; +import com.terraforged.mod.chunk.TerraContext; +import com.terraforged.mod.command.arg.BiomeArgType; +import com.terraforged.mod.command.arg.TerrainArgType; +import com.terraforged.mod.command.task.FindBiomeTask; +import com.terraforged.mod.command.task.FindBothTask; +import com.terraforged.mod.command.task.FindTask; +import com.terraforged.mod.command.task.FindTerrainTask; +import com.terraforged.mod.data.DataGen; +import me.dags.noise.util.Vec2i; +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TextComponentUtils; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.TranslationTextComponent; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.util.text.event.HoverEvent; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.server.FMLServerStartingEvent; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE) +public class TerraCommand { + + @SubscribeEvent + public static void start(FMLServerStartingEvent event) { + Log.info("Registering find command!"); + register(event.getCommandDispatcher()); + } + + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(command()); + } + + private static LiteralArgumentBuilder command() { + return Commands.literal("terra") + .requires(source -> source.hasPermissionLevel(2)) + .then(Commands.literal("query") + .executes(TerraCommand::query)) + .then(Commands.literal("data") + .then(Commands.literal("dump") + .executes(TerraCommand::dump))) + .then(Commands.literal("locate") + .then(Commands.argument("biome", BiomeArgType.biome()) + .executes(TerraCommand::findBiome) + .then(Commands.argument("terrain", TerrainArgType.terrain()) + .executes(TerraCommand::findTerrainAndBiome))) + .then(Commands.argument("terrain", TerrainArgType.terrain()) + .executes(TerraCommand::findTerrain) + .then(Commands.argument("biome", BiomeArgType.biome()) + .executes(TerraCommand::findTerrainAndBiome)))); + } + + private static int query(CommandContext context) throws CommandSyntaxException { + TerraContext terraContext = getContext(context).orElseThrow(() -> createException( + "Invalid world type", + "This command can only be run in a TerraForged world!" + )); + + BlockPos pos = context.getSource().asPlayer().getPosition(); + WorldGenerator worldGenerator = terraContext.factory.get(); + BiomeProvider biomeProvider = getBiomeProvider(context); + Cell cell = new Cell<>(); + worldGenerator.getHeightmap().apply(cell, pos.getX(), pos.getZ()); + Biome biome = biomeProvider.getBiome(cell, pos.getX(), pos.getZ()); + context.getSource().sendFeedback( + new StringTextComponent("Terrain=" + cell.tag.getName() + ", Biome=" + biome.getRegistryName()), + false + ); + + return Command.SINGLE_SUCCESS; + } + + private static int dump(CommandContext context) throws CommandSyntaxException { + context.getSource().sendFeedback( + new StringTextComponent("Exporting data"), + true + ); + DataGen.dumpData(); + return Command.SINGLE_SUCCESS; + } + + private static int findTerrain(CommandContext context) throws CommandSyntaxException { + // get the generator's context + TerraContext terraContext = getContext(context).orElseThrow(() -> createException( + "Invalid world type", + "This command can only be run in a TerraForged world!" + )); + + Terrain terrain = TerrainArgType.getTerrain(context, "terrain"); + Terrain target = getTerrainInstance(terrain, terraContext.terrain); + BlockPos pos = context.getSource().asPlayer().getPosition(); + UUID playerID = context.getSource().asPlayer().getUniqueID(); + MinecraftServer server = context.getSource().getServer(); + WorldGenerator worldGenerator = terraContext.factory.get(); + FindTask task = new FindTerrainTask(pos, target, worldGenerator); + doSearch(server, playerID, task); + context.getSource().sendFeedback(new StringTextComponent("Searching..."), false); + + return Command.SINGLE_SUCCESS; + } + + private static int findBiome(CommandContext context) throws CommandSyntaxException { + // get the generator's context + TerraContext terraContext = getContext(context).orElseThrow(() -> createException( + "Invalid world type", + "This command can only be run in a TerraForged world!" + )); + + Biome biome = BiomeArgType.getBiome(context, "biome"); + BlockPos pos = context.getSource().asPlayer().getPosition(); + UUID playerID = context.getSource().asPlayer().getUniqueID(); + MinecraftServer server = context.getSource().getServer(); + WorldGenerator worldGenerator = terraContext.factory.get(); + BiomeProvider biomeProvider = getBiomeProvider(context); + FindTask task = new FindBiomeTask(pos, biome, worldGenerator, biomeProvider); + doSearch(server, playerID, task); + context.getSource().sendFeedback(new StringTextComponent("Searching..."), false); + + return Command.SINGLE_SUCCESS; + } + + private static int findTerrainAndBiome(CommandContext context) throws CommandSyntaxException { + // get the generator's context + TerraContext terraContext = getContext(context).orElseThrow(() -> createException( + "Invalid world type", + "This command can only be run in a TerraForged world!" + )); + + Terrain terrain = TerrainArgType.getTerrain(context, "terrain"); + Terrain target = getTerrainInstance(terrain, terraContext.terrain); + Biome biome = BiomeArgType.getBiome(context, "biome"); + BlockPos pos = context.getSource().asPlayer().getPosition(); + UUID playerID = context.getSource().asPlayer().getUniqueID(); + MinecraftServer server = context.getSource().getServer(); + WorldGenerator worldGenerator = terraContext.factory.get(); + BiomeProvider biomeProvider = getBiomeProvider(context); + FindTask task = new FindBothTask(pos, target, biome, worldGenerator, biomeProvider); + doSearch(server, playerID, task); + context.getSource().sendFeedback(new StringTextComponent("Searching..."), false); + + return Command.SINGLE_SUCCESS; + } + + private static void doSearch(MinecraftServer server, UUID userId, FindTask task) { + CompletableFuture.supplyAsync(task).thenAccept(pos -> server.deferTask(() -> { + PlayerEntity player = server.getPlayerList().getPlayerByUUID(userId); + if (player == null) { + return; + } + + if (pos.x == 0 && pos.y == 0) { + player.sendMessage(new StringTextComponent("Location not found :[")); + return; + } + + ITextComponent result = new StringTextComponent("Nearest match: ") + .appendSibling(createTeleportMessage(pos)); + + player.sendMessage(result); + })); + } + + private static Optional getContext(CommandContext context) throws CommandSyntaxException { + MinecraftServer server = context.getSource().getServer(); + DimensionType dimension = context.getSource().asPlayer().dimension; + ChunkGenerator generator = server.getWorld(dimension).getChunkProvider().getChunkGenerator(); + if (generator instanceof TerraChunkGenerator) { + TerraChunkGenerator gen = (TerraChunkGenerator) generator; + return Optional.of(gen.getContext()); + } + return Optional.empty(); + } + + // the terrain parsed from the command will not be the same instance as used in the + // world generator, so find the matching instance by name + private static Terrain getTerrainInstance(Terrain find, Terrains terrains) { + for (Terrain t : terrains.index) { + if (t.getName().equals(find.getName())) { + return t; + } + } + return find; + } + + private static BiomeProvider getBiomeProvider(CommandContext context) { + return (BiomeProvider) context.getSource().getWorld().getChunkProvider().getChunkGenerator().getBiomeProvider(); + } + + private static CommandSyntaxException createException(String type, String message, Object... args) { + return new CommandSyntaxException( + new SimpleCommandExceptionType(new StringTextComponent(type)), + new StringTextComponent(String.format(message, args)) + ); + } + + private static ITextComponent createTeleportMessage(Vec2i pos) { + return TextComponentUtils.wrapInSquareBrackets(new TranslationTextComponent( + "chat.coordinates", pos.x, "~", pos.y + )).applyTextStyle((style) -> style.setColor(TextFormatting.GREEN) + .setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/tp @s " + pos.x + " ~ " + pos.y)) + .setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TranslationTextComponent("chat.coordinates.tooltip"))) + ); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/BiomeArgType.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/BiomeArgType.java new file mode 100644 index 0000000..10a3b54 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/BiomeArgType.java @@ -0,0 +1,72 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.command.arg; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.command.ISuggestionProvider; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.world.biome.Biome; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.concurrent.CompletableFuture; + +public class BiomeArgType implements ArgumentType { + + @Override + public Biome parse(StringReader reader) throws CommandSyntaxException { + ResourceLocation resourcelocation = ResourceLocation.read(reader); + return Registry.BIOME.getValue(resourcelocation) + .orElseThrow(() -> createException("Invalid biome", "%s is not a valid biome", resourcelocation)); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder suggestions) { + return ISuggestionProvider.func_212476_a(ForgeRegistries.BIOMES.getValues().stream().map(Biome::getRegistryName), suggestions); + } + + private static CommandSyntaxException createException(String type, String message, Object... args) { + return new CommandSyntaxException( + new SimpleCommandExceptionType(new StringTextComponent(type)), + new StringTextComponent(String.format(message, args)) + ); + } + + public static ArgumentType biome() { + return new BiomeArgType(); + } + + public static Biome getBiome(CommandContext context, String name) { + return context.getArgument(name, Biome.class); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/TerrainArgType.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/TerrainArgType.java new file mode 100644 index 0000000..77a4c50 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/arg/TerrainArgType.java @@ -0,0 +1,117 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.command.arg; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.terraforged.core.settings.Settings; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.core.world.terrain.Terrains; +import net.minecraft.command.ISuggestionProvider; +import net.minecraft.util.text.StringTextComponent; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class TerrainArgType implements ArgumentType { + + private final List terrains = getTerrains(); + + @Override + public Terrain parse(StringReader reader) throws CommandSyntaxException { + int cursor = reader.getCursor(); + String name = reader.readString(); + for (Terrain terrain : terrains) { + if (terrain.getName().equalsIgnoreCase(name)) { + return terrain; + } + } + reader.setCursor(cursor); + throw createException("Invalid terrain", "%s is not a valid terrain type", name); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder suggestions) { + return ISuggestionProvider.suggest(terrains.stream().map(Terrain::getName).collect(Collectors.toList()), suggestions); + } + + public static ArgumentType terrain() { + return new TerrainArgType(); + } + + public static Terrain getTerrain(CommandContext context, String name) { + return context.getArgument(name, Terrain.class); + } + + private static CommandSyntaxException createException(String type, String message, Object... args) { + return new CommandSyntaxException( + new SimpleCommandExceptionType(new StringTextComponent(type)), + new StringTextComponent(String.format(message, args)) + ); + } + + private static List getTerrains() { + Terrains terrains = Terrains.create(new Settings()); + List result = new ArrayList<>(); + + for (int i = 0; i < terrains.index.size(); i++) { + Terrain terrain = terrains.index.get(i); + result.add(terrain); + if (dontMix(terrain, terrains)) { + continue; + } + for (int j = i + 1; j < terrains.index.size(); j++) { + Terrain other = terrains.index.get(j); + if (dontMix(other, terrains)) { + continue; + } + Terrain mix = new Terrain(terrain.getName() + "-" + other.getName(), -1); + result.add(mix); + } + } + + return result; + } + + private static boolean dontMix(Terrain terrain, Terrains terrains) { + return terrain == terrains.ocean + || terrain == terrains.deepOcean + || terrain == terrains.river + || terrain == terrains.riverBanks + || terrain == terrains.beach + || terrain == terrains.coast + || terrain == terrains.volcano + || terrain == terrains.volcanoPipe + || terrain == terrains.lake; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindBiomeTask.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindBiomeTask.java new file mode 100644 index 0000000..88ed423 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindBiomeTask.java @@ -0,0 +1,80 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.command.task; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.WorldGenerator; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.biome.provider.BiomeProvider; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +public class FindBiomeTask extends FindTask { + + private final Biome biome; + private final WorldGenerator generator; + private final BiomeProvider biomeProvider; + private final Cell cell = new Cell<>(); + + public FindBiomeTask(BlockPos center, Biome biome, WorldGenerator generator, BiomeProvider biomeProvider) { + this(center, biome, generator, biomeProvider, 100); + } + + public FindBiomeTask(BlockPos center, Biome biome, WorldGenerator generator, BiomeProvider biomeProvider, int minRadius) { + super(center, minRadius); + this.biome = biome; + this.generator = generator; + this.biomeProvider = biomeProvider; + } + + @Override + protected int transformCoord(int coord) { + return coord * 8; + } + + @Override + protected boolean search(int x, int z) { + generator.getHeightmap().apply(cell, x, z); + + if (biome.getCategory() != Biome.Category.BEACH && biome.getCategory() != Biome.Category.OCEAN) { + if (cell.continentEdge > 0.4 && cell.continentEdge < 0.5) { + return false; + } + } + + return biomeProvider.getBiome(cell, x, z) == biome; + } + + protected boolean test(Cell cell, int x, int z) { + if (biome.getCategory() != Biome.Category.BEACH && biome.getCategory() != Biome.Category.OCEAN) { + if (cell.continentEdge > 0.4 && cell.continentEdge < 0.5) { + return false; + } + } + + return biomeProvider.getBiome(cell, x, z) == biome; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindBothTask.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindBothTask.java new file mode 100644 index 0000000..fda554b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindBothTask.java @@ -0,0 +1,57 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.command.task; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.WorldGenerator; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.biome.provider.BiomeProvider; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +public class FindBothTask extends FindBiomeTask { + + private final Terrain type; + private final WorldGenerator generator; + private final Cell cell = new Cell<>(); + + public FindBothTask(BlockPos center, Terrain terrain, Biome biome, WorldGenerator generator, BiomeProvider biomeProvider) { + super(center, biome, generator, biomeProvider, 300); + this.type = terrain; + this.generator = generator; + } + + @Override + protected boolean search(int x, int z) { + generator.getHeightmap().apply(cell, x, z); + return test(cell, x , z) && super.test(cell, x, z); + } + + @Override + protected boolean test(Cell cell, int x, int z) { + return cell.continentEdge > 0.5 && cell.regionEdge > 0.8F && cell.tag == type; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindTask.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindTask.java new file mode 100644 index 0000000..4ffa070 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindTask.java @@ -0,0 +1,109 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.command.task; + +import me.dags.noise.util.Vec2i; +import net.minecraft.util.math.BlockPos; + +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +public abstract class FindTask implements Supplier { + + private static final int RADIUS = 5000; + + private final BlockPos center; + private final int minRadius; + + private final long timeout; + + protected FindTask(BlockPos center, int minRadius) { + this.center = center; + this.minRadius = minRadius; + this.timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(20); + } + + @Override + public Vec2i get() { + try { + int x = 0; + int y = 0; + int dx = 0; + int dy = -1; + int radius = RADIUS; + int size = radius + 1 + radius; + int max = size * size; + + int minSize = minRadius + 1 + minRadius; + int min = minSize * minSize; + + for (int i = 0; i < max; i++) { + if (i % 100 == 0) { + if (System.currentTimeMillis() > timeout) { + break; + } + } + + if ((-radius <= x) && (x <= radius) && (-radius <= y) && (y <= radius)) { + int distX = transformCoord(x); + int distY = transformCoord(y); + int dist2 = distX * distX + distY * distY; + if (dist2 > min) { + int posX = center.getX() + distX; + int posZ = center.getZ() + distY; + if (search(posX, posZ)) { + return new Vec2i(posX, posZ); + } + } + } + + if ((x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1 - y))) { + size = dx; + dx = -dy; + dy = size; + } + + x += dx; + y += dy; + } + return new Vec2i(0, 0); + } catch (Throwable t) { + t.printStackTrace(); + return new Vec2i(0, 0); + } + } + + protected abstract int transformCoord(int coord); + + /** + * Performs the search at the given x,z coordinates and returns true if a result is found + * + * @param x the x block coordinate + * @param z the z block coordinate + * @return the search result + */ + protected abstract boolean search(int x, int z); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindTerrainTask.java b/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindTerrainTask.java new file mode 100644 index 0000000..33316ca --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/command/task/FindTerrainTask.java @@ -0,0 +1,59 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.command.task; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.WorldGenerator; +import com.terraforged.core.world.terrain.Terrain; +import net.minecraft.util.math.BlockPos; + +public class FindTerrainTask extends FindTask { + + private final Terrain type; + private final WorldGenerator generator; + private final Cell cell = new Cell<>(); + + public FindTerrainTask(BlockPos center, Terrain type, WorldGenerator generator) { + super(center, 300); + this.type = type; + this.generator = generator; + } + + @Override + protected int transformCoord(int coord) { + return coord * 64; + } + + @Override + protected boolean search(int x, int z) { + generator.getHeightmap().apply(cell, x, z); + return test(cell); + } + + protected boolean test(Cell cell) { + return cell.continentEdge > 0.5 && cell.regionEdge > 0.8F && cell.tag == type; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/data/DataGen.java b/TerraForgedMod/src/main/java/com/terraforged/mod/data/DataGen.java new file mode 100644 index 0000000..881b3f3 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/data/DataGen.java @@ -0,0 +1,72 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.data; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import net.minecraft.util.ResourceLocation; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +public class DataGen { + + public static void dumpData() { + File dataDir = new File("data"); + WorldGenBiomes.genBiomeMap(dataDir); + WorldGenBlocks.genBlockTags(dataDir); + WorldGenFeatures.genBiomeFeatures(dataDir); + } + + protected static void write(File file, IOConsumer consumer) { + if (file.getParentFile().exists() || file.getParentFile().mkdirs()) { + try (Writer writer = new BufferedWriter(new FileWriter(file))) { + consumer.accept(writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + protected static void write(JsonElement json, Writer writer) { + new GsonBuilder().setPrettyPrinting().create().toJson(json, writer); + } + + protected static String getJsonPath(String type, ResourceLocation location) { + if (location == null) { + return "unknown"; + } + return location.getNamespace() + "/" + type + "/" + location.getPath() + ".json"; + } + + public interface IOConsumer { + + void accept(Writer writer) throws IOException; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenBiomes.java b/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenBiomes.java new file mode 100644 index 0000000..d618592 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenBiomes.java @@ -0,0 +1,71 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.data; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.terraforged.core.world.biome.BiomeType; +import com.terraforged.mod.biome.map.BiomeMap; +import com.terraforged.mod.biome.provider.BiomeHelper; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.biome.Biome; + +import java.io.File; +import java.util.Set; + +public class WorldGenBiomes extends DataGen { + + public static void genBiomeMap(File dataDir) { + BiomeMap map = BiomeHelper.getDefaultBiomeMap(); + for (BiomeType type : BiomeType.values()) { + genBiomes(dataDir, type, map); + } + } + + private static void genBiomes(File dir, BiomeType type, BiomeMap map) { + String path = getJsonPath("tags/biomes", getName(type)); + + write(new File(dir, path), writer -> { + Set biomes = map.getBiomes(type); + JsonObject root = new JsonObject(); + JsonArray values = new JsonArray(); + + root.addProperty("replaceable", false); + root.add("values", values); + + biomes.stream() + .map(b -> b.getRegistryName() + "") + .sorted() + .forEach(values::add); + + write(root, writer); + }); + } + + private static ResourceLocation getName(BiomeType type) { + return new ResourceLocation("terraforged", type.name().toLowerCase()); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenBlocks.java b/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenBlocks.java new file mode 100644 index 0000000..e101bc4 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenBlocks.java @@ -0,0 +1,63 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.data; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.terraforged.mod.material.Materials; +import net.minecraft.block.Block; +import net.minecraft.util.ResourceLocation; + +import java.io.File; +import java.util.Collection; + +public class WorldGenBlocks extends DataGen { + + public static void genBlockTags(File dataDir) { + if (dataDir.exists() || dataDir.mkdirs()) { + Materials materials = new Materials(); + printMaterials(dataDir, "stone", materials.getStone()); + printMaterials(dataDir, "dirt", materials.getDirt()); + printMaterials(dataDir, "clay", materials.getClay()); + printMaterials(dataDir, "sediment",materials.getSediment()); + printMaterials(dataDir, "ore", materials.getOre()); + } + } + + private static void printMaterials(File dir, String name, Collection blocks) { + String path = getJsonPath("tags/blocks", new ResourceLocation("terraforged", name)); + write(new File(dir, path), writer -> { + JsonObject root = new JsonObject(); + JsonArray values = new JsonArray(); + root.addProperty("replace", false); + root.add("values", values); + for (Block block : blocks) { + values.add("" + block.getRegistryName()); + } + write(root, writer); + }); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenFeatures.java b/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenFeatures.java new file mode 100644 index 0000000..cbe327b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/data/WorldGenFeatures.java @@ -0,0 +1,68 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.data; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mojang.datafixers.Dynamic; +import com.mojang.datafixers.types.JsonOps; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.GenerationStage; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraftforge.registries.ForgeRegistries; + +import java.io.File; + +public class WorldGenFeatures extends DataGen { + + public static void genBiomeFeatures(File dataDir) { + if (dataDir.exists() || dataDir.mkdirs()) { + for (Biome biome : ForgeRegistries.BIOMES) { + genBiomeFeatures(dataDir, biome); + } + } + } + + private static void genBiomeFeatures(File dir, Biome biome) { + write(new File(dir, getJsonPath("features", biome.getRegistryName())), writer -> { + JsonObject root = new JsonObject(); + for (GenerationStage.Decoration type : GenerationStage.Decoration.values()) { + JsonArray features = new JsonArray(); + for (ConfiguredFeature feature : biome.getFeatures(type)) { + try { + Dynamic dynamic = feature.serialize(JsonOps.INSTANCE); + features.add(dynamic.getValue()); + } catch (NullPointerException e) { + new NullPointerException("Badly written feature: " + feature.feature.getRegistryName()).printStackTrace(); + } + } + root.add(type.getName(), features); + } + write(root, writer); + }); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/ChunkPopulator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/ChunkPopulator.java new file mode 100644 index 0000000..87e826f --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/ChunkPopulator.java @@ -0,0 +1,49 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.material.state.States; +import net.minecraft.world.chunk.IChunk; + +public class ChunkPopulator implements ColumnDecorator { + + public static final ChunkPopulator INSTANCE = new ChunkPopulator(); + + @Override + public void decorate(IChunk chunk, DecoratorContext context, int x, int y, int z) { + if (context.cell.tag == context.terrains.volcanoPipe && context.cell.riverMask > 0.25F) { + int lavaStart = Math.max(context.levels.waterY + 10, y - 30); + int lavaEnd = Math.max(5, context.levels.waterY - 10); + fillDown(context, chunk, x, z, lavaStart, lavaEnd, States.LAVA.get()); + y = lavaEnd; + } else if (y < context.levels.waterLevel) { + fillDown(context, chunk, x, z, context.levels.waterY, y, States.WATER.get()); + } + fillDown(context, chunk, x, z, y, 0, States.STONE.get()); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/BedrockDecorator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/BedrockDecorator.java new file mode 100644 index 0000000..b5e03cf --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/BedrockDecorator.java @@ -0,0 +1,43 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.base; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.material.state.States; +import net.minecraft.world.chunk.IChunk; + +import java.util.Random; + +public class BedrockDecorator implements ColumnDecorator { + + private final Random random = new Random(); + + @Override + public void decorate(IChunk chunk, DecoratorContext context, int x, int y, int z) { + fillDown(context, chunk, x, z, 1 + random.nextInt(4), -1, States.BEDROCK.get()); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/CoastDecorator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/CoastDecorator.java new file mode 100644 index 0000000..686d4b8 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/CoastDecorator.java @@ -0,0 +1,76 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.base; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.material.state.States; +import com.terraforged.core.util.VariablePredicate; +import com.terraforged.core.world.terrain.Terrains; +import com.terraforged.mod.chunk.TerraContext; +import net.minecraft.block.BlockState; +import net.minecraft.world.chunk.IChunk; + +public class CoastDecorator implements ColumnDecorator { + + private final Terrains terrains; + private final BlockState sand; + private final BlockState gravel; + private final VariablePredicate height; + + public CoastDecorator(TerraContext context) { + int min = context.levels.waterLevel - 5; + int max = context.levels.waterLevel + 16; + sand = States.SAND.get(); + gravel = States.GRAVEL.get(); + terrains = context.terrain; + height = VariablePredicate.height( + context.seed, + context.levels, + min, + max, + 75, + 1 + ); + } + + @Override + public void decorate(IChunk chunk, DecoratorContext context, int x, int y, int z) { + if (context.cell.tag != terrains.beach) { + return; + } + + if (!height.test(context.cell, x, z)) { + return; + } + + if (context.cell.temperature < 0.3F) { + setState(chunk, x, y, z, gravel, false); + } else { + setState(chunk, x, y, z, sand, false); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/ErosionDecorator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/ErosionDecorator.java new file mode 100644 index 0000000..cd59f52 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/ErosionDecorator.java @@ -0,0 +1,193 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.base; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.material.state.States; +import com.terraforged.core.world.terrain.Terrains; +import com.terraforged.mod.chunk.TerraContext; +import com.terraforged.mod.material.Materials; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.material.Material; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.surfacebuilders.ISurfaceBuilderConfig; + +public class ErosionDecorator implements ColumnDecorator { + + private static final int ROCK_VAR = 30; + private static final int ROCK_MIN = 140; + + private static final int DIRT_VAR = 40; + private static final int DIRT_MIN = 95; + + public static final float ROCK_STEEPNESS = 0.65F; + private static final float DIRT_STEEPNESS = 0.475F; + private static final float SCREE_STEEPNESS = 0.4F; + + public static final float HEIGHT_MODIFIER = 6F / 255F; + public static final float SLOPE_MODIFIER = 3F / 255F; + + private static final float SEDIMENT_MODIFIER = 256; + private static final float SEDIMENT_NOISE = 3F / 255F; + private static final float SCREE_VALUE = 0.55F; + + private final int seed1; + private final int seed2; + private final int seed3; + private final float minY; + private final Terrains terrain; + private final Materials materials; + + public ErosionDecorator(TerraContext context) { + this.terrain = context.terrain; + this.seed1 = context.seed.next(); + this.seed2 = context.seed.next(); + this.seed3 = context.seed.next(); + this.minY = context.levels.ground(4); + this.materials = context.materials; + } + + @Override + public void decorate(IChunk chunk, DecoratorContext context, int x, int y, int z) { + if (context.cell.value < minY || context.cell.tag == terrain.river || context.cell.tag == terrain.riverBanks) { + return; + } + + if (context.cell.tag == terrain.volcanoPipe) { + return; + } + + int topY = chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, x, z); + if (topY - 1 > y) { + y = topY; + } + + ISurfaceBuilderConfig config = context.biome.getSurfaceBuilderConfig(); + BlockState top = config.getTop(); + BlockState middle = config.getUnder(); + + if (materials.isErodible(top.getBlock())) { + BlockState material = getMaterial(x, z, context, top, middle); + if (material != top) { + if (materials.isStone(material.getBlock())) { + erodeRock(context, chunk, x, y, z); + return; + } else { + fillDown(context, chunk, x, z, y, y - 4, material); + } + } + placeScree(chunk, context, x, y, z); + } + } + + protected void erodeRock(DecoratorContext context, IChunk chunk, int dx, int y, int dz) { + int depth = 32; + BlockState material = Blocks.GRAVEL.getDefaultState(); + // find the uppermost layer of rock & record it's depth + for (int dy = 3; dy < 32; dy++) { + context.pos.setY(y - dy); + BlockState state = chunk.getBlockState(context.pos); + if (materials.isStone(state.getBlock())) { + material = state; + depth = dy + 1; + break; + } + } + + // fill downwards to the first rock layer + for (int dy = 0; dy < depth; dy++) { + context.pos.setY(y - dy); + chunk.setBlockState(context.pos, material, false); + } + } + + protected void placeScree(IChunk chunk, DecoratorContext context, int x, int y, int z) { + float steepness = context.cell.steepness + context.climate.getRand().getValue(x, z, seed2) * SLOPE_MODIFIER; + if (steepness < SCREE_STEEPNESS) { + return; + } + + float sediment = context.cell.sediment * SEDIMENT_MODIFIER; + float noise = context.climate.getRand().getValue(x, z, seed3) * SEDIMENT_NOISE; + if (sediment + noise > SCREE_VALUE) { + fillDown(context, chunk, x, z, y, y - 2, States.GRAVEL.get()); + } + } + + public boolean erodeRock(float x, float z, float steepness, float height) { + return steepness > ROCK_STEEPNESS || height > ColumnDecorator.getNoise(x, z, seed1, ROCK_VAR, ROCK_MIN); + } + + public boolean erodeDirt(float x, float z, float steepness, float height) { + return steepness > DIRT_STEEPNESS && height > ColumnDecorator.getNoise(x, z, seed2, DIRT_VAR, DIRT_MIN); + } + + private BlockState getMaterial(float x, float z, DecoratorContext context, BlockState top, BlockState middle) { + float height = context.cell.value + context.climate.getRand().getValue(x, z, seed1) * HEIGHT_MODIFIER; + float steepness = context.cell.steepness + context.climate.getRand().getValue(x, z, seed2) * SLOPE_MODIFIER; + + if (steepness > ROCK_STEEPNESS || height > ColumnDecorator.getNoise(x, z, seed1, ROCK_VAR, ROCK_MIN)) { + return rock(middle); + } + + if (steepness > DIRT_STEEPNESS && height > ColumnDecorator.getNoise(x, z, seed2, DIRT_VAR, DIRT_MIN)) { + return ground(top); + } + + return top; + } + + private static BlockState rock(BlockState state) { + if (state.getMaterial() == Material.ROCK) { + return state; + } + return States.STONE.get(); + } + + private static BlockState ground(BlockState state) { + if (state.getMaterial() == Material.ORGANIC) { + return States.DIRT.get(); + } + if (state.getMaterial() == Material.ROCK) { + return States.GRAVEL.get(); + } + if (state.getMaterial() == Material.EARTH) { + return state; + } + if (state.getMaterial() == Material.SAND) { + if (state.getBlock() == Blocks.SAND) { + return States.SANDSTONE.get(); + } + if (state.getBlock() == Blocks.RED_SAND) { + return States.RED_SANDSTONE.get(); + } + } + return States.COARSE_DIRT.get(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/GeologyDecorator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/GeologyDecorator.java new file mode 100644 index 0000000..af2af9c --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/GeologyDecorator.java @@ -0,0 +1,56 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.base; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.chunk.surface.ChunkSurfaceBuffer; +import com.terraforged.mod.material.geology.GeoManager; +import net.minecraft.world.chunk.IChunk; + +public class GeologyDecorator implements ColumnDecorator { + + private final GeoManager geology; + + public GeologyDecorator(GeoManager geology) { + this.geology = geology; + } + + @Override + public void decorate(IChunk chunk, DecoratorContext context, int x, int dy, int z) { + + } + + @Override + public void decorate(ChunkSurfaceBuffer buffer, DecoratorContext context, int x, int y, int z) { + int top = buffer.getSurfaceBottom(); + geology.getGeology(context.biome).getStrata(x, z).downwards(x, top, z, (py, state) -> { + context.pos.setPos(x, py, z); + buffer.getDelegate().setBlockState(context.pos, state, false); + return true; + }); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/RiverDecorator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/RiverDecorator.java new file mode 100644 index 0000000..62b5946 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/base/RiverDecorator.java @@ -0,0 +1,84 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.base; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.material.state.States; +import com.terraforged.core.util.Seed; +import com.terraforged.core.world.heightmap.Levels; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.mod.chunk.TerraContext; +import me.dags.noise.Module; +import me.dags.noise.Source; +import net.minecraft.block.BlockState; +import net.minecraft.world.chunk.IChunk; + +public class RiverDecorator implements ColumnDecorator { + + private final Levels levels; + private final Terrain river; + private final Terrain riverBank; + + private final BlockState dirt; + private final BlockState sand; + private final BlockState gravel; + + private final Module noise1; + + public RiverDecorator(TerraContext context) { + Seed seed = context.seed.nextSeed(); + this.levels = context.levels; + this.river = context.terrain.river; + this.riverBank = context.terrain.riverBanks; + this.dirt = States.DIRT.get(); + this.sand = States.SAND.get(); + this.gravel = States.GRAVEL.get(); + this.noise1 = Source.perlin(seed.next(), 50, 1); + } + + @Override + public void decorate(IChunk chunk, DecoratorContext context, int x, int y, int z) { + if (context.cell.tag == river) { + chunk.setBlockState(context.pos.setPos(x, y, z), dirt, false); + return; + } + if (context.cell.tag == riverBank) { + float value = noise1.getValue(x, z) * 5; + if (y + value >= levels.waterY) { + if (context.cell.steepness > 0.5) { + chunk.setBlockState(context.pos.setPos(x, y, z), gravel, false); + } else if (context.cell.steepness < 0.3) { + chunk.setBlockState(context.pos.setPos(x, y, z), sand, false); + } else { + chunk.setBlockState(context.pos.setPos(x, y, z), dirt, false); + } + } else { + chunk.setBlockState(context.pos.setPos(x, y, z), dirt, false); + } + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/feature/LayerDecorator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/feature/LayerDecorator.java new file mode 100644 index 0000000..5467be8 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/feature/LayerDecorator.java @@ -0,0 +1,87 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.feature; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.api.material.layer.LayerManager; +import com.terraforged.api.material.layer.LayerMaterial; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.heightmap.Levels; +import com.terraforged.mod.material.MaterialHelper; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.IChunk; + +public class LayerDecorator implements ColumnDecorator { + + private final LayerManager layerManager; + + public LayerDecorator(LayerManager layerManager) { + this.layerManager = layerManager; + } + + @Override + public void decorate(IChunk chunk, DecoratorContext context, int x, int y, int z) { + context.pos.setPos(x, y + 1, z); + + // if block is already a layer-type then simply set the layer property + BlockState state = chunk.getBlockState(context.pos); + LayerMaterial material = layerManager.getMaterial(state.getBlock()); + if (material != null) { + setLayer(chunk, context.pos, material, context.cell, context.levels, 0F); + return; + } + + if (MaterialHelper.isAir(state.getBlock())) { + return; + } + + // block is non-solid (grass/flower etc) + if (!state.getMaterial().blocksMovement()) { + // block below is solid + if (chunk.getBlockState(context.pos.setPos(x, y, z)).getMaterial().blocksMovement()) { + // block above is air + if (MaterialHelper.isAir(chunk.getBlockState(context.pos.setPos(x, y + 2, z)).getBlock())) { +// setLayer(chunk, pos.setPos(x, y + 1, z), context.cell, context.levels, 0.25F); + } + } + } + } + + private void setLayer(IChunk chunk, BlockPos pos, LayerMaterial material, Cell cell, Levels levels, float min) { + float height = cell.value * levels.worldHeight; + float depth = material.getDepth(height); + if (depth > min) { + int level = material.getLevel(depth); + BlockState layer = material.getState(level); + if (MaterialHelper.isAir(layer.getBlock())) { + return; + } + chunk.setBlockState(pos, layer, false); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/feature/SnowEroder.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/feature/SnowEroder.java new file mode 100644 index 0000000..2e3f125 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/feature/SnowEroder.java @@ -0,0 +1,93 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.feature; + +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorContext; +import com.terraforged.mod.chunk.TerraContext; +import com.terraforged.mod.decorator.base.ErosionDecorator; +import me.dags.noise.source.Rand; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.Heightmap; + +import java.util.function.Predicate; + +public class SnowEroder extends ErosionDecorator { + + private static final float SNOW_ROCK_STEEPNESS = 0.45F; + private static final float SNOW_ROCK_HEIGHT = 95F / 255F; + + private final int seed1; + private final int seed2; + private final int seed3; + private final Rand rand; + + public SnowEroder(TerraContext context) { + super(context); + this.seed1 = context.seed.next(); + this.seed2 = context.seed.next(); + this.seed3 = context.seed.next(); + this.rand = context.factory.getHeightmap().getClimate().getRand(); + } + + @Override + public void decorate(IChunk chunk, DecoratorContext context, int x, int y, int z) { + int surface = chunk.getTopBlockY(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, x, z); + if (y - surface > 0) { + if (y - surface > 4) { + return; + } + y = surface; + } + + if (context.biome.getTemperature(context.pos.setPos(x, y, z)) <= 0.25) { + float var = -ColumnDecorator.getNoise(x, z, seed1, 16, 0); + float hNoise = rand.getValue(x, z, seed2) * HEIGHT_MODIFIER; + float sNoise = rand.getValue(x, z, seed3) * SLOPE_MODIFIER; + float vModifier = context.cell.tag == context.terrains.volcano ? 0.15F : 0F; + float height = context.cell.value + var + hNoise + vModifier; + float steepness = context.cell.steepness + var + sNoise + vModifier; + if (snowErosion(x, z, steepness, height)) { + Predicate predicate = Heightmap.Type.MOTION_BLOCKING.getHeightLimitPredicate(); + for (int dy = 2; dy > 0; dy--) { + context.pos.setY(y + dy); + BlockState state = chunk.getBlockState(context.pos); + if (!predicate.test(state) || state.getBlock() == Blocks.SNOW) { + chunk.setBlockState(context.pos, Blocks.AIR.getDefaultState(), false); + } + } + } + } + } + + private boolean snowErosion(float x, float z, float steepness, float height) { + return steepness > ROCK_STEEPNESS + || (steepness > SNOW_ROCK_STEEPNESS && height > SNOW_ROCK_HEIGHT) + || super.erodeDirt(x, z, steepness, height); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/surface/DesertDunes.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/surface/DesertDunes.java new file mode 100644 index 0000000..7089486 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/surface/DesertDunes.java @@ -0,0 +1,98 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.surface; + +import com.terraforged.api.chunk.surface.Surface; +import com.terraforged.api.chunk.surface.SurfaceContext; +import com.terraforged.api.material.layer.LayerMaterial; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.heightmap.Levels; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.core.world.terrain.Terrains; +import com.terraforged.mod.biome.provider.BiomeProvider; +import com.terraforged.mod.biome.provider.DesertBiomes; +import com.terraforged.mod.chunk.TerraContext; +import me.dags.noise.Module; +import me.dags.noise.Source; +import me.dags.noise.func.CellFunc; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; + +public class DesertDunes implements Surface { + + private final int maxHeight; + private final Levels levels; + private final Module module; + private final Terrains terrains; + private final DesertBiomes deserts; + private final BlockPos.Mutable pos = new BlockPos.Mutable(); + + public DesertDunes(TerraContext context, int maxHeight, DesertBiomes deserts) { + Module dunes = Source.cell(context.seed.next(), 80, CellFunc.DISTANCE) + .warp(context.seed.next(), 70, 1, 70); + this.terrains = context.terrain; + this.levels = context.levels; + this.maxHeight = maxHeight; + this.deserts = deserts; + this.module = dunes; + } + + @Override + public void buildSurface(int x, int z, int surface, SurfaceContext ctx) { + float value = module.getValue(x, z) * getMask(ctx.cell); + float baseHeight = ctx.cell.value * levels.worldHeight; + float duneHeight = baseHeight + value * maxHeight; + int duneBase = (int) baseHeight; + int duneTop = (int) duneHeight; + if (duneTop < levels.waterLevel || duneTop <= baseHeight) { + return; + } + + LayerMaterial material = deserts.getSandLayers(ctx.biome); + if (material == null) { + return; + } + + fill(x, z, duneBase, duneTop, ctx, ctx.chunk, material.getFull()); + + float depth = material.getDepth(duneHeight); + int levels = material.getLevel(depth); + BlockState top = material.getState(levels); + ctx.chunk.setBlockState(pos.setPos(x, duneTop, z), top, false); + } + + public static Surface create(TerraContext context, BiomeProvider provider) { + return create(context, provider.getModifierManager().getDesertBiomes()); + } + + public static Surface create(TerraContext context, DesertBiomes desertBiomes) { + return new DesertDunes(context, 25, desertBiomes); + } + + private static float getMask(Cell cell) { + return cell.biomeMask(0F, 0.75F) * cell.mask(0.4F, 0.5F, 0F, 0.8F); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/surface/FrozenOcean.java b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/surface/FrozenOcean.java new file mode 100644 index 0000000..f20c5fa --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/decorator/surface/FrozenOcean.java @@ -0,0 +1,115 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.decorator.surface; + +import com.terraforged.api.chunk.surface.Surface; +import com.terraforged.api.chunk.surface.SurfaceContext; +import com.terraforged.api.material.state.States; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.heightmap.Levels; +import com.terraforged.mod.chunk.TerraContext; +import me.dags.noise.Module; +import me.dags.noise.Source; +import me.dags.noise.util.NoiseUtil; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder; + +public class FrozenOcean implements Surface { + + private final Module up; + private final Module down; + private final Module fadeUp; + private final Module bergTop; + private final Module seaFloor; + private final Levels levels; + + public FrozenOcean(TerraContext context, int height, int depth) { + Levels levels = context.levels; + Module shape = Source.perlin(context.seed.next(), 65, 3) + .warp(context.seed.next(), 15, 1, 10) + .cache(); + Module mask = shape.threshold(0.6).cache(); + Module fadeDown = shape.clamp(0.6, 0.725).map(0, 1); + + this.levels = levels; + + this.fadeUp = shape.clamp(0.6, 0.65).map(0, 1); + + this.up = Source.ridge(context.seed.next(), 50, 3) + .mult(mask) + .mult(fadeUp) + .scale(levels.scale(height)); + + this.down = Source.ridge(context.seed.next(), 60, 3) + .mult(mask) + .mult(fadeDown) + .scale(levels.scale(depth)); + + this.bergTop = Source.perlin(context.seed.next(), 25, 2) + .scale(levels.scale(3)) + .bias(levels.scale(2)); + + this.seaFloor = Source.perlin(context.seed.next(), 50, 1) + .scale(levels.scale(3)) + .bias(levels.scale(1)); + } + + @Override + public void buildSurface(int x, int z, int height, SurfaceContext ctx) { + int center = levels.waterLevel - 5; + int top = center + (int) (up.getValue(x, z) * levels.worldHeight); + int topDepth = (int) (bergTop.getValue(x, z) * levels.worldHeight); + int bottom = center - (int) (down.getValue(x, z) * levels.worldHeight); + + // set iceberg materials + BlockPos.Mutable pos = new BlockPos.Mutable(x, height, z); + for (int dy = top; dy > bottom; dy--) { + pos.setY(dy); + BlockState state = getMaterial(x, dy, z, top, topDepth); + ctx.chunk.setBlockState(pos, state, false); + } + + // set ocean floor to gravel + int floorBed = (int) (ctx.cell.value * ctx.levels.worldHeight); + int floorDepth = (int) (seaFloor.getValue(x, z) * levels.worldHeight); + for (int dy = 0; dy < floorDepth; dy++) { + pos.setY(floorBed - dy); + ctx.chunk.setBlockState(pos, SurfaceBuilder.GRAVEL, false); + } + } + + private BlockState getMaterial(int x, int y, int z, int top, int topDepth) { + if (y >= top - topDepth && fadeUp.getValue(x, z) == 1) { + return States.SNOW_BLOCK.get(); + } + return States.PACKED_ICE.get(); + } + + private static float getMask(Cell cell) { + return NoiseUtil.map(cell.biomeTypeMask * cell.riverMask, 0F, 0.3F, 0.3F); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/Matchers.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/Matchers.java new file mode 100644 index 0000000..bcd94e8 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/Matchers.java @@ -0,0 +1,39 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature; + +import com.terraforged.feature.matcher.feature.FeatureMatcher; + +public class Matchers { + + public static final FeatureMatcher STONE_BLOBS = FeatureMatcher.builder() + .or("minecraft:ore").and("minecraft:dirt") + .or("minecraft:ore").and("minecraft:gravel") + .or("minecraft:ore").and("minecraft:granite") + .or("minecraft:ore").and("minecraft:diorite") + .or("minecraft:ore").and("minecraft:andesite") + .build(); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/TerrainHelper.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/TerrainHelper.java new file mode 100644 index 0000000..184b466 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/TerrainHelper.java @@ -0,0 +1,178 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature; + +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; +import me.dags.noise.Module; +import me.dags.noise.Source; +import me.dags.noise.util.NoiseUtil; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.MutableBoundingBox; +import net.minecraft.world.IWorld; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.jigsaw.JigsawPattern; +import net.minecraft.world.gen.feature.structure.AbstractVillagePiece; +import net.minecraft.world.gen.feature.structure.Structure; +import net.minecraft.world.gen.feature.structure.StructurePiece; +import net.minecraft.world.gen.feature.structure.StructureStart; + +public class TerrainHelper { + + private final Module noise; + private final float radius; + + public TerrainHelper(int seed, float radius) { + this.noise = Source.perlin(++seed, 8, 1).alpha(0.75); + this.radius = radius; + } + + public void flatten(IWorld world, IChunk chunk, int chunkStartX, int chunkStartZ) { + ObjectList pieces = new ObjectArrayList<>(10); + collectPieces(world, chunk, pieces); + buildBases(chunk, pieces, chunkStartX, chunkStartZ); + } + + // see NoiseChunkGenerator + private void collectPieces(IWorld world, IChunk chunk, ObjectList pieces) { + ChunkPos pos = chunk.getPos(); + for (Structure structure : Feature.ILLAGER_STRUCTURES) { + String name = structure.getStructureName(); + LongIterator structureIds = chunk.getStructureReferences(name).iterator(); + + while (structureIds.hasNext()) { + long id = structureIds.nextLong(); + ChunkPos structurePos = new ChunkPos(id); + IChunk neighbourChunk = world.getChunk(structurePos.asBlockPos()); + StructureStart structurestart = neighbourChunk.getStructureStart(name); + if (structurestart != null && structurestart.isValid()) { + for (StructurePiece structurepiece : structurestart.getComponents()) { + if (structurepiece.func_214810_a(pos, 12) && structurepiece instanceof AbstractVillagePiece) { + AbstractVillagePiece piece = (AbstractVillagePiece) structurepiece; + JigsawPattern.PlacementBehaviour placement = piece.getJigsawPiece().getPlacementBehaviour(); + if (placement == JigsawPattern.PlacementBehaviour.RIGID) { + pieces.add(piece); + } + } + } + } + } + } + } + + // try to fill in type air beneath village pieces with the biomes default filler block + private void buildBases(IChunk chunk, ObjectList pieces, int chunkStartX, int chunkStartZ) { + BlockPos.Mutable pos = new BlockPos.Mutable(); + MutableBoundingBox chunkBounds = new MutableBoundingBox(chunkStartX, chunkStartZ, chunkStartX + 15, chunkStartZ + 15); + for (AbstractVillagePiece piece : pieces) { + MutableBoundingBox pieceBounds = piece.getBoundingBox(); + + int length = Math.min(pieceBounds.maxX - pieceBounds.minX, pieceBounds.maxZ - pieceBounds.minZ); + int borderRadius = Math.max(5, NoiseUtil.round(length * radius)); + MutableBoundingBox expanded = expand(pieceBounds, borderRadius); + + if (!expanded.intersectsWith(chunkBounds)) { + continue; + } + + // intersecting area between the generator bounds and the village piece bounds + int startX = Math.max(chunkStartX, expanded.minX); + int startZ = Math.max(chunkStartZ, expanded.minZ); + int endX = Math.min(chunkStartX + 15, expanded.maxX); + int endZ = Math.min(chunkStartZ + 15, expanded.maxZ); + + // iterate the intersecting area + for (int z = startZ; z <= endZ; z++) { + for (int x = startX; x <= endX; x++) { + // local generator coords + int dx = x & 15; + int dz = z & 15; + + // the paste position of the village piece + BlockPos position = piece.getPos(); + + int offset = piece.getGroundLevelDelta(); + int level = position.getY() + (offset - 1); + int surface = chunk.getTopBlockY(Heightmap.Type.OCEAN_FLOOR_WG, dx, dz) - 1; + int height = level - surface; + if (height <= 0) { + continue; + } + + float radius2 = Math.max(1, borderRadius * borderRadius * noise.getValue(x, z)); + float alpha = getAlpha(pieceBounds, radius2, x, z); + if (alpha == 0F) { + continue; + } + + if (alpha < 1F) { + alpha = alpha * alpha; + height = NoiseUtil.round(alpha * height); + } + + BlockState state = Blocks.STONE.getDefaultState(); + for (int dy = surface + height; dy >= surface; dy--) { + pos.setPos(dx, dy, dz); + if (chunk.getBlockState(pos).isSolid()) { + break; + } + chunk.setBlockState(pos.setPos(dx, dy, dz), state, false); + } + } + } + } + } + + private static MutableBoundingBox expand(MutableBoundingBox box, int radius) { + return new MutableBoundingBox( + box.minX - radius, + box.minY, + box.minZ - radius, + box.maxX + radius, + box.maxY, + box.maxZ + radius + ); + } + + private static float getAlpha(MutableBoundingBox box, float radius2, int x, int y) { + int dx = x < box.minX ? box.minX - x : x > box.maxX ? x - box.maxX : 0; + int dy = y < box.minZ ? box.minZ - y : y > box.maxZ ? y - box.maxZ : 0; + int d2 = dx * dx + dy * dy; + if (d2 == 0) { + return 1F; + } + if (d2 > radius2) { + return 0F; + } + return 1 - (d2 / radius2); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/predicate/TreeLine.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/predicate/TreeLine.java new file mode 100644 index 0000000..fd9f6d6 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/predicate/TreeLine.java @@ -0,0 +1,57 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature.predicate; + +import com.terraforged.core.world.climate.Climate; +import com.terraforged.feature.predicate.FeaturePredicate; +import com.terraforged.mod.chunk.TerraContext; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.Heightmap; + +public class TreeLine implements FeaturePredicate { + + private final int worldHeight; + private final Climate climate; + + public TreeLine(TerraContext context) { + this.worldHeight = context.levels.worldHeight; + this.climate = context.heightmap.getClimate(); + } + + @Override + public boolean test(IChunk chunk, Biome biome) { + int x = chunk.getPos().getXStart() + 8; + int z = chunk.getPos().getZStart() + 8; + int treeline = getTreeline(x, z); + int y = chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, 8, 8); + return y < treeline; + } + + private int getTreeline(int x, int z) { + return (int) (climate.getTreeLine(x, z) * worldHeight); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingConfig.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingConfig.java new file mode 100644 index 0000000..70b4333 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingConfig.java @@ -0,0 +1,122 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature.tree; + +import com.google.gson.reflect.TypeToken; +import com.mojang.datafixers.types.DynamicOps; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.NoFeatureConfig; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class SaplingConfig { + + private static final TypeToken> TOKEN = new TypeToken>() { + }; + + private final Map normal; + private final Map giant; + + public SaplingConfig() { + normal = new HashMap<>(); + giant = new HashMap<>(); + } + + public SaplingConfig(T config, DynamicOps ops) { + this.normal = getSection("normal", config, ops); + this.giant = getSection("giant", config, ops); + } + + public SaplingConfig addNormal(ResourceLocation name, int weight) { + normal.put(name, weight); + return this; + } + + public SaplingConfig addNormal(String name, int weight) { + normal.put(new ResourceLocation(name), weight); + return this; + } + + public SaplingConfig addGiant(ResourceLocation name, int weight) { + giant.put(name, weight); + return this; + } + + public SaplingConfig addGiant(String name, int weight) { + giant.put(new ResourceLocation(name), weight); + return this; + } + + private Map getSection(String key, T config, DynamicOps ops) { + return ops.get(config, key).flatMap(ops::getMapValues).map(map -> { + Map backing = new HashMap<>(); + + for (Map.Entry entry : map.entrySet()) { + String name = ops.getStringValue(entry.getKey()).orElse(""); + int weight = ops.getNumberValue(entry.getValue()).orElse(0).intValue(); + if (name.isEmpty() || weight == 0) { + continue; + } + + ResourceLocation loc = new ResourceLocation(name); + backing.put(loc, weight); + } + + return backing; + }).orElse(Collections.emptyMap()); + } + + public List> getNormal() { + return build(normal); + } + + public List> getGiant() { + return build(giant); + } + + @SuppressWarnings("unchecked") + public static List> build(Map map) { + List> list = new LinkedList<>(); + for (Map.Entry entry : map.entrySet()) { + Feature feature = ForgeRegistries.FEATURES.getValue(entry.getKey()); + if (feature == null) { + continue; + } + + if (TOKEN.getRawType().isAssignableFrom(feature.getClass())) { + Feature noConfFeature = (Feature) feature; + list.add(noConfFeature); + } + } + return list; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingFeature.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingFeature.java new file mode 100644 index 0000000..b8204ff --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingFeature.java @@ -0,0 +1,66 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature.tree; + +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.NoFeatureConfig; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class SaplingFeature { + + private final List> normal; + private final List> giant; + + public SaplingFeature(SaplingConfig config) { + this.normal = new ArrayList<>(config.getNormal()); + this.giant = new ArrayList<>(config.getGiant()); + } + + public Feature nextNormal(Random random) { + if (normal.isEmpty()) { + return null; + } + return normal.get(random.nextInt(normal.size())); + } + + public Feature nextGiant(Random random) { + if (giant.isEmpty()) { + return null; + } + return giant.get(random.nextInt(giant.size())); + } + + public int getNormalCount() { + return normal.size(); + } + + public int getGiantCount() { + return giant.size(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingListener.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingListener.java new file mode 100644 index 0000000..18e2e24 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingListener.java @@ -0,0 +1,141 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature.tree; + +import com.terraforged.mod.TerraWorld; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.SaplingBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.IWorld; +import net.minecraft.world.World; +import net.minecraft.world.WorldType; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.NoFeatureConfig; +import net.minecraftforge.event.world.SaplingGrowTreeEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE) +public class SaplingListener { + + private static final BlockPos[] NONE = {BlockPos.ZERO}; + + private static final Vec3i[][] DIRECTIONS = { + {new Vec3i(0, 0, 1), new Vec3i(1, 0, 1), new Vec3i(1, 0, 0)}, + {new Vec3i(1, 0, 0), new Vec3i(1, 0, -1), new Vec3i(0, 0, -1)}, + {new Vec3i(0, 0, -1), new Vec3i(-1, 0, -1), new Vec3i(-1, 0, 0)}, + {new Vec3i(-1, 0, 0), new Vec3i(-1, 0, 1), new Vec3i(0, 0, 1)}, + }; + + @SubscribeEvent + public static void onTreeGrow(SaplingGrowTreeEvent event) { + if (!TerraWorld.isTerraWorld(event.getWorld())) { + return; + } + + IWorld world = event.getWorld(); + BlockPos pos = event.getPos(); + Block block = world.getBlockState(pos).getBlock(); + if (block instanceof SaplingBlock) { + // get the sapling feature for the given block type + SaplingFeature tree = SaplingManager.getSapling(block.getRegistryName()); + + // tree is null if the sapling type hasn't been configured + if (tree == null) { + return; + } + + // check for a 2x2 arrangement of sapling blocks around the position + Vec3i[] directions = getNeighbourDirections(world, block, pos); + + // length is 1 if a full 2x2 arrangement could not be found + if (directions.length == 1) { + placeNormal(tree, event, directions); + } else { + placeGiant(tree, event, block, directions); + } + } + } + + private static void placeNormal(SaplingFeature tree, SaplingGrowTreeEvent event, Vec3i[] directions) { + SaplingPlacer.placeTree(tree.nextNormal(event.getRand()), event, directions); + } + + private static void placeGiant(SaplingFeature tree, SaplingGrowTreeEvent event, Block type, Vec3i[] directions) { + Feature feature = tree.nextGiant(event.getRand()); + + // if no giant tree exists for this sapling type try and place a normal one instead + if (feature == null) { + placeNormal(tree, event, directions); + return; + } + + // do not continue if unable to place the tree + if (!SaplingPlacer.placeTree(feature, event, directions)) { + return; + } + + // iterate through the contributing saplings and remove any that were not destroyed during tree creation + BlockPos pos = event.getPos(); + for (Vec3i dir : directions) { + BlockPos blockPos = pos.add(dir); + BlockState state = event.getWorld().getBlockState(blockPos); + if (state.getBlock() == type) { + event.getWorld().removeBlock(blockPos, false); + } + } + } + + private static Vec3i[] getNeighbourDirections(IWorld world, Block block, BlockPos pos) { + for (Vec3i[] dirs : DIRECTIONS) { + boolean match = true; + for (Vec3i dir : dirs) { + BlockState state = world.getBlockState(pos.add(dir)); + if (state.getBlock() != block) { + match = false; + break; + } + } + if (match) { + return dirs; + } + } + return NONE; + } + + private static boolean isTerraWorld(IWorld world) { + if (world instanceof World) { + return isTerraWorld(((World) world).getWorldType()); + } + return false; + } + + private static boolean isTerraWorld(WorldType type) { + return type.getName().equals("terraforged"); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingManager.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingManager.java new file mode 100644 index 0000000..9335703 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingManager.java @@ -0,0 +1,90 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature.tree; + +import com.mojang.datafixers.types.DynamicOps; +import com.terraforged.mod.Log; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.util.ResourceLocation; + +import java.util.HashMap; +import java.util.Map; + +public class SaplingManager { + + private static final Map saplings = new HashMap<>(); + + public static SaplingFeature getSapling(ResourceLocation name) { + return saplings.get(name); + } + + public static void register(Block block, SaplingConfig config) { + register(block.getRegistryName(), config); + } + + public static void register(ResourceLocation location, T config, DynamicOps ops) { + register(location, new SaplingConfig(config, ops)); + } + + public static void register(ResourceLocation location, SaplingConfig config) { + saplings.put(location, new SaplingFeature(config)); + } + + public static void init() { + register(Blocks.OAK_SAPLING, new SaplingConfig() + .addNormal("terraforged:oak_small", 4) + .addNormal("terraforged:oak_forest", 3) + .addNormal("terraforged:oak_large", 2) + .addGiant("terraforged:oak_huge", 1)); + + register(Blocks.BIRCH_SAPLING, new SaplingConfig() + .addNormal("terraforged:birch_small", 4) + .addNormal("terraforged:birch_forest", 3) + .addNormal("terraforged:birch_large", 1)); + + register(Blocks.JUNGLE_SAPLING, new SaplingConfig() + .addNormal("terraforged:jungle_small", 4) + .addGiant("terraforged:jungle_large", 1)); + + register( + Blocks.SPRUCE_SAPLING, + new SaplingConfig() + .addNormal("terraforged:spruce_small", 4) + .addNormal("terraforged:spruce_large", 1) + .addGiant("terraforged:redwood_huge", 1)); + + register(Blocks.DARK_OAK_SAPLING, new SaplingConfig() + .addNormal("terraforged:dark_oak_small", 4) + .addNormal("terraforged:dark_oak_large", 1)); + + register(Blocks.ACACIA_SAPLING, new SaplingConfig() + .addNormal("terraforged:acacia_small", 2) + .addNormal("terraforged:acacia_large", 1)); + + Log.info("Registered saplings"); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingPlacer.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingPlacer.java new file mode 100644 index 0000000..788a12c --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/SaplingPlacer.java @@ -0,0 +1,151 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature.tree; + +import com.terraforged.feature.template.decorator.DecoratedFeature; +import com.terraforged.feature.template.decorator.DecoratorWorld; +import com.terraforged.feature.template.feature.TemplateFeature; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.IWorld; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.NoFeatureConfig; +import net.minecraft.world.server.ServerChunkProvider; +import net.minecraftforge.event.world.SaplingGrowTreeEvent; +import net.minecraftforge.eventbus.api.Event; + +public class SaplingPlacer { + + public static boolean placeTree(Feature feature, SaplingGrowTreeEvent event, Vec3i[] dirs) { + if (feature == null) { + return false; + } + + event.setResult(Event.Result.DENY); + + if (feature instanceof DecoratedFeature) { + return placeDecorated((DecoratedFeature) feature, event, dirs); + } + + return placeNormal(feature, event, dirs); + } + + private static boolean placeDecorated(DecoratedFeature feature, SaplingGrowTreeEvent event, Vec3i[] dirs) { + if (!(event.getWorld().getChunkProvider() instanceof ServerChunkProvider)) { + return false; + } + + TreeBuffer buffer = new TreeBuffer(event.getWorld(), event.getPos()); + W world = feature.wrap(buffer); + + ChunkGenerator generator = ((ServerChunkProvider) event.getWorld().getChunkProvider()).getChunkGenerator(); + feature.placeFeature(world, generator, event.getRand(), event.getPos(), NoFeatureConfig.NO_FEATURE_CONFIG); + + // check that the tree can grow here + if (overheadIsSolid(event.getWorld(), event.getPos(), buffer.getTopY())) { + return false; + } + + BlockPos translation = buffer.getBaseMin().add(getMin(dirs)); + + // apply buffer to world with translation + applyBuffer(buffer, event.getWorld(), translation); + + // translate the decoration positions and apply in the world + world.setDelegate(event.getWorld()); + world.translate(translation); + feature.decorate(world, event.getRand()); + return true; + } + + private static boolean placeNormal(Feature feature, SaplingGrowTreeEvent event, Vec3i[] dirs) { + // apply the feature to a buffer + TreeBuffer buffer = new TreeBuffer(event.getWorld(), event.getPos()); + buffer.placeFeature(feature, event.getPos(), event.getRand()); + + // check that the tree can grow here + if (overheadIsSolid(event.getWorld(), event.getPos(), buffer.getTopY())) { + return false; + } + + // get the min position in the 2x2 grid + BlockPos translation = buffer.getBaseMin().add(getMin(dirs)); + + // copy the feature from the buffer to the world while translating each block + applyBuffer(buffer, event.getWorld(), translation); + return true; + } + + private static void applyBuffer(TreeBuffer buffer, IWorld world, BlockPos translation) { + try (BlockPos.PooledMutable pos = BlockPos.PooledMutable.retain()) { + for (TemplateFeature.BlockInfo block : buffer.getChanges()) { + int x = block.getPos().getX() + translation.getX(); + int y = block.getPos().getY(); + int z = block.getPos().getZ() + translation.getZ(); + + pos.setPos(x, y, z); + + if (pos.getY() > 90) { + BlockState current = world.getBlockState(pos); + if (current.isSolid()) { + continue; + } + } + + world.setBlockState(pos, block.getState(), 2); + } + } + } + + private static boolean overheadIsSolid(IWorld world, BlockPos pos, int topY) { + try (BlockPos.PooledMutable blockPos = BlockPos.PooledMutable.retain()) { + for (int y = pos.getY(); y <= topY; y++) { + blockPos.setPos(pos.getX(), y, pos.getZ()); + BlockState state = world.getBlockState(pos); + if (state.isSolid()) { + return true; + } + } + return false; + } + } + + private static Vec3i getMin(Vec3i[] dirs) { + Vec3i min = Vec3i.NULL_VECTOR; + for (Vec3i dir : dirs) { + if (dir.getX() < min.getX() && dir.getZ() <= min.getZ()) { + min = dir; + continue; + } + if (dir.getZ() < min.getZ() && dir.getX() <= min.getX()) { + min = dir; + } + } + return min; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/TreeBuffer.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/TreeBuffer.java new file mode 100644 index 0000000..8488612 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/tree/TreeBuffer.java @@ -0,0 +1,123 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.feature.tree; + +import com.terraforged.feature.template.feature.TemplateFeature; +import com.terraforged.feature.util.WorldDelegate; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.IWorld; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.IFeatureConfig; +import net.minecraft.world.gen.feature.NoFeatureConfig; +import net.minecraft.world.server.ServerChunkProvider; + +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +public class TreeBuffer extends WorldDelegate { + + private int topY = 0; + private final BlockPos pos; + private final MutableVec3i baseMin = new MutableVec3i(0); + private final MutableVec3i baseMax = new MutableVec3i(0); + private final List changes = new LinkedList<>(); + + public TreeBuffer(IWorld delegate, BlockPos pos) { + super(delegate); + this.pos = pos; + } + + public int getTopY() { + return topY; + } + + public BlockPos getBaseMin() { + return new BlockPos(-baseMin.x, baseMin.y, -baseMin.z); + } + + public BlockPos getBaseMax() { + return new BlockPos(baseMax.x, baseMax.y, baseMax.z); + } + + public Iterable getChanges() { + return changes; + } + + @Override + public boolean setBlockState(BlockPos pos, BlockState state, int i) { + if (state.isSolid()) { + recordPos(pos); + } + changes.add(new TemplateFeature.BlockInfo(pos, state)); + return true; + } + + public void placeFeature(Feature feature, BlockPos pos, Random random) { + placeFeature(feature, pos, random, NoFeatureConfig.NO_FEATURE_CONFIG); + } + + public void placeFeature(Feature feature, BlockPos pos, Random random, T config) { + if (getChunkProvider() instanceof ServerChunkProvider) { + ServerChunkProvider chunkProvider = (ServerChunkProvider) getChunkProvider(); + feature.place(this, chunkProvider.getChunkGenerator(), random, pos, config); + } + } + + private void recordPos(BlockPos pos) { + if (pos.getY() > topY) { + topY = pos.getY(); + baseMax.max(pos.subtract(this.pos)); + } else if (pos.getY() == this.pos.getY()) { + baseMin.min(pos.subtract(this.pos)); + } + } + + private static class MutableVec3i { + + private int x, y, z; + + private MutableVec3i(int start) { + x = start; + y = start; + z = start; + } + + private void min(Vec3i vec) { + x = Math.min(x, vec.getX()); + y = Math.min(y, vec.getY()); + z = Math.min(z, vec.getZ()); + } + + private void max(Vec3i vec) { + x = Math.max(x, vec.getX()); + y = Math.max(y, vec.getY()); + z = Math.max(z, vec.getZ()); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/OverlayRenderer.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/OverlayRenderer.java new file mode 100644 index 0000000..2cc9fde --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/OverlayRenderer.java @@ -0,0 +1,33 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui; + +import net.minecraft.client.gui.screen.Screen; + +public interface OverlayRenderer { + + void renderOverlays(Screen screen, int mouseX, int mouseY); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/OverlayScreen.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/OverlayScreen.java new file mode 100644 index 0000000..4514719 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/OverlayScreen.java @@ -0,0 +1,87 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui; + +import com.terraforged.mod.gui.element.CheckBox; +import com.terraforged.mod.gui.element.Element; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.Widget; +import net.minecraft.util.text.TranslationTextComponent; + +public class OverlayScreen extends Screen implements OverlayRenderer { + + public boolean showTooltips = false; + + public OverlayScreen() { + super(new TranslationTextComponent("")); + super.minecraft = Minecraft.getInstance(); + super.font = minecraft.fontRenderer; + } + + @Override + public T addButton(T buttonIn) { + return super.addButton(buttonIn); + } + + @Override + public void render(int mouseX, int mouseY, float partialTicks) { + super.render(mouseX, mouseY, partialTicks); + if (showTooltips) { + renderOverlays(this, mouseX, mouseY); + } + } + + @Override + public void renderOverlays(Screen screen, int mouseX, int mouseY) { + for (Widget button : buttons) { + if (button.isMouseOver(mouseX, mouseY)) { + if (button instanceof Element) { + screen.renderTooltip(((Element) button).getTooltip(), mouseX, mouseY); + return; + } + } + } + } + + @Override + protected void init() { + addButton(new CheckBox("Tooltips", showTooltips) { + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + showTooltips = isChecked(); + } + + @Override + public void render(int mouseX, int mouseY, float partial) { + this.x = OverlayScreen.this.width - width - 12; + this.y = 8; + super.render(mouseX, mouseY, partial); + } + }); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/ScrollPane.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/ScrollPane.java new file mode 100644 index 0000000..211044b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/ScrollPane.java @@ -0,0 +1,123 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui; + +import com.terraforged.mod.gui.element.Element; +import com.terraforged.mod.gui.preview.Preview; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.IGuiEventListener; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.Widget; +import net.minecraft.client.gui.widget.list.AbstractOptionList; + +import java.util.Collections; +import java.util.List; + +public class ScrollPane extends AbstractOptionList implements OverlayRenderer { + + private boolean hovered = false; + + public ScrollPane(int slotHeightIn) { + super(Minecraft.getInstance(), 0, 0, 0, 0, slotHeightIn); + } + + public void addButton(Widget button) { + super.addEntry(new Entry(button)); + } + + @Override + public void renderOverlays(Screen screen, int x, int y) { + for (Entry entry : this.children()) { + if (entry.isMouseOver(x, y) && entry.option.isMouseOver(x, y)) { + Widget button = entry.option; + if (button instanceof Element) { + screen.renderTooltip(((Element) button).getTooltip(), x, y); + return; + } + } + } + } + + @Override + public int getRowWidth() { + return width - 20; + } + + @Override + public void render(int x, int y, float partialTicks) { + super.render(x, y, partialTicks); + hovered = isMouseOver(x, y); + } + + @Override + protected int getScrollbarPosition() { + return getRight(); + } + + @Override + public boolean mouseScrolled(double x, double y, double direction) { + return hovered && super.mouseScrolled(x, y, direction); + } + + public class Entry extends AbstractOptionList.Entry { + + public final Widget option; + + public Entry(Widget option) { + this.option = option; + } + + @Override + public List children() { + return Collections.singletonList(option); + } + + @Override + public boolean mouseClicked(double x, double y, int button) { + return option.mouseClicked(x, y, button); + } + + @Override + public boolean mouseReleased(double x, double y, int button) { + return option.mouseReleased(x, y, button); + } + + @Override + public void render(int index, int top, int left, int width, int height, int mouseX, int mouseY, boolean wut, float partialTicks) { + int optionWidth = Math.min(396, width); + int padding = (width - optionWidth) / 2; + option.x = left + padding; + option.y = top; + option.visible = true; + option.setWidth(optionWidth); + option.setHeight(height); + if (option instanceof Preview) { + option.setHeight(option.getWidth()); + } + option.render(mouseX, mouseY, partialTicks); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/SettingsScreen.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/SettingsScreen.java new file mode 100644 index 0000000..f451d4c --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/SettingsScreen.java @@ -0,0 +1,257 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.terraforged.core.settings.Settings; +import com.terraforged.mod.TerraWorld; +import com.terraforged.mod.gui.element.TerraLabel; +import com.terraforged.mod.gui.page.FeaturePage; +import com.terraforged.mod.gui.page.FilterPage; +import com.terraforged.mod.gui.page.GeneratorPage; +import com.terraforged.mod.gui.page.Page; +import com.terraforged.mod.gui.page.RiverPage; +import com.terraforged.mod.gui.page.StructurePage; +import com.terraforged.mod.gui.page.TerrainPage; +import com.terraforged.mod.gui.preview.PreviewPage; +import com.terraforged.mod.settings.TerraSettings; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screen.CreateWorldScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.button.Button; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.Util; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +public class SettingsScreen extends OverlayScreen { + + private static final Button.IPressable NO_ACTION = b -> {}; + + private final Page[] pages; + private final CreateWorldScreen parent; + private final PreviewPage preview = new PreviewPage(); + private final TerraSettings settings = new TerraSettings(); + + private int pageIndex = 0; + + public SettingsScreen(CreateWorldScreen parent) { + NBTHelper.deserialize(parent.chunkProviderSettingsJson, settings); + this.parent = parent; + this.pages = new Page[]{ + new GeneratorPage(settings, preview), + new TerrainPage(settings, preview), + new RiverPage(settings, preview), + new FilterPage(settings, preview), + new FeaturePage(settings), + new StructurePage(settings) + }; + } + + @Override + protected void init() { + super.buttons.clear(); + super.children.clear(); + + int buttonsCenter = width / 2; + int buttonWidth = 50; + int buttonHeight = 20; + int buttonPad = 2; + int buttonsRow = height - 25; + + if (pageIndex < pages.length) { + Page page = pages[pageIndex]; + TerraLabel title = new TerraLabel(page.getTitle()); + title.visible = true; + title.x = 16; + title.y = 15; + buttons.add(title); + + try { + page.initPage(10, 30, this); + preview.initPage(10, 30, this); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + // -52 + addButton(new Button(buttonsCenter - buttonWidth - buttonPad, buttonsRow, buttonWidth, buttonHeight, "Cancel" + , b -> onClose())); + + // +2 + addButton(new Button(buttonsCenter + buttonPad, buttonsRow, buttonWidth, buttonHeight, "Done", b -> { + for (Page page : pages) { + page.save(); + } + parent.chunkProviderSettingsJson = NBTHelper.serializeCompact(settings); + onClose(); + })); + + + // -106 + addButton(new Button(buttonsCenter - (buttonWidth * 2 + (buttonPad * 3)), buttonsRow, buttonWidth, + buttonHeight, "<<", NO_ACTION) { + @Override + public void render(int mouseX, int mouseY, float partialTicks) { + super.active = hasPrevious(); + super.render(mouseX, mouseY, partialTicks); + } + + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + if (hasPrevious()) { + pageIndex--; + init(); + } + } + + private boolean hasPrevious() { + return pageIndex > 0; + } + }); + + // +56 + addButton(new Button(buttonsCenter + buttonWidth + (buttonPad * 3), buttonsRow, buttonWidth, buttonHeight, + ">>", NO_ACTION) { + @Override + public void render(int mouseX, int mouseY, float partialTicks) { + super.active = hasNext(); + super.render(mouseX, mouseY, partialTicks); + } + + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + if (hasNext()) { + pageIndex++; + init(); + } + } + + private boolean hasNext() { + return pageIndex + 1 < pages.length; + } + }); + + addButton(new Button(width - buttonWidth - 15, buttonsRow, buttonWidth, buttonHeight, "Export", NO_ACTION) { + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + export(settings); + } + }); + + super.init(); + } + + @Override + public void render(int mouseX, int mouseY, float partialTicks) { + super.renderBackground(); + pages[pageIndex].visit(pane -> pane.render(mouseX, mouseY, partialTicks)); + preview.visit(pane -> pane.render(mouseX, mouseY, partialTicks)); + super.render(mouseX, mouseY, partialTicks); + } + + @Override + public void renderOverlays(Screen screen, int mouseX, int mouseY) { + super.renderOverlays(screen, mouseX, mouseY); + pages[pageIndex].visit(pane -> pane.renderOverlays(screen, mouseX, mouseY)); + preview.visit(pane -> pane.renderOverlays(screen, mouseX, mouseY)); + } + + @Override + public boolean mouseClicked(double x, double y, int button) { + boolean a = pages[pageIndex].action(pane -> pane.mouseClicked(x, y, button)); + boolean b = preview.action(pane -> pane.mouseClicked(x, y, button)); + boolean c = super.mouseClicked(x, y, button); + return a || b || c; + } + + @Override + public boolean mouseReleased(double x, double y, int button) { + boolean a = pages[pageIndex].action(pane -> pane.mouseReleased(x, y, button)); + boolean b = preview.action(pane -> pane.mouseReleased(x, y, button)); + boolean c = super.mouseReleased(x, y, button); + return a || b || c; + } + + @Override + public boolean mouseDragged(double x, double y, int button, double dx, double dy) { + boolean a = pages[pageIndex].action(pane -> pane.mouseDragged(x, y, button, dx, dy)); + boolean b = preview.action(pane -> pane.mouseDragged(x, y, button, dx, dy)); + boolean c = super.mouseDragged(x, y, button, dx, dy); + return a || b || c; + } + + @Override + public boolean mouseScrolled(double x, double y, double direction) { + boolean a = pages[pageIndex].action(pane -> pane.mouseScrolled(x, y, direction)); + boolean b = preview.action(pane -> pane.mouseScrolled(x, y, direction)); + boolean c = super.mouseScrolled(x, y, direction); + return a || b || c; + } + + @Override + public boolean keyPressed(int i, int j, int k) { + boolean a = pages[pageIndex].action(pane -> pane.keyPressed(i, j, k)); + boolean b = preview.action(pane -> pane.keyPressed(i, j, k)); + boolean c = super.keyPressed(i, j, k); + return a || b || c; + } + + @Override + public void onClose() { + for (Page page : pages) { + page.close(); + } + preview.close(); + Minecraft.getInstance().displayGuiScreen(parent); + } + + private void export(Settings settings) { + for (Page page : pages) { + page.save(); + } + CompoundNBT tag = NBTHelper.serializeCompact(settings); + JsonElement json = NBTHelper.toJson(tag); + File config = new File(Minecraft.getInstance().gameDir, "config"); + File file = new File(config, TerraWorld.SETTINGS_FILE_NAME); + try (Writer writer = new BufferedWriter(new FileWriter(file))) { + new GsonBuilder().setPrettyPrinting().create().toJson(json, writer); + Util.getOSType().openURI(file.getParentFile().toURI()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/CheckBox.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/CheckBox.java new file mode 100644 index 0000000..5a6cfeb --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/CheckBox.java @@ -0,0 +1,60 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.element; + +public class CheckBox extends TerraButton implements Element { + + private boolean checked = false; + + public CheckBox(String displayString, boolean isChecked) { + super(displayString); + this.visible = true; + this.width = 70; + this.height = 16; + checked = isChecked; + } + + public boolean isChecked() { + return checked; + } + + public void setChecked(boolean checked) { + this.checked = checked; + } + + @Override + public void onClick(double x, double y) { + super.onClick(x, y); + checked = !checked; + } + + @Override + public void render(int x, int y, float ticks) { + active = !checked; + super.render(x, y, ticks); + active = true; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/Element.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/Element.java new file mode 100644 index 0000000..2596595 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/Element.java @@ -0,0 +1,59 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.element; + +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.INBT; +import net.minecraft.nbt.ListNBT; +import net.minecraftforge.common.util.Constants; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public interface Element { + + AtomicInteger ID_COUNTER = new AtomicInteger(0); + + default List getTooltip() { + return Collections.emptyList(); + } + + static int nextID() { + return ID_COUNTER.getAndAdd(1); + } + + static List readTooltip(CompoundNBT value) { + if (value.contains("#comment")) { + ListNBT comment = value.getList("#comment", Constants.NBT.TAG_STRING); + return comment.stream() + .map(INBT::getString) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraButton.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraButton.java new file mode 100644 index 0000000..5966e4c --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraButton.java @@ -0,0 +1,35 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.element; + +import net.minecraftforge.fml.client.gui.widget.ExtendedButton; + +public class TerraButton extends ExtendedButton implements Element { + + public TerraButton(String displayString) { + super(0, 0, 200, 20, displayString, b -> {}); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraLabel.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraLabel.java new file mode 100644 index 0000000..8acddf5 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraLabel.java @@ -0,0 +1,52 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.element; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; + +public class TerraLabel extends TerraButton { + +// private final GuiLabel label; + + public TerraLabel(String text) { + super(text); + visible = true; +// label = new GuiLabel( +// Collections.singletonList(text), +// 0xFFFFFF, +// Minecraft.getInstance().fontRenderer +// ); +// label.visible = true; + } + + @Override + public void render(int mouseX, int mouseY, float partialTicks) { + Minecraft minecraft = Minecraft.getInstance(); + FontRenderer fontrenderer = minecraft.fontRenderer; + fontrenderer.drawStringWithShadow(getMessage(), x, y + (height - 8) / 2, 0xFFFFFF); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraSlider.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraSlider.java new file mode 100644 index 0000000..6689422 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/TerraSlider.java @@ -0,0 +1,100 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.element; + +import net.minecraft.nbt.CompoundNBT; +import net.minecraftforge.fml.client.gui.widget.Slider; + +import java.util.List; + +public abstract class TerraSlider extends Slider implements Slider.ISlider, Element { + + private final CompoundNBT value; + private final List tooltip; + + private Runnable callback = () -> {}; + + public TerraSlider(String prefix, CompoundNBT value, boolean decimal) { + super(0, 0, 100, 20, prefix, "", value.getFloat("#min"), value.getFloat("#max"), 0F, decimal, true, b -> {}); + this.value = value; + this.parent = this; + this.tooltip = Element.readTooltip(value); + } + + public TerraSlider callback(Runnable callback) { + this.callback = callback; + return this; + } + + @Override + public List getTooltip() { + return tooltip; + } + + @Override + public void onChangeSliderValue(Slider slider) { + onChange(slider, value); + } + + @Override + public void onRelease(double mouseX, double mouseY) { + super.onRelease(mouseX, mouseY); + callback.run(); + } + + protected abstract void onChange(Slider slider, CompoundNBT value); + + public static class Int extends TerraSlider { + + public Int(String prefix, CompoundNBT value) { + super(prefix, value, false); + setValue(value.getInt("value")); + updateSlider(); + } + + @Override + protected void onChange(Slider slider, CompoundNBT value) { + value.putInt("value", slider.getValueInt()); + } + } + + public static class Float extends TerraSlider { + + public Float(String prefix, CompoundNBT value) { + super(prefix, value, true); + precision = 4; + setValue(value.getFloat("value")); + updateSlider(); + } + + @Override + protected void onChange(Slider slider, CompoundNBT value) { + int i = (int) (slider.getValue() * 1000); + float f = i / 1000F; + value.putFloat("value", f); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/Toggle.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/Toggle.java new file mode 100644 index 0000000..2dd2163 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/element/Toggle.java @@ -0,0 +1,86 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.element; + +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraftforge.common.util.Constants; + +public class Toggle extends TerraButton { + + private final String prefix; + private final CompoundNBT value; + private final ListNBT options; + + private int index; + private Runnable callback = () -> {}; + + public Toggle(String prefix, CompoundNBT value) { + super(value.getString("value")); + this.value = value; + this.prefix = prefix; + this.options = value.getList("#options", Constants.NBT.TAG_STRING); + for (int i = 0; i < options.size(); i++) { + String s = options.getString(i); + if (s.equals(value.getString("value"))) { + index = i; + break; + } + } + setMessage(prefix + value.getString("value")); + } + + public Toggle callback(Runnable runnable) { + this.callback = runnable; + return this; + } + + @Override + public boolean mouseClicked(double mx, double my, int button) { + if (super.isValidClickButton(button)) { + int direction = button == 0 ? 1 : -1; + this.playDownSound(Minecraft.getInstance().getSoundHandler()); + this.onClick(mx, my, direction); + return true; + } + return false; + } + + private void onClick(double mouseX, double mouseY, int direction) { + super.onClick(mouseX, mouseY); + index += direction; + if (index >= options.size()) { + index = 0; + } else if (index < 0) { + index = options.size() - 1; + } + String option = options.getString(index); + value.putString("value", option); + setMessage(prefix + option); + callback.run(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/BasePage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/BasePage.java new file mode 100644 index 0000000..e802b99 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/BasePage.java @@ -0,0 +1,33 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.page; + +public abstract class BasePage extends Page { + + public BasePage() { + super(4, 0, 0.7F, 0.3F); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/FeaturePage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/FeaturePage.java new file mode 100644 index 0000000..dfa0ead --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/FeaturePage.java @@ -0,0 +1,58 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.page; + +import com.terraforged.mod.gui.OverlayScreen; +import com.terraforged.mod.settings.TerraSettings; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.nbt.CompoundNBT; + +public class FeaturePage extends BasePage { + + private final TerraSettings settings; + private final CompoundNBT featureSettings; + + public FeaturePage(TerraSettings settings) { + this.settings = settings; + this.featureSettings = NBTHelper.serialize(settings.features); + } + + @Override + public String getTitle() { + return "Feature Settings"; + } + + @Override + public void save() { + NBTHelper.deserialize(featureSettings, settings.features); + } + + @Override + public void init(OverlayScreen parent) { + Column left = getColumn(0); + addElements(left.left, left.top, left, featureSettings, false, left.scrollPane::addButton, NO_CALLBACK); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/FilterPage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/FilterPage.java new file mode 100644 index 0000000..25cd306 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/FilterPage.java @@ -0,0 +1,65 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.page; + +import com.terraforged.core.settings.Settings; +import com.terraforged.mod.gui.OverlayScreen; +import com.terraforged.mod.gui.preview.PreviewPage; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.nbt.CompoundNBT; + +public class FilterPage extends BasePage { + + private final Settings settings; + private final PreviewPage preview; + private final CompoundNBT filterSettings; + + public FilterPage(Settings settings, PreviewPage preview) { + this.settings = settings; + this.preview = preview; + this.filterSettings = NBTHelper.serialize(settings.filters); + } + + @Override + public String getTitle() { + return "Filter Settings"; + } + + @Override + public void save() { + NBTHelper.deserialize(filterSettings, settings.filters); + } + + @Override + public void init(OverlayScreen parent) { + Column column = getColumn(0); + addElements(0, 0, column, filterSettings, true, column.scrollPane::addButton, this::update); + } + + private void update() { + preview.apply(settings -> NBTHelper.deserialize(filterSettings, settings.filters)); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/GeneratorPage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/GeneratorPage.java new file mode 100644 index 0000000..5308f50 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/GeneratorPage.java @@ -0,0 +1,65 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.page; + +import com.terraforged.core.settings.Settings; +import com.terraforged.mod.gui.OverlayScreen; +import com.terraforged.mod.gui.preview.PreviewPage; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.nbt.CompoundNBT; + +public class GeneratorPage extends BasePage { + + private final Settings settings; + private final PreviewPage preview; + private final CompoundNBT generatorSettings; + + public GeneratorPage(Settings settings, PreviewPage preview) { + this.settings = settings; + this.preview = preview; + this.generatorSettings = NBTHelper.serialize(settings.generator); + } + + @Override + public String getTitle() { + return "Generator Settings"; + } + + @Override + public void save() { + NBTHelper.deserialize(generatorSettings, settings.generator); + } + + @Override + public void init(OverlayScreen parent) { + Column left = getColumn(0); + addElements(left.left, left.top, left, generatorSettings, true, left.scrollPane::addButton, this::update); + } + + private void update() { + preview.apply(settings -> NBTHelper.deserialize(generatorSettings, settings.generator)); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/Page.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/Page.java new file mode 100644 index 0000000..68c365e --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/Page.java @@ -0,0 +1,201 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.page; + +import com.terraforged.mod.gui.OverlayRenderer; +import com.terraforged.mod.gui.OverlayScreen; +import com.terraforged.mod.gui.ScrollPane; +import com.terraforged.mod.gui.element.TerraButton; +import com.terraforged.mod.gui.element.TerraLabel; +import com.terraforged.mod.gui.element.TerraSlider; +import com.terraforged.mod.gui.element.Toggle; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.client.gui.IGuiEventListener; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.Widget; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.INBT; +import net.minecraftforge.common.util.Constants; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Function; + +public abstract class Page implements IGuiEventListener, OverlayRenderer { + + protected static final Runnable NO_CALLBACK = () -> {}; + + private static final int SLIDER_HEIGHT = 20; + private static final int SLIDER_PAD = 2; + + private final Column[] columns; + private final float[] sizes; + private final int hpad; + private final int vpad; + protected OverlayScreen parent; + + public Page(int hpad, int vpad, float... columnSizes) { + this.hpad = hpad; + this.vpad = vpad; + this.sizes = columnSizes; + this.columns = new Column[columnSizes.length]; + } + + public abstract void save(); + + public abstract void init(OverlayScreen parent); + + @Override + public void renderOverlays(Screen screen, int mouseX, int mouseY) { + for (Column column : columns) { + if (column.scrollPane.children().isEmpty()) { + continue; + } + column.scrollPane.renderOverlays(screen, mouseX, mouseY); + } + } + + public void visit(Consumer consumer) { + for (Column column : columns) { + if (column.scrollPane.children().isEmpty()) { + continue; + } + consumer.accept(column.scrollPane); + } + } + + public boolean action(Function action) { + boolean result = false; + for (Column column : columns) { + if (column.scrollPane.children().isEmpty()) { + continue; + } + boolean b = action.apply(column.scrollPane); + result = b || result; + } + return result; + } + + public void close() { + + } + + public String getTitle() { + return ""; + } + + public Column getColumn(int index) { + return columns[index]; + } + + public final void initPage(int marginH, int marginV, OverlayScreen parent) { + this.parent = parent; + int top = marginV; + int left = marginH; + int pageWidth = parent.width - (marginH * 2); + int pageHeight = parent.height; + for (int i = 0; i < columns.length; i++) { + int columnWidth = Math.round(sizes[i] * pageWidth) - (2 * hpad); + Column column = new Column(left, top, columnWidth, pageHeight, hpad, vpad); + columns[i] = column; + left += columnWidth + (2 * hpad); + } + init(parent); + } + + public void addElements(int x, int y, Column column, CompoundNBT settings, Consumer consumer, Runnable callback) { + addElements(x, y, column, settings, false, consumer, callback); + } + + public void addElements(int x, int y, Column column, CompoundNBT settings, boolean deep, Consumer consumer, Runnable callback) { + AtomicInteger top = new AtomicInteger(y); + + NBTHelper.stream(settings).forEach(value -> { + String name = value.getString("#display"); + Widget button = createButton(name, value, callback); + if (button != null) { + button.setWidth(column.width); + button.setHeight(SLIDER_HEIGHT); + button.x = x; + button.y = top.getAndAdd(SLIDER_HEIGHT + SLIDER_PAD); + consumer.accept(button); + } else if (deep) { + INBT child = value.get("value"); + if (child == null || child.getId() != Constants.NBT.TAG_COMPOUND) { + return; + } + TerraLabel label = new TerraLabel(name); + label.x = x; + label.y = top.getAndAdd(SLIDER_HEIGHT + SLIDER_PAD); + consumer.accept(label); + addElements(x, label.y, column, (CompoundNBT) child, consumer, callback); + } + }); + } + + public Widget createButton(String name, CompoundNBT value, Runnable callback) { + INBT tag = value.get("value"); + if (tag == null) { + return null; + } + + byte type = tag.getId(); + if (type == Constants.NBT.TAG_INT) { + return new TerraSlider.Int(name + ": ", value).callback(callback); + } else if (type == Constants.NBT.TAG_FLOAT) { + return new TerraSlider.Float(name + ": ", value).callback(callback); + } else if (type == Constants.NBT.TAG_STRING && value.contains("#options")) { + return new Toggle(name + ": ", value).callback(callback); + } else if (type == Constants.NBT.TAG_STRING) { + return new TerraButton(name); + } else { + return null; + } + } + + public static class Column { + + public final int left; + public final int right; + public final int top; + public final int bottom; + public final int width; + public final int height; + public final ScrollPane scrollPane; + + private Column(int left, int top, int width, int height, int vpad, int hpad) { + this.left = left + vpad; + this.right = left + width - vpad; + this.top = top + hpad; + this.bottom = height - hpad; + this.width = width; + this.height = height; + this.scrollPane = new ScrollPane(22); + this.scrollPane.updateSize(width, height, 30, height - 30); + this.scrollPane.setLeftPos(this.left); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/RiverPage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/RiverPage.java new file mode 100644 index 0000000..484093b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/RiverPage.java @@ -0,0 +1,66 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.page; + +import com.terraforged.core.settings.Settings; +import com.terraforged.mod.gui.OverlayScreen; +import com.terraforged.mod.gui.preview.PreviewPage; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.nbt.CompoundNBT; + +public class RiverPage extends BasePage { + + private final Settings settings; + private final PreviewPage preview; + private final CompoundNBT riverSettings; + + public RiverPage(Settings settings, PreviewPage preview) { + this.settings = settings; + this.preview = preview; + this.riverSettings = NBTHelper.serialize(settings.rivers); + } + + @Override + public String getTitle() { + return "River Settings"; + } + + @Override + public void save() { + NBTHelper.deserialize(riverSettings, settings.rivers); + } + + @Override + public void init(OverlayScreen parent) { + Column center = getColumn(0); + center.scrollPane.setScrollAmount(0D); + addElements(0, 0, center, riverSettings, true, center.scrollPane::addButton, this::update); + } + + private void update() { + preview.apply(settings -> NBTHelper.deserialize(riverSettings, settings.rivers)); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/StructurePage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/StructurePage.java new file mode 100644 index 0000000..ed8a522 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/StructurePage.java @@ -0,0 +1,58 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.page; + +import com.terraforged.mod.gui.OverlayScreen; +import com.terraforged.mod.settings.TerraSettings; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.nbt.CompoundNBT; + +public class StructurePage extends BasePage { + + private final TerraSettings settings; + private final CompoundNBT structureSettings; + + public StructurePage(TerraSettings settings) { + this.settings = settings; + this.structureSettings = NBTHelper.serialize(settings.structures); + } + + @Override + public String getTitle() { + return "Structure Settings"; + } + + @Override + public void save() { + NBTHelper.deserialize(structureSettings, settings.structures); + } + + @Override + public void init(OverlayScreen parent) { + Column left = getColumn(0); + addElements(left.left, left.top, left, structureSettings, false, left.scrollPane::addButton, NO_CALLBACK); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/TerrainPage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/TerrainPage.java new file mode 100644 index 0000000..a5514ce --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/page/TerrainPage.java @@ -0,0 +1,66 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.page; + +import com.terraforged.core.settings.Settings; +import com.terraforged.mod.gui.OverlayScreen; +import com.terraforged.mod.gui.preview.PreviewPage; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.nbt.CompoundNBT; + +public class TerrainPage extends BasePage { + + private final Settings settings; + private final PreviewPage preview; + private final CompoundNBT terrainSettings; + + public TerrainPage(Settings settings, PreviewPage preview) { + this.settings = settings; + this.preview = preview; + this.terrainSettings = NBTHelper.serialize(settings.terrain); + } + + @Override + public String getTitle() { + return "Terrain Settings"; + } + + @Override + public void save() { + NBTHelper.deserialize(terrainSettings, settings.terrain); + } + + @Override + public void init(OverlayScreen parent) { + Column center = getColumn(0); + center.scrollPane.setScrollAmount(0D); + addElements(0, 0, center, terrainSettings, true, center.scrollPane::addButton, this::update); + } + + private void update() { + preview.apply(settings -> NBTHelper.deserialize(terrainSettings, settings.terrain)); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java new file mode 100644 index 0000000..29599f8 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java @@ -0,0 +1,259 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.preview; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.terraforged.core.cell.Cell; +import com.terraforged.core.region.Region; +import com.terraforged.core.region.RegionGenerator; +import com.terraforged.core.settings.Settings; +import com.terraforged.core.util.concurrent.ThreadPool; +import com.terraforged.core.world.GeneratorContext; +import com.terraforged.core.world.WorldGeneratorFactory; +import com.terraforged.core.world.terrain.Terrain; +import com.terraforged.core.world.terrain.Terrains; +import com.terraforged.mod.util.nbt.NBTHelper; +import me.dags.noise.util.NoiseUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.AbstractGui; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.widget.button.Button; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.client.renderer.texture.NativeImage; +import net.minecraft.nbt.CompoundNBT; + +import java.awt.*; +import java.util.Random; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class Preview extends Button { + + private static final int FACTOR = 4; + private static final int BLOCK_SIZE = 256;//Size.chunkToBlock(1 << FACTOR); + private static final float[] LEGEND_SCALES = {1, 0.9F, 0.75F, 0.6F}; + + private final int offsetX; + private final int offsetZ; + private final Random random = new Random(System.currentTimeMillis()); + private final PreviewSettings previewSettings = new PreviewSettings(); + private final DynamicTexture texture = new DynamicTexture(new NativeImage(BLOCK_SIZE, BLOCK_SIZE, true)); + + private int seed; + private long lastUpdate = 0L; + private Settings settings = new Settings(); + private Future task = null; + private Region region = null; + + private String[] labels = {"Area: ", "Terrain: ", "Biome: "}; + private String[] values = {"", "", ""}; + + public Preview() { + super(0, 0, 0, 0, "", b -> {}); + this.seed = random.nextInt(); + this.offsetX = random.nextInt(50000) - 25000; + this.offsetZ = random.nextInt(50000) - 25000; + } + + public void regenerate() { + this.seed = random.nextInt(); + } + + public void close() { + texture.close(); + } + + @Override + public void render(int mx, int my, float partialTicks) { + preRender(); + + texture.bindTexture(); + RenderSystem.enableBlend(); + RenderSystem.enableRescaleNormal(); + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + + AbstractGui.blit(x, y, 0, 0, width, height, width, height); + RenderSystem.disableRescaleNormal(); + + updateLegend(mx, my); + renderLegend(labels, values, x + 1, y + height + 2, 15, 0xFFFFFF); + } + + public void update(Settings settings, CompoundNBT prevSettings) { + long time = System.currentTimeMillis(); + if (time - lastUpdate < 50) { + return; + } + lastUpdate = time; + + NBTHelper.deserialize(prevSettings, previewSettings); + settings.generator.seed = seed; + + task = generate(settings, prevSettings); + } + + private void preRender() { + if (task != null && task.isDone()) { + try { + region = task.get(); + render(region); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.getCause().printStackTrace(); + } finally { + task = null; + } + } + } + + private void render(Region region) { + NativeImage image = texture.getTextureData(); + if (image == null) { + return; + } + + RenderMode renderer = previewSettings.mode; + Terrains terrains = Terrains.create(settings); + GeneratorContext context = new GeneratorContext(terrains, settings); + + int stroke = 2; + int width = region.getBlockSize().size; + region.iterate((cell, x, z) -> { + if (x < stroke || z < stroke || x >= width - stroke || z >= width - stroke) { + image.setPixelRGBA(x, z, Color.black.getRGB()); + } else { + Color color = renderer.color(cell, context); + image.setPixelRGBA(x, z, RenderMode.rgba(color)); + } + }); + + texture.updateDynamicTexture(); + } + + private Future generate(Settings settings, CompoundNBT prevSettings) { + NBTHelper.deserialize(prevSettings, previewSettings); + settings.generator.seed = seed; + this.settings = settings; + + GeneratorContext context = new GeneratorContext(Terrains.create(settings), settings); + + RegionGenerator renderer = RegionGenerator.builder() + .factory(new WorldGeneratorFactory(context)) + .pool(ThreadPool.getCommon()) + .size(FACTOR, 0) + .build(); + + return renderer.generate(offsetX, offsetZ, 101 - previewSettings.zoom, false); + } + + private void updateLegend(int mx ,int my) { + if (region != null) { + int zoom = (101 - previewSettings.zoom); + int width = Math.max(1, region.getBlockSize().size * zoom); + int height = Math.max(1, region.getBlockSize().size * zoom); + values[0] = width + "x" + height; + + if (mx >= this.x && mx <= this.x + this.width && my >= this.y && my <= this.y + this.height) { + float fx = (mx - this.x) / (float) this.width; + float fz = (my - this.y) / (float) this.height; + int ix = NoiseUtil.round(fx * region.getBlockSize().size); + int iz = NoiseUtil.round(fz * region.getBlockSize().size); + Cell cell = region.getCell(ix, iz); + values[1] = getTerrainName(cell); + values[2] = getBiomeName(cell); + } + } + } + + private float getLegendScale() { + int index = Minecraft.getInstance().gameSettings.guiScale - 1; + if (index < 0 || index >= LEGEND_SCALES.length) { + // index=-1 == GuiScale(AUTO) which is the same as GuiScale(4) + // values above 4 don't exist but who knows what mods might try set it to + // in both cases use the smallest acceptable scale + index = LEGEND_SCALES.length - 1; + } + return LEGEND_SCALES[index]; + } + + private void renderLegend(String[] labels, String[] values, int left, int top, int lineHeight, int color) { + float scale = getLegendScale(); + lineHeight = Math.round(lineHeight * scale); + + RenderSystem.pushMatrix(); + RenderSystem.translatef(left, top, 0); + RenderSystem.scalef(scale, scale, 1); + + FontRenderer renderer = Minecraft.getInstance().fontRenderer; + int spacing = 0; + for (String s : labels) { + spacing = Math.max(spacing, renderer.getStringWidth(s)); + } + + int maxX = this.x + this.width; + for (int i = 0; i < labels.length && i < values.length; i++) { + String label = labels[i]; + String value = values[i]; + + while (left + spacing + Minecraft.getInstance().fontRenderer.getStringWidth(value) > maxX) { + value = value.substring(0, value.length() - 1); + } + + drawString(renderer, label, 0, i * lineHeight, color); + drawString(renderer, value, spacing, i * lineHeight, color); + } + + RenderSystem.popMatrix(); + } + + private static String getTerrainName(Cell cell) { + String terrain = cell.tag.getName().toLowerCase(); + if (terrain.contains("river")) { + return "river"; + } + return terrain; + } + + private static String getBiomeName(Cell cell) { + String terrain = cell.tag.getName().toLowerCase(); + if (terrain.contains("ocean")) { + if (cell.temperature < 0.3) { + return "cold_ocean"; + } + if (cell.temperature > 0.6) { + return "warm_ocean"; + } + return "ocean"; + } + if (terrain.contains("river")) { + return "river"; + } + return cell.biomeType.name().toLowerCase(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/PreviewPage.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/PreviewPage.java new file mode 100644 index 0000000..b63103d --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/PreviewPage.java @@ -0,0 +1,100 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.preview; + +import com.terraforged.core.settings.Settings; +import com.terraforged.mod.gui.OverlayScreen; +import com.terraforged.mod.gui.element.TerraButton; +import com.terraforged.mod.gui.page.BasePage; +import com.terraforged.mod.util.nbt.NBTHelper; +import net.minecraft.nbt.CompoundNBT; + +import java.util.function.Consumer; + +public class PreviewPage extends BasePage { + + private final Preview preview = new Preview(); + private final Settings settings = new Settings(); + private final CompoundNBT previewerSettings = NBTHelper.serialize(new PreviewSettings()); + + public PreviewPage() { + + } + + public void apply(Consumer consumer) { + consumer.accept(settings); + preview.update(settings, previewerSettings); + } + + @Override + public void close() { + preview.close(); + } + + @Override + public void save() { + + } + + @Override + public void init(OverlayScreen parent) { + Column right = getColumn(1); + preview.x = 0; + preview.y = 0; + preview.setWidth(256); + preview.setHeight(256); + + addElements(right.left, right.top, right, previewerSettings, right.scrollPane::addButton, this::update); + right.scrollPane.addButton(new TerraButton("Generate") { + @Override + public void onPress() { + preview.regenerate(); + update(); + } + }); + + right.scrollPane.addButton(preview); + + // used to pad the scroll-pane out so that the preview legend scrolls on larger gui scales + TerraButton spacer = createSpacer(); + for (int i = 0; i < 6; i++) { + right.scrollPane.addButton(spacer); + } + + update(); + } + + private void update() { + preview.update(settings, previewerSettings); + } + + private static TerraButton createSpacer() { + return new TerraButton("") { + @Override + public void render(int x, int y, float tick) { } + }; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/PreviewSettings.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/PreviewSettings.java new file mode 100644 index 0000000..661c80c --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/PreviewSettings.java @@ -0,0 +1,36 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.preview; + +import com.terraforged.core.util.serialization.annotation.Range; + +public class PreviewSettings { + + @Range(min = 1, max = 100) + public int zoom = 100 - 32; + + public RenderMode mode = RenderMode.BIOME_TYPE; +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/RenderMode.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/RenderMode.java new file mode 100644 index 0000000..3bb8caa --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/RenderMode.java @@ -0,0 +1,83 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.gui.preview; + +import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.GeneratorContext; +import com.terraforged.core.world.heightmap.Levels; +import com.terraforged.core.world.terrain.Terrain; +import me.dags.noise.util.NoiseUtil; + +import java.awt.*; + +public enum RenderMode { + BIOME_TYPE, + TEMPERATURE, + MOISTURE, + BIOME_SHAPE, + ; + + public Color color(Cell cell, GeneratorContext context) { + float baseHeight = Levels.getSeaLevel(context.settings.generator); + if (cell.value < baseHeight) { + return new Color(40, 140, 200); + } + + float bands = 10F; + float alpha = 0.15F; + float elevation = (cell.value - baseHeight) / (1F - baseHeight); + + int band = NoiseUtil.round(elevation * bands); + float scale = 1F - alpha; + float bias = alpha * (band / bands); + + float saturation = 0.7F; + float brightness = 0.8F; + + switch (this) { + case BIOME_SHAPE: + return Color.getHSBColor(cell.biome, saturation, brightness); + case BIOME_TYPE: + Color color = cell.biomeType.getColor(); + float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), new float[3]); + return Color.getHSBColor(hsb[0], hsb[1], (hsb[2] * scale) + bias); + case MOISTURE: + return Color.getHSBColor(step(1 - cell.moisture, 8) * 0.65F, saturation, brightness); + case TEMPERATURE: + return Color.getHSBColor(step(cell.temperature, 8) * 0.65F, saturation, brightness); + default: + return Color.black; + } + } + + private static float step(float value, int steps) { + return ((float) NoiseUtil.round(value * steps)) / steps; + } + + public static int rgba(Color color) { + return color.getRed() + (color.getGreen() << 8) + (color.getBlue() << 16) + (255 << 24); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/material/MaterialHelper.java b/TerraForgedMod/src/main/java/com/terraforged/mod/material/MaterialHelper.java new file mode 100644 index 0000000..fdc1360 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/material/MaterialHelper.java @@ -0,0 +1,151 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.material; + +import com.google.common.collect.Sets; +import com.terraforged.core.util.concurrent.ObjectPool; +import com.terraforged.mod.util.DummyBlockReader; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ConcretePowderBlock; +import net.minecraft.block.material.Material; +import net.minecraft.tags.BlockTags; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.DecoratedFeatureConfig; +import net.minecraft.world.gen.feature.OreFeatureConfig; +import net.minecraftforge.common.Tags; +import net.minecraftforge.registries.IForgeRegistryEntry; + +import java.util.Set; + +public class MaterialHelper { + + private static final Set BLACKLIST = Sets.newHashSet( + Blocks.INFESTED_CHISELED_STONE_BRICKS, + Blocks.INFESTED_COBBLESTONE, + Blocks.INFESTED_CRACKED_STONE_BRICKS, + Blocks.INFESTED_MOSSY_STONE_BRICKS, + Blocks.INFESTED_STONE, + Blocks.INFESTED_STONE_BRICKS, + Blocks.SLIME_BLOCK, + Blocks.RED_SAND, + Blocks.SOUL_SAND, + // honey etc + Blocks.HONEY_BLOCK, + Blocks.HONEYCOMB_BLOCK, + Blocks.BEE_NEST, + Blocks.BEEHIVE, + Blocks.COMPOSTER + ); + + public static boolean isAir(Block block) { + return block == Blocks.AIR || block == Blocks.CAVE_AIR || block == Blocks.VOID_AIR; + } + + public static boolean isGrass(Block block) { + return block == Blocks.GRASS_BLOCK || block == Blocks.MYCELIUM; + } + + public static boolean isStone(Block block) { + return Tags.Blocks.STONE.contains(block) + && !isBlacklisted(block) + && !("" + block.getRegistryName()).contains("polished_"); + } + + public static boolean isDirt(Block block) { + return Tags.Blocks.DIRT.contains(block) + && !isBlacklisted(block); + } + + public static boolean isClay(Block block) { + return block.getDefaultState().getMaterial() == Material.CLAY + && !isBlacklisted(block); + } + + public static boolean isSand(Block block) { + return BlockTags.SAND.contains(block) + && !isBlacklisted(block) + && !(block instanceof ConcretePowderBlock); + } + + public static boolean isSediment(Block block) { + return (isSand(block) || isGravel(block)) + && !isBlacklisted(block) + && !(block instanceof ConcretePowderBlock); + } + + public static boolean isGravel(Block block) { + return getName(block).contains("gravel"); + } + + public static boolean isOre(Block block) { + return Tags.Blocks.ORES.contains(block) + && !isBlacklisted(block); + } + + public static boolean isBlacklisted(Block block) { + return BLACKLIST.contains(block); + } + + public static String getName(IForgeRegistryEntry entry) { + return "" + entry.getRegistryName(); + } + + public static String getNamespace(IForgeRegistryEntry entry) { + ResourceLocation name = entry.getRegistryName(); + if (name == null) { + return "unknown"; + } + return name.getNamespace(); + } + + public static float getHardness(BlockState state) { + try (ObjectPool.Item reader = DummyBlockReader.pooled()) { + reader.getValue().set(state); + return state.getBlockHardness(reader.getValue(), BlockPos.ZERO); + } + } + + public static boolean isCube(BlockState state) { + try (ObjectPool.Item reader = DummyBlockReader.pooled()) { + reader.getValue().set(state); + return state.isNormalCube(reader.getValue(), BlockPos.ZERO); + } + } + + public static OreFeatureConfig getOreConfig(ConfiguredFeature feature) { + if (feature.config instanceof DecoratedFeatureConfig) { + DecoratedFeatureConfig config = (DecoratedFeatureConfig) feature.config; + if (config.feature.config instanceof OreFeatureConfig) { + return (OreFeatureConfig) config.feature.config; + } + } + return null; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/material/Materials.java b/TerraForgedMod/src/main/java/com/terraforged/mod/material/Materials.java new file mode 100644 index 0000000..aa42ad1 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/material/Materials.java @@ -0,0 +1,163 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.material; + +import com.terraforged.api.material.MaterialTags; +import com.terraforged.api.material.layer.LayerManager; +import com.terraforged.api.material.state.States; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.tags.Tag; +import net.minecraftforge.registries.ForgeRegistries; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Predicate; + +public class Materials { + + private final Set stone = create(MaterialTags.WG_ROCK); + private final Set dirt = create(MaterialTags.WG_EARTH); + private final Set clay = create(MaterialTags.WG_CLAY); + private final Set sediment = create(MaterialTags.WG_SEDIMENT); + private final Set ore = create(MaterialTags.WG_ORE); + private final Set erodible = create(MaterialTags.WG_ERODIBLE); + private final LayerManager layerManager = new LayerManager(); + + public Materials() { + Predicate filter = getTagFilter(); + for (Block block : ForgeRegistries.BLOCKS) { + if (filter.test(block)) { + continue; + } + + if (!MaterialHelper.isCube(block.getDefaultState())) { + continue; + } + + if (MaterialHelper.isStone(block)) { + stone.add(block); + } else if (MaterialHelper.isDirt(block)) { + dirt.add(block); + } else if (MaterialHelper.isClay(block)) { + clay.add(block); + } else if (MaterialHelper.isSediment(block)) { + sediment.add(block); + } else if (MaterialHelper.isOre(block)) { + ore.add(block); + } + } + + if (stone.isEmpty()) { + stone.add(Blocks.STONE); + } + } + + public LayerManager getLayerManager() { + return layerManager; + } + + public boolean isStone(Block block) { + return stone.contains(block); + } + + public boolean isEarth(Block block) { + return dirt.contains(block); + } + + public boolean isClay(Block block) { + return clay.contains(block); + } + + public boolean isSediment(Block block) { + return sediment.contains(block); + } + + public boolean isOre(Block block) { + return ore.contains(block); + } + + public boolean isErodible(Block block) { + return erodible.contains(block); + } + + public Collection getStone() { + if (stone.isEmpty()) { + return Collections.singleton(States.STONE.getBlock()); + } + return Collections.unmodifiableSet(stone); + } + + public Collection getDirt() { + if (dirt.isEmpty()) { + return Collections.singleton(States.DIRT.getBlock()); + } + return Collections.unmodifiableSet(dirt); + } + + public Collection getClay() { + if (clay.isEmpty()) { + return Collections.singleton(States.STONE.getBlock()); + } + return Collections.unmodifiableSet(clay); + } + + public Collection getSediment() { + if (sediment.isEmpty()) { + return Collections.singleton(States.CLAY.getBlock()); + } + return Collections.unmodifiableSet(sediment); + } + + public Collection getOre() { + if (ore.isEmpty()) { + return Collections.singleton(States.STONE.getBlock()); + } + return Collections.unmodifiableSet(ore); + } + + private static Set create(Tag tag) { + return new HashSet<>(tag.getAllElements()); + } + + private static Predicate getTagFilter() { + Set namespaces = new HashSet<>(); + collectNamespace(namespaces, MaterialTags.WG_ROCK.getAllElements()); + collectNamespace(namespaces, MaterialTags.WG_EARTH.getAllElements()); + collectNamespace(namespaces, MaterialTags.WG_EARTH.getAllElements()); + collectNamespace(namespaces, MaterialTags.WG_SEDIMENT.getAllElements()); + collectNamespace(namespaces, MaterialTags.WG_ORE.getAllElements()); + return b -> namespaces.contains(MaterialHelper.getNamespace(b)); + } + + private static void collectNamespace(Set set, Collection blocks) { + for (Block block : blocks) { + set.add(MaterialHelper.getNamespace(block)); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/material/geology/GeoGenerator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/material/geology/GeoGenerator.java new file mode 100644 index 0000000..75a770a --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/material/geology/GeoGenerator.java @@ -0,0 +1,118 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.material.geology; + +import com.terraforged.api.material.geology.StrataConfig; +import com.terraforged.api.material.geology.StrataGenerator; +import com.terraforged.core.world.geology.Strata; +import com.terraforged.mod.material.MaterialHelper; +import com.terraforged.mod.material.Materials; +import me.dags.noise.Source; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Random; + +public class GeoGenerator implements StrataGenerator { + + private final List rock; + private final List soil; + private final List clay; + private final List sediment; + private final List types = new ArrayList<>(); + + public GeoGenerator(Materials materials) { + types.add(Source.PERLIN); + rock = new ArrayList<>(materials.getStone()); + soil = new ArrayList<>(materials.getDirt()); + clay = new ArrayList<>(materials.getClay()); + sediment = new ArrayList<>(materials.getSediment()); + } + + @Override + public Strata generate(int seed, int scale, StrataConfig config) { + Random random = new Random(); + Strata.Builder builder = Strata.builder(++seed, Source.build(++seed, scale, 3)); + addLayer(seed + 1, random, config.soil, soil, builder); + addLayer(seed + 2, random, config.sediment, sediment, builder); + addLayer(seed + 3, random, config.clay, clay, builder); + addLayer(seed + 4, random, config.rock, rock, builder); + return builder.build(); + } + + private void addLayer(int seed, Random random, StrataConfig.Config config, List materials, Strata.Builder builder) { + random.setSeed(seed); + List layers = generateLayers(materials, config, random); + layers.forEach(l -> builder.add(l.type, l.state, l.depth)); + } + + private List generateLayers(List materials, StrataConfig.Config config, Random random) { + int lastIndex = -1; + int layers = config.getLayers(random.nextFloat()); + List result = new ArrayList<>(); + for (int i = 0; i < layers; i++) { + int attempts = 3; + int index = random.nextInt(materials.size()); + while (--attempts >= 0 && index == lastIndex) { + index = random.nextInt(materials.size()); + } + if (index != lastIndex) { + lastIndex = index; + BlockState material = materials.get(index).getDefaultState(); + float depth = config.getDepth(random.nextFloat()); + Source type = nextType(random); + result.add(new Layer(material, depth, type)); + } + } + return result; + } + + private Source nextType(Random random) { + int index = random.nextInt(types.size()); + return types.get(index); + } + + private List sortHardness(List layers) { + layers.sort(Comparator.comparing(s -> MaterialHelper.getHardness(s.state))); + return layers; + } + + private static class Layer { + + private final BlockState state; + private final float depth; + private final Source type; + + private Layer(BlockState state, float depth, Source type) { + this.state = state; + this.depth = depth; + this.type = type; + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/material/geology/GeoManager.java b/TerraForgedMod/src/main/java/com/terraforged/mod/material/geology/GeoManager.java new file mode 100644 index 0000000..2b6634b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/material/geology/GeoManager.java @@ -0,0 +1,116 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.material.geology; + +import com.terraforged.api.material.geology.GeologyManager; +import com.terraforged.api.material.geology.StrataConfig; +import com.terraforged.core.util.Seed; +import com.terraforged.core.world.geology.Geology; +import com.terraforged.core.world.geology.Strata; +import com.terraforged.mod.chunk.TerraContext; +import com.terraforged.mod.material.Materials; +import me.dags.noise.Module; +import me.dags.noise.Source; +import net.minecraft.block.BlockState; +import net.minecraft.world.biome.Biome; + +import java.util.HashMap; +import java.util.Map; + +public class GeoManager implements GeologyManager { + + private final Module selector; + private final Materials materials; + private final GeoGenerator builder; + private final Geology general; + private final Map> specific = new HashMap<>(); + + public GeoManager(TerraContext context) { + int scale = context.settings.generator.land.regionSize / 2; + this.selector = Source.cell(context.seed.next(), scale) + .warp(context.seed.next(), scale / 4, 2, scale / 2D) + .warp(context.seed.next(), 15, 2, 30); + this.builder = new GeoGenerator(context.materials); + this.general = new Geology<>(selector); + this.materials = context.materials; + init(context.seed); + } + + public GeoGenerator getStrataGenerator() { + return builder; + } + + @Override + public void register(Strata strata) { + general.add(strata); + } + + @Override + public void register(Biome biome, Strata strata) { + register(biome, strata, false); + } + + /** + * Register a biome specific strata group. + * If a specific geology doesn't exist for a given biome, the global geology can optionally be added using + * the 'inheritGlobal' flag. + */ + public void register(Biome biome, Strata strata, boolean inheritGlobal) { + Geology geology = specific.get(biome); + if (geology == null) { + geology = new Geology<>(selector); + specific.put(biome, geology); + if (inheritGlobal) { + geology.add(general); + } + } + geology.add(strata); + } + + /** + * Register/replace a biome-specific geology group + */ + public void register(Biome biome, Geology geology) { + specific.put(biome, geology); + } + + public Geology getGeology(Biome biome) { + return specific.getOrDefault(biome, general); + } + + public Strata getStrata(Biome biome, float value) { + return getGeology(biome).getStrata(value); + } + + private void init(Seed seed) { + StrataConfig config = new StrataConfig(); + GeoGenerator generator = new GeoGenerator(materials); + for (int i = 0; i < 10; i++) { + Strata strata = generator.generate(seed.next(), 128, config); + general.add(strata); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/settings/FeatureSettings.java b/TerraForgedMod/src/main/java/com/terraforged/mod/settings/FeatureSettings.java new file mode 100644 index 0000000..e623ae5 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/settings/FeatureSettings.java @@ -0,0 +1,48 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.settings; + +import com.terraforged.core.util.serialization.annotation.Comment; +import com.terraforged.core.util.serialization.annotation.Serializable; + +@Serializable +public class FeatureSettings { + + @Comment("Modifies layer block levels (ie snow) to fit the terrain") + public boolean smoothLayerDecorator = true; + + @Comment("Generates strata (rock layers) instead of just stone") + public boolean strataDecorator = true; + + @Comment("Replace surface materials where erosion has occurred") + public boolean erosionDecorator = true; + + @Comment("Removes snow from the terrain where it shouldn't naturally settle") + public boolean naturalSnowDecorator = true; + + @Comment("Use custom biome features in place of vanilla ones (such as trees)") + public boolean customBiomeFeatures = true; +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/settings/StructureSettings.java b/TerraForgedMod/src/main/java/com/terraforged/mod/settings/StructureSettings.java new file mode 100644 index 0000000..cc77d11 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/settings/StructureSettings.java @@ -0,0 +1,58 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.settings; + +import com.terraforged.core.util.serialization.annotation.Comment; +import com.terraforged.core.util.serialization.annotation.Range; +import com.terraforged.core.util.serialization.annotation.Serializable; + +@Serializable +public class StructureSettings { + + @Range(min = 1, max = 10) + @Comment("Controls the distance between villages") + public int villageDistance = 4; + + @Range(min = 1, max = 10) + @Comment("Controls the distance between mansions") + public int mansionDistance = 4; + + @Range(min = 1, max = 10) + @Comment("Controls the distance between strongholds") + public int strongholdDistance = 4; + + @Range(min = 1, max = 10) + @Comment("Controls the distance between biome structures") + public int biomeStructureDistance = 2; + + @Range(min = 1, max = 10) + @Comment("Controls the distance between ocean monuments") + public int oceanMonumentSpacing = 4; + + @Range(min = 1, max = 10) + @Comment("Controls the separation between ocean monuments") + public int oceanMonumentSeparation = 4; +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/settings/TerraSettings.java b/TerraForgedMod/src/main/java/com/terraforged/mod/settings/TerraSettings.java new file mode 100644 index 0000000..1dc1b42 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/settings/TerraSettings.java @@ -0,0 +1,37 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.settings; + +import com.terraforged.core.settings.Settings; +import com.terraforged.core.util.serialization.annotation.Serializable; + +@Serializable +public class TerraSettings extends Settings { + + public FeatureSettings features = new FeatureSettings(); + + public StructureSettings structures = new StructureSettings(); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/DummyBlockReader.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/DummyBlockReader.java new file mode 100644 index 0000000..00254c4 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/DummyBlockReader.java @@ -0,0 +1,80 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util; + +import com.terraforged.core.util.concurrent.ObjectPool; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.fluid.IFluidState; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockReader; + +import javax.annotation.Nullable; + +public class DummyBlockReader implements IBlockReader { + + private static final ObjectPool pool = new ObjectPool<>(10, DummyBlockReader::new); + + private BlockState state; + private IFluidState fluid; + + public DummyBlockReader set(BlockState state) { + return set(state, Fluids.EMPTY.getDefaultState()); + } + + public DummyBlockReader set(FluidState fluid) { + return set(Blocks.AIR.getDefaultState(), fluid); + } + + public DummyBlockReader set(BlockState state, IFluidState fluid) { + this.state = state; + this.fluid = fluid; + return this; + } + + @Nullable + @Override + public TileEntity getTileEntity(BlockPos pos) { + return null; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return state; + } + + @Override + public IFluidState getFluidState(BlockPos pos) { + return fluid; + } + + public static ObjectPool.Item pooled() { + return pool.get(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/Environment.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/Environment.java new file mode 100644 index 0000000..d539ac0 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/Environment.java @@ -0,0 +1,43 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util; + +import com.terraforged.mod.Log; + +public class Environment { + + private static final boolean dev = System.getProperty("dev") != null; + + public static boolean isDev() { + return dev; + } + + static { + if (dev) { + Log.info("Running in developer mode!"); + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/ListUtils.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/ListUtils.java new file mode 100644 index 0000000..608d5be --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/ListUtils.java @@ -0,0 +1,74 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util; + +import me.dags.noise.util.NoiseUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ListUtils { + + public static T get(List list, float value, T def) { + if (list.isEmpty()) { + return def; + } + return get(list, list.size() - 1, value, def); + } + + public static T get(List list, int maxIndex, float value, T def) { + int index = NoiseUtil.round(value * maxIndex); + if (index < list.size()) { + return list.get(index); + } + return def; + } + + public static List minimize(List list) { + Map counts = count(list); + List result = new ArrayList<>(list.size()); + int min = counts.values().stream().min(Integer::compareTo).orElse(1); + for (T t : list) { + int count = counts.get(t); + int amount = count / min; + for (int i = 0; i < amount; i++) { + result.add(t); + } + } + return result; + } + + public static Map count(List list) { + Map map = new HashMap<>(list.size()); + for (T t : list) { + int count = map.getOrDefault(t, 0); + map.put(t, ++count); + } + return map; + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/annotation/Name.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/annotation/Name.java new file mode 100644 index 0000000..4809ef9 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/annotation/Name.java @@ -0,0 +1,31 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util.annotation; + +public @interface Name { + + String value(); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/annotation/Ref.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/annotation/Ref.java new file mode 100644 index 0000000..b63e01b --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/annotation/Ref.java @@ -0,0 +1,31 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util.annotation; + +public @interface Ref { + + Class[] value(); +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTHelper.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTHelper.java new file mode 100644 index 0000000..3a07f3d --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTHelper.java @@ -0,0 +1,116 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util.nbt; + +import com.google.gson.JsonElement; +import com.mojang.datafixers.Dynamic; +import com.mojang.datafixers.types.JsonOps; +import com.terraforged.core.util.serialization.serializer.Serializer; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.INBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.nbt.NBTDynamicOps; + +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; + +public class NBTHelper { + + public static JsonElement toJson(CompoundNBT tag) { + Dynamic input = new Dynamic<>(NBTDynamicOps.INSTANCE, tag); + Dynamic output = input.convert(JsonOps.INSTANCE); + return output.getValue(); + } + + public static Stream stream(CompoundNBT tag) { + return tag.keySet().stream() + .map(tag::getCompound) + .sorted(Comparator.comparing(t -> t.getInt("#order"))); + + } + + public static CompoundNBT serialize(Object object) { + try { + NBTWriter writer = new NBTWriter(); + writer.readFrom(object); + return writer.compound(); + } catch (IllegalAccessException e) { + return new CompoundNBT(); + } + } + + public static CompoundNBT serializeCompact(Object object) { + try { + NBTWriter writer = new NBTWriter(); + writer.readFrom(object); + return stripMetadata(writer.compound()); + } catch (IllegalAccessException e) { + return new CompoundNBT(); + } + } + + public static CompoundNBT readCompact(Object object) { + try { + NBTWriter writer = new NBTWriter(); + writer.readFrom(object); + new Serializer().serialize(object, writer); + return writer.compound(); + } catch (IllegalAccessException e) { + return new CompoundNBT(); + } + } + + public static T stripMetadata(T tag) { + if (tag instanceof CompoundNBT) { + CompoundNBT compound = (CompoundNBT) tag; + List keys = new LinkedList<>(compound.keySet()); + for (String key : keys) { + if (key.charAt(0) == '#') { + compound.remove(key); + } else { + stripMetadata(compound.get(key)); + } + } + } else if (tag instanceof ListNBT) { + ListNBT list = (ListNBT) tag; + for (int i = 0; i < list.size(); i++) { + stripMetadata(list.get(i)); + } + } + return tag; + } + + public static void deserialize(CompoundNBT settings, Object object) { + try { + NBTReader reader = new NBTReader(settings); + reader.writeTo(object); + } catch (Throwable e) { + e.printStackTrace(); + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTReader.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTReader.java new file mode 100644 index 0000000..2ed5270 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTReader.java @@ -0,0 +1,121 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util.nbt; + +import com.terraforged.core.util.serialization.serializer.Reader; +import net.minecraft.nbt.ByteNBT; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.FloatNBT; +import net.minecraft.nbt.INBT; +import net.minecraft.nbt.IntNBT; +import net.minecraft.nbt.ListNBT; + +import java.util.Collection; + +public class NBTReader implements Reader { + + private final INBT root; + + public NBTReader(INBT root) { + this.root = root; + } + + @Override + public int getSize() { + if (root instanceof CompoundNBT) { + return ((CompoundNBT) root).size(); + } + if (root instanceof ListNBT) { + return ((ListNBT) root).size(); + } + return 1; + } + + @Override + public NBTReader getChild(String key) { + return new NBTReader(((CompoundNBT) root).get(key)); + } + + @Override + public NBTReader getChild(int index) { + return new NBTReader(((ListNBT) root).get(index)); + } + + @Override + public Collection getKeys() { + return ((CompoundNBT) root).keySet(); + } + + @Override + public String getString(String key) { + return ((CompoundNBT) root).getString(key); + } + + @Override + public float getFloat(String key) { + return ((CompoundNBT) root).getFloat(key); + } + + @Override + public int getInt(String key) { + return ((CompoundNBT) root).getInt(key); + } + + @Override + public String getString(int index) { + return ((ListNBT) root).getString(index); + } + + @Override + public float getFloat(int index) { + return ((ListNBT) root).getFloat(index); + } + + @Override + public int getInt(int index) { + return ((ListNBT) root).getInt(index); + } + + @Override + public String getString() { + return root.getString(); + } + + @Override + public boolean getBool() { + return ((ByteNBT) root).getByte() == 1; + } + + @Override + public float getFloat() { + return ((FloatNBT) root).getFloat(); + } + + @Override + public int getInt() { + return ((IntNBT) root).getInt(); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTWriter.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTWriter.java new file mode 100644 index 0000000..a2a0550 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/nbt/NBTWriter.java @@ -0,0 +1,132 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util.nbt; + +import com.terraforged.core.util.serialization.serializer.Writer; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.FloatNBT; +import net.minecraft.nbt.INBT; +import net.minecraft.nbt.IntNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.nbt.StringNBT; + +public class NBTWriter implements Writer { + + private final Context root = new Context(null); + + private String name = ""; + private Context context = root; + + public NBTWriter() { + + } + + public INBT root() { + return root.value; + } + + public CompoundNBT compound() { + return (CompoundNBT) root(); + } + + public ListNBT list() { + return (ListNBT) root(); + } + + private NBTWriter begin(INBT value) { + if (root.value == null) { + root.value = value; + context.value = value; + } else { + append(value); + context = new Context(context); + context.value = value; + } + return this; + } + + private NBTWriter append(INBT value) { + if (context.value instanceof CompoundNBT) { + ((CompoundNBT) context.value).put(name, value); + } else if (context.value instanceof ListNBT) { + ((ListNBT) context.value).add(value); + } + return this; + } + + @Override + public NBTWriter name(String name) { + this.name = name; + return this; + } + + @Override + public NBTWriter beginObject() { + return begin(new CompoundNBT()); + } + + @Override + public NBTWriter endObject() { + context = context.parent; + return this; + } + + @Override + public NBTWriter beginArray() { + return begin(new ListNBT()); + } + + @Override + public NBTWriter endArray() { + context = context.parent; + return this; + } + + @Override + public NBTWriter value(String value) { + return append(StringNBT.valueOf(value)); + } + + @Override + public NBTWriter value(float value) { + return append(FloatNBT.valueOf(value)); + } + + @Override + public NBTWriter value(int value) { + return append(IntNBT.valueOf(value)); + } + + private static class Context { + + private final Context parent; + private INBT value; + + private Context(Context root) { + this.parent = root; + } + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/setup/SetupDebug.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/setup/SetupDebug.java new file mode 100644 index 0000000..f0b73da --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/setup/SetupDebug.java @@ -0,0 +1,69 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util.setup; + +import com.terraforged.api.event.SetupEvent; +import com.terraforged.mod.Log; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE) +public class SetupDebug { + + @SubscribeEvent + public static void terrain(SetupEvent.Terrain event) { + log(event); + } + + @SubscribeEvent + public static void surface(SetupEvent.Surface event) { + log(event); + } + + @SubscribeEvent + public static void layers(SetupEvent.Layers event) { + log(event); + } + + @SubscribeEvent + public static void geology(SetupEvent.Geology event) { + log(event); + } + + @SubscribeEvent + public static void features(SetupEvent.Features event) { + log(event); + } + + @SubscribeEvent + public static void columns(SetupEvent.Decorators event) { + log(event); + } + + private static void log(SetupEvent event) { + Log.debug("Setting up {}", event.getManager().getClass().getSimpleName()); + } +} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/util/setup/SetupHooks.java b/TerraForgedMod/src/main/java/com/terraforged/mod/util/setup/SetupHooks.java new file mode 100644 index 0000000..f533316 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/util/setup/SetupHooks.java @@ -0,0 +1,77 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.mod.util.setup; + +import com.terraforged.api.biome.modifier.ModifierManager; +import com.terraforged.api.chunk.column.ColumnDecorator; +import com.terraforged.api.chunk.column.DecoratorManager; +import com.terraforged.api.chunk.surface.SurfaceManager; +import com.terraforged.api.event.SetupEvent; +import com.terraforged.api.material.geology.GeologyManager; +import com.terraforged.api.material.layer.LayerManager; +import com.terraforged.core.world.GeneratorContext; +import com.terraforged.core.world.terrain.provider.TerrainProvider; +import com.terraforged.feature.modifier.FeatureModifiers; +import net.minecraftforge.common.MinecraftForge; + +import java.util.List; + +public class SetupHooks { + + public static T setup(T provider, GeneratorContext context) { + MinecraftForge.EVENT_BUS.post(new SetupEvent.Terrain(provider, context)); + return provider; + } + + public static T setup(T manager, GeneratorContext context) { + MinecraftForge.EVENT_BUS.post(new SetupEvent.Surface(manager, context)); + return manager; + } + + public static T setup(T manager, GeneratorContext context) { + MinecraftForge.EVENT_BUS.post(new SetupEvent.BiomeModifier(manager, context)); + return manager; + } + + public static T setup(T manager, GeneratorContext context) { + MinecraftForge.EVENT_BUS.post(new SetupEvent.Layers(manager, context)); + return manager; + } + + public static T setup(T manager, GeneratorContext context) { + MinecraftForge.EVENT_BUS.post(new SetupEvent.Geology(manager, context)); + return manager; + } + + public static T setup(T manager, GeneratorContext context) { + MinecraftForge.EVENT_BUS.post(new SetupEvent.Features(manager, context)); + return manager; + } + + public static void setup(List base, List feature, GeneratorContext context) { + MinecraftForge.EVENT_BUS.post(new SetupEvent.Decorators(new DecoratorManager(base, feature), context)); + } +} diff --git a/TerraForgedMod/src/main/resources/META-INF/mods.toml b/TerraForgedMod/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..d0976c2 --- /dev/null +++ b/TerraForgedMod/src/main/resources/META-INF/mods.toml @@ -0,0 +1,12 @@ +modLoader="javafml" +loaderVersion="[31,)" +[[mods]] +modId="terraforged" +displayName="TerraForged" +version="${version}" +authors="dags" +credits="dags" +logoFile="terraforged.png" +displayURL="https://terraforged.com" +issueTrackerURL="https://github.com/TerraForged/TerraForged/issues" +description="A 'terrain first' world generator" \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/assets/terraforged/lang/en_us.json b/TerraForgedMod/src/main/resources/assets/terraforged/lang/en_us.json new file mode 100644 index 0000000..075525d --- /dev/null +++ b/TerraForgedMod/src/main/resources/assets/terraforged/lang/en_us.json @@ -0,0 +1,4 @@ +{ + "generator.terraforged": "TerraForged", + "generator.terratest": "TerraTest" +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/biomes.txt b/TerraForgedMod/src/main/resources/biomes.txt new file mode 100644 index 0000000..8abd632 --- /dev/null +++ b/TerraForgedMod/src/main/resources/biomes.txt @@ -0,0 +1,13 @@ +#TerraForged BiomeType Hex Colors (do not include hash/pound character) +#Fri Jan 10 23:15:10 GMT 2020 +ALPINE=4b7835 +TAIGA=4b7835 +TEMPERATE_RAINFOREST=3c602b +TUNDRA=ba9d47 +TROPICAL_RAINFOREST=4aa73a +SAVANNA=389a38 +GRASSLAND=429545 +TEMPERATE_FOREST=456938 +STEPPE=c3aa61 +DESERT=e5d98f +COLD_STEPPE=a7a374 \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/ores/coal.json b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/coal.json new file mode 100644 index 0000000..ffc6646 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/coal.json @@ -0,0 +1,32 @@ +{ + "match": [ + [ + "minecraft:ore", + "minecraft:coal" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:ore", + "config": { + "size": 17, + "target": "natural_stone", + "state": { + "Name": "minecraft:coal_ore" + } + } + }, + "decorator": { + "name": "minecraft:count_range", + "config": { + "count": 20, + "bottom_offset": 0, + "top_offset": 0, + "maximum": 160 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/ores/diamond.json b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/diamond.json new file mode 100644 index 0000000..a937721 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/diamond.json @@ -0,0 +1,32 @@ +{ + "match": [ + [ + "minecraft:ore", + "minecraft:diamond_ore" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:ore", + "config": { + "size": 8, + "target": "natural_stone", + "state": { + "Name": "minecraft:diamond_ore" + } + } + }, + "decorator": { + "name": "minecraft:count_range", + "config": { + "count": 1, + "bottom_offset": 0, + "top_offset": 0, + "maximum": 32 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/ores/gold.json b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/gold.json new file mode 100644 index 0000000..4bcbeba --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/gold.json @@ -0,0 +1,32 @@ +{ + "match": [ + [ + "minecraft:ore", + "minecraft:gold_ore" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:ore", + "config": { + "size": 9, + "target": "natural_stone", + "state": { + "Name": "minecraft:gold_ore" + } + } + }, + "decorator": { + "name": "minecraft:count_range", + "config": { + "count": 2, + "bottom_offset": 0, + "top_offset": 0, + "maximum": 80 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/ores/gold_extra.json b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/gold_extra.json new file mode 100644 index 0000000..58d0771 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/gold_extra.json @@ -0,0 +1,39 @@ +{ + "biomes": [ + "minecraft:badlands*", + "minecraft:eroded_badlands*", + "minecraft:modified_badlands*", + "minecraft:modified_wooded_badlands*", + "minecraft:wooded_badlands*" + ], + "match": [ + [ + "minecraft:ore", + "minecraft:gold_ore" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:ore", + "config": { + "size": 9, + "target": "natural_stone", + "state": { + "Name": "minecraft:gold_ore" + } + } + }, + "decorator": { + "name": "minecraft:count_range", + "config": { + "count": 20, + "bottom_offset": 64, + "top_offset": 64, + "maximum": 112 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/ores/iron.json b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/iron.json new file mode 100644 index 0000000..e324866 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/iron.json @@ -0,0 +1,32 @@ +{ + "match": [ + [ + "minecraft:ore", + "minecraft:iron_ore" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:ore", + "config": { + "size": 9, + "target": "natural_stone", + "state": { + "Name": "minecraft:iron_ore" + } + } + }, + "decorator": { + "name": "minecraft:count_range", + "config": { + "count": 20, + "bottom_offset": 0, + "top_offset": 0, + "maximum": 112 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/ores/lapis.json b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/lapis.json new file mode 100644 index 0000000..189b069 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/lapis.json @@ -0,0 +1,31 @@ +{ + "match": [ + [ + "minecraft:ore", + "minecraft:lapis_ore" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:ore", + "config": { + "size": 7, + "target": "natural_stone", + "state": { + "Name": "minecraft:lapis_ore" + } + } + }, + "decorator": { + "name": "minecraft:count_depth_average", + "config": { + "count": 1, + "baseline": 32, + "spread": 32 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/ores/redstone.json b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/redstone.json new file mode 100644 index 0000000..55fa194 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/ores/redstone.json @@ -0,0 +1,35 @@ +{ + "match": [ + [ + "minecraft:ore", + "minecraft:redstone_ore" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:ore", + "config": { + "size": 8, + "target": "natural_stone", + "state": { + "Name": "minecraft:redstone_ore", + "Properties": { + "lit": "false" + } + } + } + }, + "decorator": { + "name": "minecraft:count_range", + "config": { + "count": 8, + "bottom_offset": 0, + "top_offset": 0, + "maximum": 64 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/shrubs/dead_bush.json b/TerraForgedMod/src/main/resources/data/terraforged/features/shrubs/dead_bush.json new file mode 100644 index 0000000..f19f718 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/shrubs/dead_bush.json @@ -0,0 +1,42 @@ +{ + "match": [ + [ + "minecraft:decorated", + "minecraft:dead_bush" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_patch", + "config": { + "state_provider": { + "type": "minecraft:simple_state_provider", + "state": { + "Name": "minecraft:dead_bush" + } + }, + "block_placer": { + "type": "minecraft:simple_block_placer" + }, + "whitelist": [], + "blacklist": [], + "tries": 2, + "xspread": 7, + "yspread": 3, + "zspread": 7, + "can_replace": false, + "project": true, + "need_water": false + } + }, + "decorator": { + "name": "minecraft:count_heightmap_double", + "config": { + "count": 3 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/shrubs/forest_grass.json b/TerraForgedMod/src/main/resources/data/terraforged/features/shrubs/forest_grass.json new file mode 100644 index 0000000..e6a19e9 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/shrubs/forest_grass.json @@ -0,0 +1,52 @@ +{ + "biomes": [ + "minecraft:birch_forest", + "minecraft:birch_forest_hills", + "minecraft:forest", + "minecraft:forest_hills", + "minecraft:dark_forest", + "minecraft:dark_forest_hills" + ], + "match": [ + [ + "minecraft:decorated", + "minecraft:grass" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_patch", + "config": { + "state_provider": { + "type": "minecraft:simple_state_provider", + "state": { + "Name": "minecraft:grass" + } + }, + "block_placer": { + "type": "minecraft:simple_block_placer" + }, + "whitelist": [], + "blacklist": [], + "tries": 32, + "xspread": 7, + "yspread": 3, + "zspread": 7, + "can_replace": false, + "project": true, + "need_water": false + } + }, + "decorator": { + "name": "minecraft:noise_heightmap_double", + "config": { + "noise_level": -0.8, + "below_noise": 5, + "above_noise": 10 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/acacia.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/acacia.json new file mode 100644 index 0000000..cf5e9ac --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/acacia.json @@ -0,0 +1,42 @@ +{ + "match": [ + [ + "minecraft:acacia_log", + "minecraft:acacia_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:acacia_large", + "config": {}, + "chance": 0.4 + }, + { + "name": "terraforged:acacia_small", + "config": {}, + "chance": 0.15 + } + ], + "default": { + "name": "terraforged:acacia_large", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 0, + "extra_chance": 0.2, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/birch.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/birch.json new file mode 100644 index 0000000..aa2313d --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/birch.json @@ -0,0 +1,45 @@ +{ + "biomes": [ + "minecraft:birch_forest*" + ], + "match": [ + [ + "minecraft:birch_log", + "minecraft:birch_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:birch_forest", + "config": {}, + "chance": 0.2 + }, + { + "name": "terraforged:birch_large", + "config": {}, + "chance": 0.2 + } + ], + "default": { + "name": "terraforged:birch_forest", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 21, + "extra_chance": 0.1, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/birch_oak.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/birch_oak.json new file mode 100644 index 0000000..e9ffca8 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/birch_oak.json @@ -0,0 +1,57 @@ +{ + "biomes": [ + "minecraft:wooded_hills" + ], + "match": [ + [ + "minecraft:birch_log", + "minecraft:birch_leaves", + "minecraft:oak_log", + "minecraft:oak_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:birch_forest", + "config": {}, + "chance": 0.2 + }, + { + "name": "terraforged:birch_large", + "config": {}, + "chance": 0.2 + }, + { + "name": "terraforged:oak_forest", + "config": {}, + "chance": 0.2 + }, + { + "name": "terraforged:oak_large", + "config": {}, + "chance": 0.2 + } + ], + "default": { + "name": "terraforged:birch_forest", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 20, + "extra_chance": 0.1, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/dark_oak.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/dark_oak.json new file mode 100644 index 0000000..71c52a3 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/dark_oak.json @@ -0,0 +1,56 @@ +{ + "biomes": [ + "minecraft:dark_forest", + "minecraft:dark_forest_hills" + ], + "match": [ + [ + "minecraft:dark_oak_log", + "minecraft:dark_oak_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:dark_oak_large", + "config": {}, + "chance": 0.3 + }, + { + "name": "terraforged:dark_oak_small", + "config": {}, + "chance": 0.2 + }, + { + "name": "terraforged:birch_forest", + "config": {}, + "chance": 0.05 + }, + { + "name": "terraforged:oak_forest", + "config": {}, + "chance": 0.025 + } + ], + "default": { + "name": "terraforged:dark_oak_large", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 10, + "extra_chance": 0.1, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle.json new file mode 100644 index 0000000..f13ec01 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/jungle.json @@ -0,0 +1,73 @@ +{ + "match": [ + [ + "minecraft:mega_jungle_tree" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:jungle_small", + "config": {}, + "chance": 0.2 + }, + { + "name": "terraforged:jungle_large", + "config": {}, + "chance": 0.3 + }, + { + "name": "terraforged:jungle_huge", + "config": {}, + "chance": 0.4 + }, + { + "name": "minecraft:jungle_ground_bush", + "config": { + "trunk_provider": { + "type": "minecraft:simple_state_provider", + "state": { + "Name": "minecraft:jungle_log", + "Properties": { + "axis": "y" + } + } + }, + "leaves_provider": { + "type": "minecraft:simple_state_provider", + "state": { + "Name": "minecraft:oak_leaves", + "Properties": { + "distance": "7", + "persistent": "false" + } + } + }, + "decorators": [], + "base_height": 4 + }, + "chance": 0.5 + } + ], + "default": { + "name": "terraforged:jungle_small", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 15, + "extra_chance": 0.25, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_badlands.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_badlands.json new file mode 100644 index 0000000..6166a24 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_badlands.json @@ -0,0 +1,43 @@ +{ + "biomes": [ + "minecraft:wooded_badlands*", + "minecraft:shattered_savanna*", + "minecraft:modified_badlands*", + "minecraft:modified_wooded*" + ], + "match": [ + [ + "minecraft:oak_log", + "minecraft:oak_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:oak_small", + "config": {}, + "chance": 0.2 + } + ], + "default": { + "name": "terraforged:oak_small", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 4, + "extra_chance": 0.1, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_forest.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_forest.json new file mode 100644 index 0000000..dd1ff71 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_forest.json @@ -0,0 +1,45 @@ +{ + "biomes": [ + "minecraft:forest" + ], + "match": [ + [ + "minecraft:oak_log", + "minecraft:oak_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:oak_forest", + "config": {}, + "chance": 0.2 + }, + { + "name": "terraforged:oak_large", + "config": {}, + "chance": 0.3 + } + ], + "default": { + "name": "terraforged:oak_forest", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 10, + "extra_chance": 0.1, + "extra_count": 2 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_plains.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_plains.json new file mode 100644 index 0000000..da7e09d --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/oak_plains.json @@ -0,0 +1,46 @@ +{ + "biomes": [ + "minecraft:plains", + "minecraft:flower_forest" + ], + "match": [ + [ + "minecraft:oak_log", + "minecraft:oak_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:oak_huge", + "config": {}, + "chance": 0.1 + }, + { + "name": "terraforged:oak_small", + "config": {}, + "chance": 0.2 + } + ], + "default": { + "name": "terraforged:oak_small", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 0, + "extra_chance": 0.02, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/pine.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/pine.json new file mode 100644 index 0000000..e3b26b3 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/pine.json @@ -0,0 +1,44 @@ +{ + "biomes": [ + "minecraft:taiga", + "minecraft:taiga_hills", + "minecraft:taiga_mountains", + "minecraft:taiga_scrub" + ], + "match": [ + [ + "minecraft:normal_tree", + "minecraft:spruce_log", + "minecraft:spruce_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:pine", + "config": {}, + "chance": 0.3 + } + ], + "default": { + "name": "terraforged:pine", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 8, + "extra_chance": 0.1, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/redwood.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/redwood.json new file mode 100644 index 0000000..69f0998 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/redwood.json @@ -0,0 +1,46 @@ +{ + "match": [ + [ + "minecraft:mega_spruce_tree" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:redwood_huge", + "config": {}, + "chance": 0.025641026 + }, + { + "name": "terraforged:pine", + "config": {}, + "chance": 0.15 + }, + { + "name": "terraforged:redwood_large", + "config": {}, + "chance": 0.33333334 + } + ], + "default": { + "name": "terraforged:redwood_large", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 10, + "extra_chance": 0.1, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/spruce.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/spruce.json new file mode 100644 index 0000000..f145636 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/spruce.json @@ -0,0 +1,46 @@ +{ + "biomes": [ + "minecraft:snowy_taiga", + "minecraft:snowy_taiga_hills", + "minecraft:snowy_taiga_mountains", + "minecraft:snowy_taiga_scrub", + "minecraft:snowy_tundra", + "minecraft:wooded_mountains" + ], + "match": [ + [ + "minecraft:normal_tree", + "minecraft:spruce_log", + "minecraft:spruce_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:pine", + "config": {}, + "chance": 0.3 + } + ], + "default": { + "name": "terraforged:pine", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 8, + "extra_chance": 0.1, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/features/trees/willow.json b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/willow.json new file mode 100644 index 0000000..ba31490 --- /dev/null +++ b/TerraForgedMod/src/main/resources/data/terraforged/features/trees/willow.json @@ -0,0 +1,48 @@ +{ + "action": "replace", + "biomes": [ + "minecraft:swamp", + "minecraft:swamp_hills" + ], + "match": [ + [ + "minecraft:normal_tree", + "minecraft:oak_log", + "minecraft:oak_leaves" + ] + ], + "replace": { + "name": "minecraft:decorated", + "config": { + "feature": { + "name": "minecraft:random_selector", + "config": { + "features": [ + { + "name": "terraforged:willow_small", + "config": {}, + "chance": 0.2 + }, + { + "name": "terraforged:willow_large", + "config": {}, + "chance": 0.35 + } + ], + "default": { + "name": "terraforged:willow_large", + "config": {} + } + } + }, + "decorator": { + "name": "minecraft:count_extra_heightmap", + "config": { + "count": 8, + "extra_chance": 0.1, + "extra_count": 1 + } + } + } + } +} \ No newline at end of file diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/bush/acacia_bush_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/bush/acacia_bush_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..d6dc4ac20a459a6115cea164e5ad7b2abb0353cb GIT binary patch literal 684 zcmV;d0#p4TiwFP!000000Iir?Zrd;rMJbBpQ2H*=b@chsMHJmZp}I^V#IX!Z2->co z%dX^#hQVCTj8O+5kQhgIF6TiyYK>_O9lL#+rqmi^C>r;UQ0( z@4oJbAFXM>oQ7jMO??_?visSOrut<}e(Lqhr&9Nv#^S;Fy&tFUuuG#Da|p-}b}qaa=m(Ph;xiFxK<0%kt*mkFSlN zWu|og>m^eOOqiFQmlJfXpyO61Fic+dim8AfiHQov=6F|(1^X(Q(sKk%T`&z`Nt5??X3O|8i9AHrYNKDqDE5-wzg{v4pmp)@^;Q&*{@(P`xU`P=wDs(P6 zVsso}(40y-LBWtBR#fQN>|2=kk||>mV7Bt?ggKS`WY3<_iGV@#<%+o|bu3`Yebs3G1Pd6#tP2G87?=>W6khtnazY}K7R55u}F)2nV} zxcTycLHm*zQpB<=!_A-rOc~27m;e}53rQ#Y@l2k*f(d}x$|0w7(NfejDEx5qMG7V= zV&xo?B8MCk0JG(po585i$$lc!s{|O-Kctwil0kJR#q5*}%Cp1-z@R&YgkE)kLGKDA z=8|hgp1D5sfI&5tbaE_G?2BUvFlb*p6#xJL9aXjf literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/bush/acacia_bush_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/bush/acacia_bush_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..d6dc4ac20a459a6115cea164e5ad7b2abb0353cb GIT binary patch literal 684 zcmV;d0#p4TiwFP!000000Iir?Zrd;rMJbBpQ2H*=b@chsMHJmZp}I^V#IX!Z2->co z%dX^#hQVCTj8O+5kQhgIF6TiyYK>_O9lL#+rqmi^C>r;UQ0( z@4oJbAFXM>oQ7jMO??_?visSOrut<}e(Lqhr&9Nv#^S;Fy&tFUuuG#Da|p-}b}qaa=m(Ph;xiFxK<0%kt*mkFSlN zWu|og>m^eOOqiFQmlJfXpyO61Fic+dim8AfiHQov=6F|(1^X(Q(sKk%T`&z`Nt5??X3O|8i9AHrYNKDqDE5-wzg{v4pmp)@^;Q&*{@(P`xU`P=wDs(P6 zVsso}(40y-LBWtBR#fQN>|2=kk||>mV7Bt?ggKS`WY3<_iGV@#<%+o|bu3`Yebs3G1Pd6#tP2G87?=>W6khtnazY}K7R55u}F)2nV} zxcTycLHm*zQpB<=!_A-rOc~27m;e}53rQ#Y@l2k*f(d}x$|0w7(NfejDEx5qMG7V= zV&xo?B8MCk0JG(po585i$$lc!s{|O-Kctwil0kJR#q5*}%Cp1-z@R&YgkE)kLGKDA z=8|hgp1D5sfI&5tbaE_G?2BUvFlb*p6#xJL9aXjf literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/large/acacia_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/large/acacia_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..17e91d13c9cc38b9cb1785aa777eff917f0709e1 GIT binary patch literal 500 zcmVgiwFP!000000Ik;DYTG~%1>lkPPttWD&(XBK$wP$F7YG|=6GR=$ctvRY z^wlO_2$Q4PHMPNDn@>lx((K3sQBUsRcVSt=Kt#49edk{SUA;{EFdqHWa_dJwPX2y3 zKMyqgIL?P~T&56L>3>g=nmA>%o4*Z4#d$JF^dHfaAjs4k^I?Pe6AJf~=`%5b_Ys?bfzI`@Lg=s5{ zsxUg8?}KTW(WiGS7z0epj0Hw*M0p_MvktiM_QN3S%T3g zV+E7z{@5Funf7E_!6;x1sD~TgV>w3&#sb6p&{gDg!0?*$+O=R5Flg^^gL;)4)MMQ6 zwRaWufdtrogX<$&#mvUT1J-KEpyc)Q24?FTO0&*Z-!7Fcz4t`&F7hC1Zfusz;hF`CQUWFuI_OU9m&!}~|a>2Js=f%N~pSL9yPfB1R-wzPj|hU&|j4FCXJQT@dL literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/large/acacia_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/large/acacia_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..c80c016b3ba79e3a6217767ad5d74ff4485cadf6 GIT binary patch literal 567 zcmV-70?7RziwFP!000000Iio>Zrd;rMMvUO`YzCQ^p%e+BFGL3R%HUAj%8Rv&}Q|K zT(bz6%i$Qc0l~6yba;nPs3+>k9l|ayOYDirdD3^`Dbk0haUX|ccvx=35QZ^)-%XD_ z_4mi=5Rc0k=ame<$C;WxMCqxI@1Kg{InLRG{^vL^VLwD_zIf`_|L66OxL?RTgxwtf zNBH^L!cW&0e!aHv+qH!^SlE&Mv73fp^EOi+J5q;fR_pC{8T$J%ip;86;_26WrV%i0 zh0*D|;!y&Ru6XR3u@%p$y^6;OJXYYb70-4#I^FzZ8qikGw4e`bp7zW%45I*(x1|B2 z3VAq>7368oOe?6XW!h4J$un!fpt)M}SV10lL0w8PUP>@tN|2dr%Ly0{7+o0Cw`{Rljku4#sa1^R}C|Nyui~5@}&N; zf^oNuM+2s`|5&Cj2bg?M@B&Yo8;+@~1x#tZJHdK)OdbQ6ye=p3ctM^{P*(DpvM9SFT5_q@HgPWw|=zXD^FnuOmF F005Y;9EAV? literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/small/acacia_small_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/small/acacia_small_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..de1dc6d918497fb24e30b430a2d26fa3ca82f4ed GIT binary patch literal 530 zcmV+t0`2`DiwFP!000000IiqnPUA2Tg(vYf`MuIU$6nwE4}o}sMc%AgMUwzo6X!YhMC3f_>+lfi%fq;h!#>>2S78Xl7;ZPy z-=6xL{j`hwd5mcx!_%0kc_zxEzJ44P!(&Wrp#L+bIc$eW&84S)d7hRBaXXW_3!4=G zNATj@fv+N_Av0(6N}jghWdI!u z!;E>;N4PY4;lo$heS%FzQUnL#?i98izuz({+w##q410gMN{I)UM9n(I^Q zQA%Le&P9nafR_~*skhX0Jdh{v9|zQ@)T0!jqXEVW%-VkTfR|^zMW^v4W~hHFWUt6Q U9{zJp+ha-o0nv0<*P9Oj09kzrU;qFB literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/small/acacia_small_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/acacia/small/acacia_small_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..fbfe087ef9f3bfbf4c0e366a9d5e658b430b2e67 GIT binary patch literal 510 zcmVCj?*v@2H;72N^*_Fb8x!hAy(XQjZE;OXuC~SN2`UW zCn+^Tvwy~eXr)SR_2r+jW4rA@)KfUcLt2(J5Rvc5-o?j6uO6pk8qe{5`54DIPVviO z{yxy~={%p(d6`mPWqh78H9v^TRj*%OmEu#%?85Hbl$UrM6E$x-8rIMA`awD_%b2sbJsVbwo3>;L#skxWT2?VX+!$s8FnhJ6 zad}|SJa1Vf)H1+WW}2&Mgq#)_K6azXvB2;PEpG%asn`X4m)7@9QMHQ zIV|(z6#G;%78vwhzzsUbYB@pCPY2B28RZq{p=2yDduzih)`pZ56#aC-@U>BT3WBk~ zpzkJb_Ra(;Cn)+UwYthZz5FhzoORrwvv)JKv$tjpFlg^^gXXzrNx1jmd!h}shhZ0&CH6$rcH}+}50PF!jQhAf4tL9^+wr*l z@pU(S@2UTCoDT80jB#GU;nz4*^P4DN^yb+`ad?a~yRQ2-&dadhMrz)6)UW5~^+en+ z|vVi`Y|1j{=J+fzsfA} z>DzOrQ7}fqSOwz*CLKP#3p!q*6O!*UrYSH|t;BetRx9Y36Lap_Fs3;%zQP#5*n%Hl z>D>XQIN*mf+z*`?Q_*PvgVu*LXni^*4bA47{H*J z=M3L>ukaH9Q|ikp_OzA@1DMiY)R>^Cl|6ffXDO$gDb1mjQ_ht3tEQ7Zhe}@tFuX4d z7~Yoy4EK|3wTk;jtMJnaeQl_oQwtc>m!v}{rk0BaFlc@_gXV`bXnrIeukaII&{r+* zLBX_u@r7EooLaz?_OzCr02sbLt-??CT+5dMOljY1`Er0k_YFxWDEws4QcgKj+KX1W zZ=^?E={cbnX4L=&dFBlAEa_xFwRtvxLG#QRUdt&Muc#GX;91HbXG)%HIrR#ipzzZI zrqq}8JII;RY}E4Q6*@uTC)cXwD;-xtb{t^P`dkV5vVcLJB^?hK?kCr(&9hT59x$a^ zwHb4OK^c^E0${kGTuYk6YsDH~3%xr9;{g*2I=pu`kAGr@(nH;=_1IWyAJhLH)BY(w Ne*wOe3AGp#000wiITHW? literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/forest/forest_birch_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/forest/forest_birch_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..e20cd288b3cbedaa13f39a2835e0ebbd2d1ec49c GIT binary patch literal 550 zcmV+>0@?i^iwFP!000000IiqXYTG~%hR52YRQIaT_vq;+4-xtXMNv1YsACyRgtSi| zDV4xX{+%7D4aNsPeYe`uOIe)IOz=T^E1$DA$&6iOy7BHw@C68Bl0$_OT zTU@1i=L|Z-OCG221i< zS@L*=Cjf@`&$geS=&{m0_Z_P{@^FTGyuxEvMyi(u49auK;}xE5Eh*0qFuB+@o>IN` oidyUy`Pqa0*s=ecE)VDT%d4UAwERyTj;|y30s@ir`1lV10Bdy==>Px# literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/forest/forest_birch_3.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/forest/forest_birch_3.nbt new file mode 100644 index 0000000000000000000000000000000000000000..e14b0e1e4df641f6ee6622419920bd4214a4c4de GIT binary patch literal 577 zcmV-H0>1qpiwFP!000000IiqZZrd;ng=tHa5~tnGfWAk6wu?Q)us0aoOwEj@aS$2B zwx^%fSr=m|ibWH^h+}*@Jfe=Gqyt!kKlb}DO`!t-zXJQUe+uyNX*h)KsehQh+z+Si z&+q$j*FpE~G#W5~>%G9^FKzCecI&Vmie5B1YB47Xl2#xhLF zv&1+>t?XIKp<$SkXNf@tlWRGl&z50IwQ6TdV!Ti*>0DIuY!^n#Aulrh zE~NcPeA(}Y^dEY!PptR$#CmT|toQE3dhfB`io(V2u=zgBBl3Gi?)ose>F4}q`up*Y zc#;{@?Z1aiV_;eX;|z>9Fl=Cgfr%ByFAOV8Ffj93-Td3YX!QiZG=gypQ>(eg@a>gJ zUpdP!OnZlE3p%Z$6CRAf@5lf8SzR|^=a;{a3Yg~sHwSYaGs(7xAn z0$|YY)XvH^2gCCTNuIn9}@cI=SX(*yX6qPXG+s_a}5e!{u24Flbh1rd4*? z0Y>V0z@T182KAz*6Akq=3scJ|$)Jpq4C<+5O8wF@Dj0O4p`Om*D|^>+%mJq4U1I`Z zO1?CmTr*d4Loz5gYC3Gt34lSlQPasaYdPitBm2b$odB3pFKYQbGg^*$z{nj8fI;4$ z((RvrOaR^`BhSh@jlwWsP(Igm+`?$N>H&j#DjC$%nohegS{^#Upq@$w?T%#7?$mUm zp`Pr^8+>V5=M4-4rqm0KiH5T}gRktJ6&(kd(tc?=Y|sgSLD^E%$u(SIW@* cV(|I&?Ht9M`Mv#NKaVB-0X$PWCD<7N04yqyMF0Q* literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/large/birch_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/large/birch_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..3ecd569608eff29c7bcb542f1fc1cd00eab2d6f1 GIT binary patch literal 351 zcmV-l0igaLiwFP!000000IikVPJ}QJh6mbmx;61NjE5^9!uSTJi?iCu0-?=DeR^S) z4Vj%V-MAzq(0u(m^Or*fP(t3@21CFK089b;$?g!YcCN*Gu-ovsaYOz7(sXYXRL?`# z;}9JB5wxGqL-GYMPr5#tgm#af%qw1<54NollG_5Raomps*ak3L+j#tk@BYHRhYS1e zcwY(Grs?VrKW)KH399eB8ZW0)!|!1fm{Ak(aCgci5lkA%WD6!COggg*CS^R7QG_uO zo+B&Ntc02k!%$Clh9?*D(WEcU^hFtNtys@mK89iFTC-=ZcOAhPhT(FK^+-9JhCCv)2Q4liIoUR!`mgOK;vJ$mhw7 zG&xOn2GyswP<;V}L$_y#NFB&V=bBe-ofb?Ov@7sBU1_!uPy`_Kn9ko#S3Yc7$NHD2B^QaznAuBg85$<-v@I)3y w^`x?5=2F&@M-(!cso67iiKVlfnV~7O+08M0za{vGU literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/large/birch_3.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/large/birch_3.nbt new file mode 100644 index 0000000000000000000000000000000000000000..28c32ab03a33554bb1ed9b66cd3b2ef7af6f06a6 GIT binary patch literal 608 zcmV-m0-yaKiwFP!000000IiqnZqq;zg(vaO?%IW4NW2H-@`Hy!ya95<7Oc8S6k8SW z^w`B)MbqP%twMDqk=&Q|KGB=|Zl89i_ICVy)17ud zzx9V74K-g+hhsX8T^c5^{nZV$`Aw8>di(6A)IOv^+!w!h!`SY3i8j}fn(6mp`XTK{ z;@h?#(tqf^yRhE-3+sKnu->N&>wUm_HTj$Vu)7=Pi1am8$HP!fALond--|2aNoGus z-<~s@>2d{(rL!&2*%~@-VZ5PJ8MWN#atDbiwo!SN+@0ET6V31$l@bsx(fsVC4mQJn}j2ub^ z=>)Nc4y>VMq)q?~%4#U^LNc;e1Pq!zkiJhDFfu=(K<6n#1^sBA@(Xk{-?L6UV;Fcb zuj5vkJ+Jk-rylep8MIF&BYO@7Ozy`i{V-tAStl8kLrW(DX02yd_7?+YZGEigPBil4 z0kh_NDDYi7pEDB+{EEi;Q;V#k*=r+*9x&(}lMM2-7QUm|C4=^%-Z^B84CqE)xvniF9#Tu4auM!N@mTkVC3gY_>O8_8D3-^YcER%J)0~Y uw=mi|d%&Q6UKx4LjO@n^-Jf)R_`IIqD+&+uf9=EmQR*+xIe%u15dZ)y_A6Zg literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/small/birch_bush_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/small/birch_bush_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..7d6a7e566a6e2f658caae864deafff4a5bd4720b GIT binary patch literal 301 zcmV+|0n+{-iwFP!000000IieTPJ}QJhJUsP4!0&IK8B|oAHw(sri-ICvOs9vs84S} zNY*+Fn+Qphw)y6pKLZp%jxS-lY`tF_np}SgFcfGFec3s^K ztIMIcRa3sX{%v055N+c`*!D}|=TQPGSUe4<-;8FM1iVKtFiBYQfZ0Th92qskD29o9 z=@UFD!-yk!^bDgIChnzA@aUl~><<{p_sR7X5>Ov*5EFEKJ|#v>Vqn@`Me^I7st zdGrbM(KDG9!*H3!!VKlaF7%3-zL#|NizX-=WvM{A?|9zK_E)Inikiv<7x#k7oi literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/small/birch_bush_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/birch/small/birch_bush_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..680adee74fdeefaf7c6c0f567393fdb95a2a2a91 GIT binary patch literal 315 zcmV-B0mS|viwFP!000000IihUPJ}QJhKKEe!>x&TK7^+mAHw(sri-ICvOwCpQJ>y| zkYJrHjk_d-(tLa~^EaUaC?W4vgU(?E0I~pnuXYIMJKbV!)U&(WYE!>HHmLS!E2th! z*Q0S7Z2`rTwobJ*LUvO?<@c@Mz}5kG<{bh4t-Fls&i?4Sm%zoVEzH+;ndx zluv!Pb^TB~KZ4a~?IHOBm=|51EOOP(dA2Tpsr^v3YX`|q4&`{=j~84!0KQg@cmL44 zJ+t23nf0#DtamNz6+o+|TYvber4I#|ZRgGSb$VO)eOUuI$_#G*>y$|pOsZf^gdu@R z7?U18@rX$mYS9s681v^lpo0Qq1<#y8!SmizAQuV@-=9pOGv^BJGYZU7F006e0Y6s3 zkb;@DNKuOdJ)QHX5gpF(>?knO9>#Ri17j4mLbE{v!~JB6TC<H_Ae!r zk3+w8!`L`Kfz^BCA^QLblP-=XxoYP;nP;CHKUQ7sAiJ?pPVfEn!gV9)b=7+J554OX z>s_8$@AkxcSG-;UX4Urfo1Y`SFCgFcKA(Qh!@$p_bI_B_=)RwhnM`1C8fVi`5k~JB zX)TG-`+I)CWQ4H{b4C~y?gvcH_>mZ7m?h7#eFZ-zvX)L@f@eHn)PiS>)KCXcks2by zP(PMoG_!Xl)LAnO)iDV=$S^buo1hbX>jlb7*x7;%!_~(KdFcekFbtO|Ww=aJb)-x&;oG4Mw|69dxGX5cWkDIPe+kdW zLq06v?}q)HUnlwg`jYRiFZuEMlAmzN4TNv|X}g@XSD7V0 zefyX33a09Kg$~CQolfCLVycehM`DVOQTUOVsw4T47+2o~9f^5nFPM&qC3%(@S1*PM z3dWxqE|xhnLC|^H>kGypekA5Oe?_cI#uBkOCL&Dh$J9My7>_WH@WU|?VOq~n<6SVm zVLF7N^72e9tI+WXgY{v{P`Pt{lEP2jpTIC3!nCmr!nB^PLMJG6;tTvlMZKbMzKW-g zVI0D=J?0T6Ap2Yx!{``Qzq1DRex-P`Pu@ z8&RQC^Wx^AQ!pN3s9ueVdTDf$aK4I`G{;(|ozK-1H(MOSVEfZDlpl_X2t#@1&bvbx z?EWR`1O*cnu@b_xd6_fA%_ys29Kulb;^szBFi{aJDLmKQx!Gb7hRU7O@d!iZEV(W(C8%L?4;%lVxC(DJEuuF7Y<=s9k0*{3a0w;Oe}{mRPJ2Q2L)4O lasBKOrs%kN{Fy7Xsh5TLQ2wWy4p46UzX3Eu%}2r%003oeLCpXF literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..bcd26353331cf3903c2b91c57d68486742deceec GIT binary patch literal 711 zcmV;&0yzC2iwFP!000000IisBj?*v@#qIb{((oOLW3cdvLm+Oj3SBR%+HI;PS^-Dl zv@|8ImeI4{AhuGaHu~kwjOUG=wl`*D+S71I^PGBPOz4dNJUk`y_Gx!a+w<@^e|i|s z`}=X&-ygSD}o#$GU1FwM%?3gZCNfP9!v1dJ0*tB93zZYqoejEv!msZ@c zB4AK2m`<}YY~Ce<`o(ktU`ln>m=-X;P!~G~C4**%=>)(aZ%78s#r3&FvCg%<44|6`PVtrbLH)=k#*XZ0El0oat7_W$BR;Jc>3mDW3$)H{^9k0kI07mvJ z*H!b61&qwO0Sx->z;wJKp8%LrFW5dNnNly4Y>$!*>V;NE_RCMZpXuf1^JW2^X`h$>c*o;4&EL1<^*nPI008RFT7Uom literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_3.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_3.nbt new file mode 100644 index 0000000000000000000000000000000000000000..3d5c4a800adfb7307f130ff76c259f66ed5469f0 GIT binary patch literal 581 zcmV-L0=oSliwFP!000000Iio{Z__XkhA(k!C*eC1KZC)i{fLQwFqzInQM;yUqG|B! ziAN0Obn$zIRjSlhPo8^yKBsL@)R8}hL!9T>6On7FxeHH`-aie;xI2f(`Su~4_xEGi z-ygjm+9HiuZJ~c7tI`B-~G*4 z1(Q8a;VCi6Qz`NwCVNmG#3WCx$b*>dL3t4Ko4sHvp%!PPT8POW)SuU9fvHyp)k2KD z!c)pzXV(Q&3m(o$c{pPjQ)RADs}>mUF$$&;n61nuCTG6vVY89h71m`GOd~LST~@*5 z%wCb%D>8dUpS_~bC6BuzPpjx*tLS0L;{-> zndi*bexZ3rO!lA|LyQ*~E7a=PjB$@ynHroO&PeM*j8oLgdC+WdMw(T`I7O{`Wzcsw zXQb~+&Pel&JULJ4US|YmtB2_Ka|h-yd3w-8voh$v181Z?)q42an`!tFFBkPLfrk8Z T`tLs;(=nl+RRO*c!w~=g1Jfew literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_4.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_4.nbt new file mode 100644 index 0000000000000000000000000000000000000000..f7a72fc9edc7aeb623552f856b7a3b22e209f96d GIT binary patch literal 904 zcmV;319$u%iwFP!000000Iiv8Zrd;rgeg&^9!>hQK#$SKuO6c44FWY`3d4?VSVGYD zp1n`4L>r`wA!mcefMLh@G#u`W5;8c~yY@Kl(mbcZITt$Dd>!wSdv~|lr|Z*rJAb(u zPur`*xV_pPehhB-b~+r>Y2Kvi%#J@dldFC?C)d44*QN13P3qF_W;4y>{yMqpL+6I` z@6-8*w4YtL9d}duKgo};FZt>9B|l@y-o@{`!}WGrGV-T){&<-D`Rnp-+~ce9E}UiN z^zhfeOfWE_6AU^fX3?n(ei$P<%nxH0o!a1sF`~o#Fs6}DqQe-`VSX4RI+5r+a0Z5X zZVa9oBRUq(&zTA^f$S>+CZ|){djyQuX#rC!rUQ%z@d<`FYexHtfYE+hz-T`mV493) zmQ&5-JV%4)Snw=5ZGj0Z=SVIVrXuyD8SOb57+I?(G1Lr-A!9^`^$ufN!05F^tJD_( zFu8rP9%O6PQXd7z14gga045hh)`ObSbts-oz2gCcW-ntRV6-3MT0?wFjOcWvFP0dU z`m|n|=l!K;lm`szcg>($WjfKI(*j1vr!(lt-fNOC&E(?D<_6P=2Avi#x(+*oPXB^^ zd4p#*>ok+gS7|NrQ<@u3bAj|SW1f7IK4nZWFtV1VmrG1|flg1(cg9H0*i2wd1PtnT z#?&jrdRa3lK8y(lCISZaGGjW!TC(?2?|8tVdS*Jopc4UuW&+dc41Q$qrF$C>7&H@@ zPBbtrV9>LSW^%P!x{qoBlha}IkTC%;x%y%AP&4R0is{H&Y#wSRm(!9?3m7yr7p6DN zgwkA%?(u9c^}C$ktWOye0fTCc>C`Jzs(B9>lvAb?U$9oGcZ6Ym!I%J;+*+)j850al z1PnUkdPDCpX36`YY6CqxXa?OUFdbQo%}mXpy)&Hvn4BN>EUX!nccv2ngP!9CLmg@c z)jZP)fXVHh)x2hMI_#cSGsyEBl2gAjEMJ;I`O*xU^P16qs+B3lxdsf%yJk?{nNBeH ziGb02cPmrMdj%Nnrv?mqj$=9jFxs>DVe?Qk$nyoM9}gH*hfF5`2JP#@P-B`wHKrL< zV@xL){D|jL{nUWTd1f=px>l(+ypg#iIc)%g&Vox4Lk}3Vccv2ngU;nk62k^Ci%v6b eey8UL!Q182yY_zhfAL}eVEZrjfO#T(4CC literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_5.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/large/dark_oak_tall_5.nbt new file mode 100644 index 0000000000000000000000000000000000000000..a20faf154c700fc34f84251b381959a317e80176 GIT binary patch literal 798 zcmV+(1L6E1iwFP!000000IiwpYTG~%g;(0E)c=J(Mz25m5T$Pr)MQhP9m`lEw0)(% zWWA{t+~MrZKphAq=JS~|JF`b78I2iCJWYqZu6Z=Z#AMu;=_#AnPrGBjKTnVA=ZERM zzniE1-C_Pdn(^y-KIQYe%gcqGe(V<0{WPY&?+toio}TkU-kKhE%Q_wJv+3R@GhSX_ zE-&QcYW(ALSn~f#zPr8T``b%?xV_}Z+e?1Jl7k7~4)gtfDSPvKF!nSr_VQ!-cINMw zv&LU!*8K9@zl>KfRmUrIIHu@y3O^E4btFF;rWb12jgk727_Vfk!jDzzXqdn-Ve8Eb zI!@u)Df~FWPps#7#dL&8fayUkPx_)d0Wd8;9Fr8a2Eb6yeLW-L3?(K2hWd#LCIP19 zS(-a#TC?DoS}QP&S1P{?+GxjaXwOJQik^4D|9SiXkQL6D90q7 zpzsqFI<D0PZ&E?4`F>6y7(z@Y4qbdti4*%&DgDbt#} zl!uf-c}SVo`EWW>p;P;kvW_yXeU?5OQ>L{W98>*ppJ^RnJn&rhgUieS7?iz*;d0C> z7zY^i9Hb0-4pIhXf}~UX;_}A=rqvh6ctx!M7_{?}PPZ{!wm87h{qcZl?GKkxQK4ft zM*7@B8FZH=9SazAzbMn1X<*KGT_Oy{n#KylfA7a~huK&ric? z_c#u_$Nl)dH~rVsc+97Ho2N57{Mb&W`Dsjf?k##Q53hM5kEW;XG!KVOHqApa{rU6h z{6Rj<#y=1HDgU43hx<#uzrW; zYy4Se&X>RbWxRqZI$oi}F^f*4@FOurNAe>vi%zRpt5vKeF|Pa;bgZCLv?R|G<8NR% zKVfBppmW(DS4=~gw#@8`X+d3-p*kKg7I>C)qQXy8X3sD!VCY&_q2mGLDs{C??NQ+; z-B6ch*7AU%dm{p-wl^G8y9G>bA975YsaN>%Ozjph)N@j-Wme|;eEEv!pqN)s?8Cs+ zT6pRNPifg6io5WWw-+j%7lgCGSdPE-363EcL8P4 zT_EWcKU|JEz@T#>=>&yN1PnUMm+uOSK6Clw6gpm^6BJBTtksnBbH#*8?cDcg3z*t| z<`@r{TJ2npMTJgM_$jqVraulav_Bp&==&jMYBQB`bz!9ZS?X^E&y+#=voKb%7MD>D zFzCEf2Ay|Fr}*JA!2t&KS<(p#ozg?E=plEOokGVebb^A3H+bf9!zq{m7_>J!VeW2a poX{W2p#EreXn)+a{gn&UudmLU_`3XGdOTcg{{h{6>{!hh0017aZ}0#B literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..aca73e633ded916d9876d50cb8405ec942fe8677 GIT binary patch literal 696 zcmV;p0!RHHiwFP!000000Iis9Zqq;zhS%PYS^EG;90Lmd(L*3^P?>a#qNH&YTNQ8y z4ozaRRT#ZHI}xd5sg0gI^X{x?ZM8S1GqfMJX`WMWjG<`Um*FLucQ5N*S{;Vx`Qd3e zY#zs9^SB+q_on}P829NguhVp7hac<7G(U~W-+Pa~mxk9gv9HFb^)wH=RWi+8H2v}O z>G(m~&Bi|u+bR8@^5gB5A8xPwbbICJ+bi#}vNPe^c3f?y(|z;1Gj=~t_W0}Ra^~-k zx5gheb6WoTm+=ZFdxF9vF(*%>$Wvpor&YAt(q!8E|L^jcsXU|O(t&RE9K6}7D3@qj@+ta%79d|eSRJae1({(^CZT24{R z3ALKMexX(in9{yW9uFAaX95iGa|8@ud#A{p*A)a00fuLefZ>^QpGm0I0)~4mU`lf; zd4eJjDLhf($!m{_y@-Ox0*2>t3XfOhA%!Pr?iA;b#Dw$J2(y#9wlZIw;h6(q(AsOY z+B0JnnWeMN0S4_CXVCpz^SnI^6?vp{)dQxKNAkSQp^(P|2Aw}OPu{N}FcvU8vr~8o zFzDQ<)#|RuTs!MH!#z>Kl)rT3_yUWndu(}kR@`GI(nWN)F7<{=(lga?6-!sF)Ndg0V8Yk&xtMp)+kM%b#>e@~!+3hS zKa5ZJyTgyQS${hnj^#9O%XDVPpWDgUU&gfi-lO}<_);cv>+rCh=5fC%#@-EPeg1wr ze^K_ciI3xMD*q?>@$Dr)yuIY7x0n2kC08bW-yJqjQyuc3mGQ^JV3|$*7XU@kQU}#-lp_3F$1`P7Ci0rcAHT zl8;HTT0WAuNK61sudb|E^PEoOBp;&;%BS|o2(c<7oyRECi)Dq{B^|pkj;Y-N2F;W* z$j2x{bB=&PYgN+8ihRt%c&2s>7_>K9bi_ zrnfIRrp^7@$I!EYGQEAlo!J6ldO366n-n@N=gidQ0fWvzltE`>%JlX-=c{eKa_6xK z7_?R^!}*v844M~Z(7Y&v<|XOah2i$8S1=JUz5a0f)B~p1A8s!vz@Ytg`OPEjFUp|j zj-(S6I!U3E0fT;nT+W(`{z$&DQp}w)s6Q)VUI8$)uBgyS3Y`{<>tO^;jTNTt?{ZyK a{oi%%O@68W%^vob^8W%`+@(K$7XSd43~(3# literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_bush_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_bush_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..4c39cfd439f7f5f7660283f8619d5c4359407f21 GIT binary patch literal 385 zcmV-{0e=1;iwFP!000000IijMYJ)%!#YcB`{Ww6O$7qwkdWg~+gl#e{l0?PbhPJ1# zCJOB`jvG=z{NTr%H#1Kx0Sd@kdm!(r1OS}_KHDR~{jsWP+u1k&w6ooQ)7bsyK=wqg zgz}|pTIzg7E{A+qIdAJNL9)uB?BBco1=SwVcOL|Bi{j(ID?W&dcbsDL9>}5ntQ;g? zw>ep}P6{xuhi1EX!#36hsJ3ycza35kmm^i6dlgUTr@xFE8TE@v0!A}T3Vba|Cu5k< zPdYLv=qQE>{UF1rnZ6{(MAVx6=mw=D2=nEN!%TKIM f=owx0O%s%lV6GrL4Zq^1K9A@J;%7(#7YP6Wm(aET literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_bush_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/dark_oak/small/dark_oak_bush_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..9202c797d4c6ee781c2ce714e51c423828122d5e GIT binary patch literal 396 zcmV;70dxKziwFP!000000Iik_YQr!PMAz0z{v4ptWAq25hbX;4P!qM7B(|~C(Dw9o zf(7X!OKXyVAvTYh(asuN15{A-dS{$7H2|m*@Kx^(-0qv!tOxz!UN(Bzt~$M4?TkJc zTSNUebiEl|V{8fKrm;@9YXjLs3HA8gjt@-h0NwCq04`Cyzq;aGRPiRH*nJW?)ZdMT z?B}v1k5@n2Q&6!2^}g%YTYK#HxdPF5R*ZlBb^PT5Fz(1PmG8#BEV#X!2pvJ zMlp1X1$i*yg#ktoMlu++z)Lc`VvN^O39l^CshF793^BmaE+m6d3%p`8lnh1%7@EhQ zT_$#wGIM!ijAD4@6La1v&SX}M87f9!F-9@GiUpaA2%{K`pV9d~%fPuPLv`}$5)mB{ zV5klT7&;%xU=+j4=fOnplnM2oV*Gq0BadX{iS4{(Fy1S+V;ErOx=6h{d14RI=Ss{+ qkU7uk#5$D>#_P~7&^EuuL*)dP0*b@&)1Yflqy7W3c;3kQ2><~9+`YE| literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..533df04da87754fb614cdc42146782e710b82b3a GIT binary patch literal 2225 zcmXw3d05iv7Iq6WiMc&a3ME>(m()+lN=$j29Bb68O`76Tp}1w5qoS6HFqf34Kus;f zg@LYJw_J12axeom5lu9gjLO_l6qhhX&HHQS-tUj|$9c|o&Uw#!-ebtvsq*)$FPMwb zIs1LJ!GL8dYA|DL zU@6pta+&j|U4F7%%Z($uD{%$a^Z2;N*uM0Xg-^+ZE5T){QwGGPrPe^JjdWzv>VIfr zD$i{QB_};(-q0b6$^DRVW%W;7MGMp**<9< zWjNxUaM}YmRwMa(Qqh{sJ0|yCOhMhqNRw95ZgEfwCm(X$7IuDY&VMXgJ2^#M5;Yzb zs#mo=O0M;>m*=l82Z|R~_fIB7eB0PN%BH+i#E(**bs1~wgqRut49GidP|jTxqlwZPPt-8V*@F*8ZkGJvCFR-H7>NNl~H5gNMr~S6wH4~kjiB7{V zZnTxDI%M7?%c#S+aJ_xp5+QXT42a7^+Tl=&xo48N~{5<>ZV#nnyy|E!AAD#ghVbT$L0{xknaz@ z>jnt+hD-sX!azok9uZAAPJw-Xeql3UMZ}sE>Wlc9feeL4S7-EY>dgbJH=o?=2W`hs z?`!C{uWXSU03WC|dPUYO4HlheSmY5Im+Gd*WX8tNe4)PCv?*st?WI_P=#=6f=({Rs z$e@K7#I20Q+h+Vo!Lyy6R_KFL=K(&EpL4Oe@Jz8%yx+zv_xFL1&I8=?$>-a->|Q&< zc{0)_M%I~0%*y=cNOy|?=7snbiRu)7Y5m^Kf3FFb&!bBk0XEp|6l=`Pk#~AW^X#BB z?WQ{nti0O3Z@P_e2t)-rTYzGJ)BVPTWUCbNJNhkzG=ZcR6)Gqbzil(-o%2&CJnm)W z-bt-6Ax!KW!m>ndjyx_l*i{Z6`bco%b;2uEj5d?5n zG%2zHqKQ#+5)JJFV#rm|qV%K&77Xa;eC)eJ%?uSx^hs3<6}KZMhG(kl)tLVI;RO|O z#c=?V&^P*ubcoQG{D<@ljNR78g!#qE=-0+D{G56(0a+MN?re&k2`w^NvS8K=f_Ys3 zU@Yy7nUGlzGu0HULlo05@pC_G!#(9$Z<0b1EV44|Ppg2a`9eH7P76MOF`+G#!?RUs zB1a*o&8l(_5ca~HIXLad3u_LSg5!(_>gEy7B>B=wjLR7_bqJp>t-}!COXH{xHoc^*?=Hn}MIIf&6&4^OJ1vl9 zwhV+!TgWk?EjI%Z75AaDFR~o21qTdc%@I9@4KR@Vk^WM9ziS@a@JN4|t0z4Es5!J{ z`XMt1mm~VvSK2VlW(J*?D`xQt=;*2!+uT**YyH)W%@jAP!!+ZiK#eBnHJlnrt!G3xg=wC+i>Kb8w%yf_(DhbYH(*Vo}uQiF{J!B}kOKSA`6w1O-JQRE>yDF~emm z5A;7jRpK?_bsq>=EntQTmUeYOhaEeJsMEa>S03xH!aN}_uhN~W&UlotZd}OERkXjX zHZTE6(I&Ekcg4Df1K>uVy$dt7VTChZ96OAt%gBOaZO=glhpb9Lq9bLx*+^IUVlf-B zx(O1U%#=E6f;vunKdbfPOmRBH&L|sNh7=gnE1>X5H#>Vjxu+v9%-#>Y;V0x?8Heq0 zSFbPV-0RKCBwMS42RLB()>@Xs%}!Dxk6YgY$lix^aa&)rG~i!^DWDQL4>9c$vrK52 zM^~r4p*|Lr4T^wW*Zmy00UmMt+Is3p8FcQGJJQ-5F`#2iOC0dHptlDoaYKMoTh5h| zFvQ8E-+=#n-a332$NxO7tiMIK9@zUBuA^GW?|Zhb1v6E1T4_Exdt62l%(78{!`-=RqDK^mqLwAh81src@0T|04Kr!Bv2JyIKrw%s%%9{SESoX`a9%)DEPm_GP e{52;sTk>%G&$;pE7FDQC?E;&a*h8KwD*pw$S7Af| literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..0b86d6b304ea1a3bb54f5ace77d62062f91778fa GIT binary patch literal 1813 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 literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_5.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_5.nbt new file mode 100644 index 0000000000000000000000000000000000000000..ac2da9dcb88d507a43656358449635db7a3cdb66 GIT binary patch literal 2210 zcmXX{dsLFy9_`+lGNv!GtOjnPrjF*IiBc()T4_5nEx!=e^U?Q$L;y%vES7X#TbP^j0q~ zH<5@ec1@1|Ix+dVEN-Z}rlztyraAOgNPw{3`q7|u+!^bqqpwCXifa~HyEP@6Zf*Fh zVH?!|G5Cpfz@3uhs&}=jl4)zh7U{VbX~szGCD}}K@XL((G3J4mdZ$%x&5Ik=LDb-m z(HP%6$Ggj#>4Dl#P2QcM`rZ$dc{ey`%d1#r^QJV@sXu{J!$H z3Mvd>tTz@6)~2eq1_Dl@vAo*a?^Tn^PVWk-`cg^@w|O}rw*(vC8=aU+3(__zlxQp= zb%=B&>{b1?y2b9Rnlj-GcTHxhPb&pS{-YyF@Bt26GZq#u#t9E zIl8bT-9AN)iM*`B)eRnFSlh0sHWvZsb2Vzo$K4co*Ma}>`c0N_=dENv_%1zIs}}r2 z=^W|$xymut1#X^sR8 zXvt5S;}BT)MqA743|2_U$?2QHr0_*;E>vGV#x?Gq6CI2X!N z9n=z!l*LjMSS+#94d_3?S&}^>Oy>Qn6HL`)FZWAFEa=Q_rc$)c_J_=V&r`35w%P|*;I1D-}C*2#WGxexjr^miv91|ur(+fCAgW^9BHGJDE0 zTY;4jd0Y^%bD-GLB1z}SrAm&l5o2H%4vdagb*y|-<(sw86F!<#W5Mu8eUb}<* zL0D%vUT|dJczg@;$;x+h+{v@$7}A5aG?bE09b@}0;9+zN{rWMc(NEYS>E zId+HqvF^}k%RdqYk*dp@2}^9S=z}b9hnzY^7Ggq`Ud{+!W}Rz(KlDG_*b#85HMt3?4;LGfds9g_aB!eRwn?@4GZO#nS_^P`MXq@(_i= z^*{->rJ*d9=ZC#TmE>*qy~`h`9aFInz|C|w zF$;`Aj2QQbKbv+8k)l%org~2#kwLXw6n5#`zvY}=LklZPxdnVlV^7~~TF3mYj^?a` zpd`)*Zv_U5y{In^i#VkOisofsH0tvL#I-#K3G=PFldN-5zjTEx z?C7m#I9fWf&S6xKGR@5PQ9~TXc14~>z(}pAXpS}0_Bv1v=kr|zWe>-Dk(*o7lf&y! z2Cyt%p>tHck_uC1+j;bOOvL8kMM4E?5L>li^1QP_8yY%$Q^heY9V z`&A+Re=nj@cogV5A1A#=!Bhl?WoTvF*^dYW zu-A7kNE#REit~HA2dN#lu(i(`j!GUau+Y$6sK1CmH=I6ngKW{=)CQ0(rkQIing<`m zVSl~vrY5J(&GPt6ymI_m;aH*rx!tjIy|P*tZDYSpf%1 zF4=`5&-lNf4`H$*DPd|dDpJIeUIR!nZ#;j@%M>7mp26d^JwB-_cG4H*e{6*_^aIp1 zUyVT}#583~KB$(unvXQZ`5FT%MO0JhmWbwebgtHmfJjL#^5k9UPJm*lR{R=(T+H02 zrPCNL2yMu3z`Oszd^;Kk#MzkGlzdP;)^TQ#uc#s>{&|H_KH(%<4qqmn*QnX+>OqbF S!8PTSwR!o2fGXb&8~zX2jd!I0 literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_6.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/huge/jungle_massive_6.nbt new file mode 100644 index 0000000000000000000000000000000000000000..398d72c0100654e1c690dc044ce4f3493a4ec21c GIT binary patch literal 2505 zcmXX`e>~IqA7`}kXz_JdY(=g%63NGWQ|4yh-JKtI7Ot)(zcxSiqe$HhM=|BHFP3Uf zHd(kG>QESUq&)4(ye7#<8o1AS1AJ6Ya zYQ`Svj%#;{{76*$S9Q&a4>UKsCw%tq&p+zxBA@lnA~Le-tUrIOf$_^{5 zySMT~I<%MmAu!QoA6pl}5?S5wGuhYdSGplye_|V&nw0vaxpjmUzosAY98S9=TAQAD z_v(`_8;!>E&~k_R?Zy4e8_k|S%38mmYnpVUVa5ITawBw9R(#M2c%0_l4VHfN z?dZF|RNZqOW*hpJjSfZ%iPgOE+voBZ;*h-;rdLk~1fLSEZ6Rb)s&BW}++Td(TCDMc;U)M6#xXH1ndHhqO#AJ2nAy;QCC>B})2NmFER-h~|2~3X49iwXZf%!*FjN1j=ECaQ zdFA5E)J=21%1Nz*L$*RyWO4zl>r%j&D~6ZW zAG!JknjXfN@=*Ym@N3F1FKvBVP#H6T`?+wmeI^G)vIL=THPSJMmkU!#6!wh*&SI}xC6EFuli!Fai%gm2x*oXLT5BFe+8%p@MctO5d1KSxgqS++$ zkI&f(l(^6~9K&!x>?|(0qB$uo(E)Z4Z!P<+AG#3NcdviK>=h|rnu#+3>`4!=Vh?ak z!D>UnH_-kWN1q~F;dD_)@`<&G#vk5xUW>k|C_u^00sD%A+<)H4Dhu|@MPp{&1<`>m zhpiiLa$b`@B`1DHDnOC)ldU~*fAxkg7mhn(coCG3RWMe9$00H&kfJ;7u(7hbC;9Mr z<7*?3(VHxu+?Z#XjZ@7h8*u9L4UnmFkj&0MLfy|TX1^HS27e($A(#KGL08?~)MG`G zFC^L`wfUzdzt)-n&bwGa{{o;5-F{)OZ#Dz~dg(&C$bD@JjnS7knNTt6Ii`Z1a) zJJ8nIuPRCIyP4EBdP9WZAOd(^ z41w2=@a41f75(r#{k9n?SEK7ggJ{a}-m?ioFgru

s#v++&Yt`a@ut0KUKIh{wQ@ zJs1Q~XG0V`^e2Uk1yC?`iPDk=S3w*lND)nFMGwFL2=tAkqd3JbYCkzpQN`&OQrk*g zAE2uq`$88j%9UfWi$6l($JsKI{Mx0{*R`*9htws(MPdBvr<=mFFG{^wWG0p*w;-&lGTsTC{DL3UC*>cHj zDILFzFIUE%ab=moKdiB*Iq7`^lsSPRaDrys6H|6q75<EEOXg$o*g} zEz!QHjK?!p^5*CN;6zZ|pso<;qD#3b3=#7{ZIChJgSNNi-dc?mp<^?Dg3Xu^Dz?IA za6JR#HM@xAN0-{6U+{q)G5bY;V!R1FoKl+zII}`uk@sW zu;1*C*f9#Lm^BnGTDVoZ3Z~|1jHw<|fi=0eF_b^m`DF2Of&u4t(o%E5G$9=yzf-Al zUppJ~m|ATD1h-UOQpzLL5P;!)lvTw)59QQ17;)dt)$M5%8}}-|mO5k#3=KI^FAnZD6u#|_0tub2wy7z)a)nv&uE#Jzqp!w05Q(tY(vwDOk_g2{= z{vG8iu%M~a`rLVYfnZ!cb-k}N&uTrI?f<>b)tyOYQ}V*aVVx zdYQqi*&)jhxABgCQ{=>|6Bm!o(;yt8_O@I;s%S@!745LY@QmTrL?^*mFW*48Nb{?x z?rsu;lF=TQf#dhC0_^Qn{cz-)cqF;r^<~o?8e0wqNCs(zAU!j*00KWhH(>1s^1X@M zzJ!enLa5`8fMbA>pxD)MltHW)`kpD@p9x4K96gwi9HV42*5vV4#Izw&kFa{=&Hm^D z<4w5u<=0)X+=dXXb{o9f?NEsHX>?m>kp)|kC!Tzi7*lR-h@`b{gHOjp!LPwKLk@nz z9=?>SGtA8q@;dr>g900J#YP@UQ>o& g#O3NJ`X}4ppVU80)Wm(M=e@-dQo7={kPQs}2U(Qe5C8xG literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/large/jungle_tall_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/large/jungle_tall_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..6f0516a367818b6d9ab47da832a1bca5dda56998 GIT binary patch literal 1593 zcmV-92FCdxiwFP!000000L7d8Zre5##u;0pNXkjtV!&Qw*MIg9!`@)fJSFw!+J^09 zm#3fPiHUpi@qEY4E(HQq&d=Yu@{m-~_YI=EG{!FZbQu z@w><2Z^@+}_S;>*Kd$=2!1UeW=<2&}edvR$H{1R3{qy5t`!GCK$Nj_bG<(=R{igb@ zfBKqp+0FAz23xv)wzS+Xx1BIG@9ukd^+j<0u3N4)_mj%g*FS&RAAR@OAB0T!d388;o8_SO zWpHWu{4jjbZ(jOyx!_m2;J>(7L@QqI`q%%V>uMLgx{RN&D(wT{BiCP@^l$#@2K*Ka zM(2N8ZaI{xk-EG$!kWGo~u&XiRg`)^q45#*?@tgW_UL1dPmcw#9PDn5@HcXkliC9L@|m zlnjcCF%d8*hciPC8IyG^`C)xHCppXv%MW9G)+4o$Ovx6@&)kq7$<%(Lbm+B!g-p8FcmwqJQN66##>38Vo%q znNnP=#~70UBhM1qXEqNdQ}T!PA!D*F?c5R#Oqe*<82hf)}XU6=v)Iv#@=MV6(#@%oevq447OT>&f)@}lfjn8 zG?U-jxg{7FKQY?5r2!1;L&ijdtppgkH)VgcGg1HyS{F}dtfmU{J402K6e_$#H3Y=m8`5xW=Fp08^@k*3ZeHlYMT_nA?KStrAxNjEpM+rc~1f zIVX(wM(w=O00z}`WY~E_GH7l{2F(qoQ%y{5h+Vs1c)*~upJdS4kLhHez2Z*|m{OiK zoyMRO4LS)hGS4|K?JiRT2F+Eb699wO4%0~nofa^q9%JVp#zerN8O3y3z{t23fRS-! zf3&@z1x#t}XmdUQ2CdT@$M0y8L3^BJ&>9>WHtQsVW}RfvtYbRS;7|6M%{s}Ha>!;K z(}@Ous)^BNOAVM(Tx=dn2CY-36AerzhX$`h=>`{r!1Ey4Wb}x`jY5uT#0b^Q&t%bqo96P%^NTyVGc6X3W zsqPxnUSLZ*^Ctr{=I7S&Grwd|AC8RnzRm*%?J1H$dkWL3Cgyxkk@v3}Flg;arj#G{ zj*;nPf3!L84NL z!vkQ@+>i{KQH*H~wiX7Tb1jmg7VKUinNlsZxta`2_D6fS69I!_f8+SSag2;M$2?%r z8k9`QA5Y?9OqiI)prft#WMHx_c0ZL2nj4I1FR-Ppi)3J0z?6K}dcFk=%JW-8z21`8 rlYwagGuo;St3Ue;jfa+Z!_W2HHIL)}{n>8ZctrdQ1k01@wK)I)A&?x< literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/large/jungle_tall_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/large/jungle_tall_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..76ff0072fb64c323fa96e3587b107947fe07ef42 GIT binary patch literal 1796 zcmV+f2mAORiwFP!000000KJ>*avMbug~#68Tvl;_0I$LQ2M>F>adHSL47bahX}g_r2;s9B+8>s0rtsp;^=i7j zTfW|Y|91Dg9P*EM>)YvWdp&J-%(UEWL-%@lvzcP(R_nX%ua6%$>-*iuc6)cf`*ePP z`~Gj*Kc@Fz7Q!*1W-%P8e)L1VdJOg3{p#vw`srqUB?B)1yxxSfzm79#uZ8aLA+cwL zs!Mb1TAVhk*~O`n(rd{IbnzJIa(%g8ib&1!Y6@px#L&N9US6-RYOoKp`cI6?#sB=j zg*YwWP8$(2{&Kz9E?1X3+h4_ycYnUw{lT<4h2Ynx7W~o{JZYPoi+gk#ywg8j>i=8) zpP;9o7BGEiAHPQ5oVwn={lFa|cJbL6KU?oRoNx;L&o}GK-*;ab&i+ZExm|CX-Ce%_ zIfQpVx@Nb-+vRq8`1F*C1k=wlK`W|^73LC zZ%^C(Pv_id#lEAwB-4REj9+QYz88Dh>uf`;U_T6I=U}VkLSOc*>UX6J=88F&@MT3ce z(e}%#)lBHrSc$0BMC8usWGDBYs8z|JIjR@6F@n9245~wqNq|A?+!Hk>8L3k}dwVJw zEtaK|0i*4gRT~K~Qm3!IalkBqj%3ij56*rK1QQ7+5lr=SxTk0akjx9kb2M^~fYJOM z_Mkk&yIIS+zuK9x?u!mEvNn2xiGaBPdm$NFKh<+jc(!`E2Mnq)$;ds8gia!u>c^^$ zfnXwFv{+WZ4}d}a<>}N|)_pMmM(T9)IyO!{C#U8+z#z|_P9m5L7}=-Qv(-BtV9=gQ z<|WuKFIFO$3>cYv0SuZmlhYRiVB|AMBy?xr3v=p6HOB4E&ry=MxcQ;gne=N@aF z+>29(iC}6hZ)TGW>I=!pTCHb?bsn~WLA5FwREHi@V_9cr2N-nrN=Dl+>ltkTjEq%1 zTXon02K9wxP+xdD@d!V~=^e?SGr`k|fI+j(o*50DdS9M9eJUB`M=~FQ-tl6c&rC!3 zX`J4X45~xPNY9b*lZaTE&?$tUx<1wn&;SPYmt;^cdpZMPWL}Z*oQPPN&?$tUx<1yk zLkk$K#ts=Y+ju$&FzB53bPADIjpaRGNCwT=lF`t5~vgPzSjolG!=h&2*Am9w6$TEJ+v;l2OybTYvdBGyRgEC8eB?!8}? z44OA2qn+WFP9d0)@LcmcoLl4_*8v92Hj>fqcS|Q8;b%0M9x&*ggQpV#^Rc3{Fqm3r zXGW|RFle16BV#4Nd;*KwnmJGU6 zJe?jesOCK;60s8DITJdC@G}BNTW4>^mJFJ)J)Isf=&bXYNW@Bn=S=7n!p{g8G)L_j z>m7ar7_H{LdEV3M2|okDL?TuqJZD0u5Pn9$XfR>ob{a20!F(}z4zgs&OrD{1e1wah44HQI(2=ldAxat7zv#P;is;T_3Y3CMynr7Cjv(6Q}3BbGFtsuIt#*c&E0#Jk&HI0 zThBY0h*bciol%xf^ zi(uaciC~(U@y<-ipgGgii3F2~SosLg*14Jprp%0YZd^EhQ2>K#)zhi5tllYrL3POL m$XabT*MCok!^X?~uT2=#ha^}>GE## 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 Wt^CdKJ!i8$w)t;5f$&$%8UO$(i=;0A literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/small/jungle_small_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/jungle/small/jungle_small_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..eae5e04c4dc924d33af174a374c6d5ada3660742 GIT binary patch literal 704 zcmV;x0zds9iwFP!000000JWFvlA1seg&UX)l4O6SD$lXG{m4V4@&+klW;bqJumsKJ z=>y8JZ0}*FNsLvNV11rGJ$+FNq7~U)xpi&p3L?77NqsEu9bMgTD!1O3H|>XGcU4gF zXk{BEbPWrcf88`~ zS*;x<*Etp4`%U+TtNzZqxMLnUzx*K$^8X{!eC30YmY3soxE#v`-&|Vo-K7QJURv-y z7F?0}vaQ$On|>wME6R3tlXaK`$;pBymU&k8WykMgw-_{jk?mshOroI(AgS>5MqVFa}>0nc8?2AFZzPnc(*$6`!6 zFiC*P0P`Hwi!;lhPP>ayKYrvrW9MiD^ol#5RIka1}K*tDle5}y^Nr&8~bNT}4 za0bnLj7bJ24a7rIq+G8;tBhaw|9iLYw@EqC+M!3I#9xq3--Y@o+_CKvQ!E~qJIke`^25$L1?qXIe^U{EbMgK8SnF#=38!aCT=6hDcHD^y{26x3!d#Xhm*YZG6}Hl8BxZq~BGyo-S_JwZGg|*WKGge^pZX ze%EaMu3P)IXMELmWUi}C>kBePb+^=;i!_I;kgcrC&XkBR-2)L5FiwOF*nr_05Z!bwGyp8eim zUmbkQB#Wzc+g0_Yr|fw_W&eHKf8pzYJevz!f3dXnKb@rc)?fZV>kAkCRowaffUlPh z_yz}Dk^Q)7F2A(HF@IQ*+BU7~FNe3LUoWNl3U*cJk9YSObz<~{$q2`pGC5-mU{oNE znJ^kKS%T**!82#L=aG^8eFHoZ+)N#)m z%9M=BPRz7l4+4H-Jyn1~vpO&?qNPv1b758u>jl@cfI*%O>jh^FV7MO(81Bah6M~d!v~H8t$~T18yYa^OpxN_Cnk1oD8Qgza0czE6vxi+ z6PtMh7__IB&FZ6omX7SQ0SwPQ8|NdUl>>%rDZqqgL)r_@gz94x>JvLxbpm4mgZ3*m zj&$}qVJ|FTQ0`8cRSg)lr&1gX7@n5{OvtlJsE@Q?5@Qpz9AMC1IFk32Goies=OAa$ z870MW34YGc%LLCc#sDVNi&z{7m{6T#XM#>(3}8aJPj~qO#3_Usvw%UK3!z^IFsRN_ z90wRwpF-%D1q_d4j~?%z36Fu+N~n(o4DW>l4A&at@Z5E~{^3VxNNV`KqEO@x!*3c* QO~WJfCt-hDIk*-80B1#PmjD0& literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/forest/forest_oak_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/forest/forest_oak_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..2e0d9de61399e5601fadbdcc2c709fa30b2d105e GIT binary patch literal 585 zcmV-P0=E4hiwFP!000000IiqnZrd;ng=x#W*#8FXMYe8#_7FqgpztzvFp|bWY!q9c zzE)-x0e4KQLjnj%jL(M@$>U2$)RI5-hd9r%BO({5`QAT8`tURygRDBB6Eyt(jKs?T5AN#`; z|6A?urfQ#Ws`mA!YG1HgOYX;E-0i2-#(hiXG)`vunN~x8Kh}_4WafDJ_b*cwFtx(u zrRivlUB8{eT(qv33dFiBfw6$$v0Q;py)sr{3}ASytm6uFB*p>;)iAAEsi9TsYzr9o z4jri%Z(IRWvUjb9Y59VEd1ZbQ6BM1asw<`j`*N&boUwq}ignEx#`uC*^~#unoQ)8x z0SxK|XHcCroi{%Vj0a4!v3JeyoY~6R3N`eAv5X0T;W<}1UO`Wt&@Tg+tr?Sa{5$gT z1vBP_Shj$1fZ3W2NhfOsA!i2|H1jE*ig#+jpnErGcq|7Pbp7EB-&f8@^95(NX4MO^Y=Mp| z$j2Axgo1o>&OxZN2MpKA@r=MYz@TepP`qORgL=Uk)C*0=73g@tAYTO1{jM!$5(-b=*@(tNf{Zph@Ps1r5F8#ywaqJ(z9LMh+ zb)PTeIbNnArWxyh4vDH?MERB1?3LI*$Hd-hz71*WPlrg=n})jid72-@(?sr}Kc@KK zYVYo<_V%u7@9(Pi0jss-zaGcKV_M4iqa|}5lbJ7gxw!U=^_RrtSWTf8%i5&*;{b!^&%%`E4`?t`#*6LNDhzUOEGC2AzKzMemdt3mB9`&Y&EYnCwT| z^A<2@W|o*7t8_Nz49~MuFj+_1KZ{Pw@`H3*wl3tS#CX8C9UaFQn}**pLkqBc|F(w0 U^YV>6p4Kh&2T62u@8}Q!03drEq5uE@ literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/huge/oak_big_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/huge/oak_big_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..7f40e0b822be2dbc20a0ddf793764b24728ce45d GIT binary patch literal 1702 zcmV;X23h$ZiwFP!000000JWQIjvGZ3h0E^l>U#759zZ+=r2gWJQU-IdyNY6|_2yZ3YW;{E=4etg}% zJ^gUlz5Ds;@LLJxr`N;F{Ce8Y$1}G3V}A^rKSO9g@+JJpy!$X8;iuBC`{QZ%{5Xfr zcPW(f{~yo)F+ZQe@OJlfod2`hw|7hxDk2TWa=LD4BI6BVWs25p`kI{nIw4&Sx2sD#0_x|K;+OfT4z9A~QO zjD$g(+ge#!E3Zsd_9v~(q%fH<(z#EHUrpyhVTNV>GiU7c7IH_@(ef)2hQ*Ji)7H{* zFcJpyYjo%&!m#{82KT31ncm@-Rudv&2Ei{)rw|5xuMV3h!l3Wl^`q6XjxcCXPZ()` zu9+zKrLEQMiOO0%VMgIhubCv+9F_W09rjFwk^ItXcuyE9ceFZR96Hru&qNrEp9jK7 zbu2mR&oz?;o3*|(5k{&BT2JW-gEkL@kf7%{uvqVoADNnWeCpnl* z7~G#i7~G%guz4bk6hGQ*(-VgEZe&GB>d@J)jCGHM469MdNc(fmu-c0Zt1Z@A z#i3IjI&FWnnYJSg>+i@&F>lSH7E=j>zE6Zf-<#dqoS6wD*{scnl`v>e+e2;E%7kHk z85t>ew6!L}NV%iUQ9WVMo`EoIwn0Y9FHI*q>?sbvDq*C&(B_{=7&f<9Od<@M>nxo@ z7%6@88$y#I-`S0 zgkkq%$gsY%#E?;2kdgLTn_EVQPP)M!tuGIRVc&(YbVdi02*di|g|Y6VkdgM;x{pEz zeIFe<#lbY2toLZhNHK5S5g{YlY~2xA_H0*1yZ`A3Bl)h)00Uv9ys+jLOQ$$=noZWc zfs7QZ*1Um?6swv}a@dm{epSLq=dQi`>j@*ByEX$v!bmkr(@74U;;^|AhTYd8BlT15 zPG%&G)CaA1nwCx_4BFh*vhIkMdm2t7*<=5VfFdK zSnqC-VfS^&Ncpby-0aXP4tweiep&B;kYRlX8P<0!o#e156NbGvM@Bk#tM4Eqox63P zfQ*zonoe@)WQR`kOY2_)Vb~o$GHh1&>S*sKhE=OGVhkBJYc2N|Mh!=X^;1h{Bn&%K zWF(udeu@n1rpNWQR_1*xc;VYIt@q!)muy z>-v?k>W{@l!mygK^f_bRK!%-{rIQ>wnJ}z>rC`?tWY}|orIQK6>IO3Gd3jmSu|L*5 z5;D?SS}rHTuxBDNxcpeGE&dbti zera(S2_yNX9y;81_4B$gtnV%Z}VZhJE+D81XZ#OmygI-(eUD w!_J}@=QR?BorPBi&#OP~Kh6z$6Fv=GZ@Mskxcs{9;dycGZ#$&at(-mp05f}E{{R30 literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/huge/oak_big_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/huge/oak_big_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..63982e39038353732f5eff5fd23b7303c2a015c5 GIT binary patch literal 1267 zcmV{@ z!|5O6&Cv~4+v~&l-)irkRPF7Ps=blbtV{jt{I@*(x6iMibOl~;2{T(>vYj zWp?Y^{b7CDh0`m#KRU1Cs+A4&>xGG_AeI9KZd>#aU2Ers#r^v7x)cmQ0!Pqy1>BSxl zu?Iu!L5RH)2K~8cdrg@a$e=%-FzC-f80?wBpc4s$IZXyzl`y@WO1<~drXi=vkke$y zX)^e%_4&D<8G1q%e6ED)`6Hd99&6Fyvos3^!m!z`=_JBn?Ac(e7;II-u=$P*o4K=| zGINn(XGo1ngkke*p1Z<+_RVi)A0mUf2nHq+2K`BA=y;+3WWum>3o>l(XgX~TrTG;I z!{#6|tll+SEnm|7YJN*|#}kJ2Ok~&@Kw~0ddO4MtN*FdzCr0Ywo-n<2mS##M4BnfH zL8lu0*_;uVG%p6i^mdUzmAFD_~BB*I|q*`QMi!)C$6NPRvL zhMf=R*`w4lGOSKDokSScTjqVHoL7;-{Syp2(ZD3au=55oY+h*0V{C@?(e7W!^kSEE zlEI&1uvHDVHU?WQwxHBIGORx%!|v3`upWgB#@_5ncNtHZp3lM3`QW(&pF1NHZlHm|@9>G%x(Z zNPTr649mNwQw%!IAMN~%47=|j!}f;8RD-SNv-Ug+ncf;oy)GMcib1CubXr_mKSZXN z3+;Y^4C_CdPBr+`e3s7YfiSEmAj5isrc(?$)u7YjlFsUZFul06yMv~a4LXG|y}hc< zl!?*$Dl+UGg$%3H`5BOMPeF#=Q}!w{ ztREuN^GEwEKx3NE(it)lhV?OIdU=<6R3r@B^T@Co)O4CZ+A{}aSWnQHLYQ8@wB9l? zS}i9-eIdi@3mNuVEi&x+b5d$25Qf#MrjrQ6&Y8%t=d_xRUl{F-giJ4AQcs8mon-JQ z6Nb$TYh2nhSdEE<>E%??$p)R~kJei>CKHDB`H9hbi^e1alh4r6zIUN9Vd12{5edU= zA;Z4+m6aL{gkil-(@BKs`7C|bUko}eE~&l(VOZ^GI*BlB{~*Kqp{CP()}GrS)60dV zlMOnBFzi|PyziCs4>G+qwCG5mDa6HZY5#Q(5vP!W+U$vo8WfiB*gb(nAKMR(uU%jZ@5R#El1 zZ8u@tH6iv`{nB9csUuGgOre9OWOn?q& zxK43k+z~S!iFM2rBe7h@Ptx&1to?O{v4G)zoI+=asqkZjy>o`|-2=vq__2z;=W6Nz z!~G1bVqdwMI)#o04Byu@v5Nd?`o}AJBd3#Anek&%PiGjXU2PG>kbj~EL)bB52?G{wv46o5hJ24~Q@!5NfiNyjVv4Dp0oa0bi#(n+y$J!S!ea_CrZ zpxNOJnjOxd-f%+QEnvp-oU4}w%vheiP%kg!*&LW$4lQ7iXV3P&(=oC64hbp`YgUl| U*nM-g>-~uR00Cozk~j|l0MQ(5vP!W+U$vo8WfiB*gb(nAKMR(uU%jZ@5R#El1 zZ8u@tH6iv`{nB9csUuGgOre9OWOn?q& zxK43k+z~S!iFM2rBe7h@Ptx&1to?O{v4G)zoI+=asqkZjy>o`|-2=vq__2z;=W6Nz z!~G1bVqdwMI)#o04Byu@v5Nd?`o}AJBd3#Anek&%PiGjXU2PG>kbj~EL)bB52?G{wv46o5hJ24~Q@!5NfiNyjVv4Dp0oa0bi#(n+y$J!S!ea_CrZ zpxNOJnjOxd-f%+QEnvp-oU4}w%vheiP%kg!*&LW$4lQ7iXV3P&(=oC64hbp`YgUl| U*nM-g>-~uR00Cozk~j|l0Muof0e zY&@tsoVj<3Qpsu?eLg<->1lgoIup*rG0k)8jWHn_`*C21b4-+hiM*8`(&Co(e&5Pr|SplG#hs} z9H;c(YOimq_Ufi;Z*Qvh?xt$*xmsuZm*cpA1*Rp;4j4_cSOIbrP~rKZTRp4W7C*^@FC$gnv@rc?`UM`=2-!cVg?Ug+l?VM@I!?F~y9 z^y3M`&NiA(tnicL3Kg>~?I=eW%ujfRjz`X@r&S-p^9_0n|m4tiQIJSSMfusKDBK{j#k{@aJh6


SGvdjM3hd{gm3dO7<+NP?b3V3=_ zh$A!{d%P zf2-QBZ&mw=)iz}BcjNXcUJ}3GkUEZ0%|9=Pp^uMNWM-Kuy#M>kGyUqv^&%T05`H^OjGr1g=bR1xEH7(7K zu3$_BV*!)%Q<^Wm#A^gU5|cP-204SC{UjX=7&NC6(=Lp(?{Eg~-JHpJmY(Y*9bdtu zbxE_#88pkBL36wQXiewG*u7&JSaK{b__ltXDx;Y`ktq~ll)`TJ>NG-GreUPFR5%i_EucfP!j RkB9d(z5(_|(Q*b6002$s732T_ literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/large/oak_5.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/large/oak_5.nbt new file mode 100644 index 0000000000000000000000000000000000000000..0d95ab0768bd400f929b0863b06081789e2bb9fa GIT binary patch literal 1089 zcmV-H1it$piwFP!000000IizaYTPgohGpyMy({!Zdc5dEl)gdPrcPP1$%c3dY2Une zwOuQOzrSWCB>|J{^7+?{GzUp!=iK1(?fy8P&*RQHSBeXt_t&F)b$z%QFYfl2=TE2o z)z{;y6!`9yZQTR z{$jj2yLh=jPUC;8y?;@)cQ2~;_C?j+$ZFPwd-!hyK5_#Fm%bfO7gy5~N52Q>Z%>n- z|6HEv{{BSo;w*C>*H8a4-TVXtBk6?s>D~I_k?9&t_k`)mSjeDGUmroo8<;>C4@@yI z-Ntxfte!Ba;|)w8OlZvEQzinF4NNhNH5hEJz6XQNQXD42q=sJ-QwW2;SG%L26AVlw zOv|q%FqtrDbDigGh%t#tglWZ37S^;p!=7Tu(<10(0~5F3QjO}j-%^bVgh8EX&`E@0 zej$VN+*R9^`hyHx3r!~&m}nR)5eEIrgkd#G)2Z{L)hJ|8Cm3`RVOZ`UgZW+zIzy!juy@aNd3_AH4_6&xxBqnT+)SEJ4u=aKv^R!=NGSA4cy*e_i z&LhM6m&O#sSZ-r_!LN=mtiL0}dK@xrFM$lkPcrCagHAE%RNtjDNF)r)Wn@@hXgbB9 zGZ2Q=v2{I^8ifqohaAhMiM1ooukD5C-FCAPnYL^(z_H zG#S=Z>y^l~;xHL%g49m~VOqJ94KXI^)Hy5$o27G;CrryPt)FU4A`IIDEX*MI4x@yOp|%47<~_>PUGS3B&fr$guUwO727hlL*tALn(J6Vc5DO!}e^* zu>8sp^R4;O?ie+lNSKyiQVu@)W*AHA-H9;loKlont+iF#A0or@LeoivVfDEY&7!-xz7V z0%6#`PSdGlNoyJmOe75Yo(O~MRmYO*PbADTR+tXI#tQA~<6XXA{(t6lvtoY$l0{1M Hg(Uz0O3S=%1`Py;k-kNB+{oD_KIz(T<)N&iNkhzcxy3+U3c~cjJ=pjYk)m>Mh+Cv>p<>=qG!&PnfSG7Izwha2M zvc<{ugTKol8tX*&*xw4jmlRN)#?$cjmq|uO&6p@)^iTuh850ML92v3VQ4AA$G{Xoc z^Oi@9j9keh87A~7h6(d%hT-PC?%`n`zZ+Ya%%;zH)@`U>(UHMHDlue5YB$xglxAlPi0AyOCr&Cb8p|Eji&t5u+0 zI@28OY}H)_VlY-rk8?=)x=;X4GLEiq-e^Lxam~bxji(0Aw5F<>?XTjJn!SE+!Vm)(pFkqx@KgfS97O_%5ah}gW(_d}; M1j*JFjVuNL0F1|wE&u=k literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_small_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_small_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..39338cbfba3f0b3871871972d5f848fce880d820 GIT binary patch literal 336 zcmV-W0k8faiwFP!000000IihUPJ}QJhKHq74tFL#gts29dP&ZnXXxqNlNqe|L0IB0Sd@^(~xsi0s!Wqp3I)$YG1ch4QA^eJF|Ofy7v;w=b`Iq za5dQxGat2uXMb#d{ zY6a-mrmJ>#YWz(BY2Vp&Tuy=T^QaVXlyNk_{biB}W;3l9Ou`uT%cvCw0}S`lXXxMx zbBqPE3FOR~G~mSxd>0sCLODx1-b<=WVswDv i_Yi#_nU1pc7kMc2kwSKu{_R~mkNz9!m1wO}2LJ%!I-u(S literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_small_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/oak/small/oak_small_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..441a18178bbb358fd200d297caa11d0721f49c3f GIT binary patch literal 327 zcmV-N0l5AjiwFP!000000Ik&RPQxG+2H?}dfwuolyaTVmk6&Jd@eT~uGt;aqO(Dzl z>TTzk4KiTHCC#?x;XUu5Sq)Hu98^cvQVjs21pcJ91XtU(r)E@Z`>56CrPJ><)X$?H zXtXVviK%^Sp!fiAORl{oq;_PyRpnc2tm+$r;J0s)zX3(AM!tg*J%y?FP6Y7&PE(arh&^>1hgJ%qn=26k8ru2X%?CJmP(kq= zW|484M6nQP!NA{6%(H1oWhCvfnfwx)OZXSKV3RlDWYYS1se zZFg>I)lCgzv`);g%TVz7NC9e=@p%6F$rK4@8JlH%V+uxxG8@9^44n{5M06Cx2*Q*M zvldHc)D`QGW|*~nWTdW;kBTsoVQ4JHFl#yI=;#Qe8HUSQ5Z}32l;Qd#6241@;r4t= zM`tjXn2Ehg8O{sJtko6U8;W7p>Wb-<3`5UF$VVs64rRDIA*FMPm*`K5QNPfMd7&7F z^E9OsVx>H#4CiUCPU@VhjM{SI|iuwO1uThSiuUPi z7ca7=XLl@-NLCW-^O@b5GqXucv?G7&4sje~OGG}9x$Yh!y?^MBaewaa#&79#{=JXi z55upP+MDxmis!M9X(GEneWK=xC_nY#?NhOPjEOxMe)ehXj{8W>RiJjdpQan}IFh~V z4k`YR;Kxe~e!8^amrDzNy|mzGEVv{0<1p;+)9jSn9qH4M^z?DQ4E=o>O?HwQ4DsgZ3`zIKUv!+YNQ+4Dyd< zwLm^}Lhm#SBYB=PsJ}Ra`b&yM3!^uzxmGU)7*sFLlyX?p$?K8&lryDR>zbNE4%dt= z#BzdvI8$1WtHjEC_mz4nz@R?mOeu#!m_MF*p65_4jH)miFb?>KGiYvj!K)51sJ|qg zyf5io<_zk0&hS`%gHGmZXNm(1*YSYiwa9y~ojV#Z=q%W(Bl$T{eb%Xkk$xv|2A!vz zK{GRuJfk>+&M!&F0S3+aAk@794Dvi@(0mT8rs(&^j_NZ(EljNz8Zf0;y5i?%VQM|7 z0E7IqRY%HmyTN0vP;)Ylf5)2- F001Y~8uS1F literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/huangshan_pine_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/huangshan_pine_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..cbfaff2d622f78deabbbeeb1da0a9aebf5acb13d GIT binary patch literal 446 zcmV;v0YUyBiwFP!000000Iil=Zi6roMF;!>zAJSdeSY+_3#ht-ic+USC4jISwOxH7 z!Byl8_EaH~r34+{JH9iqC;)O$ec5PhbpZe>1O8NQHC$}#R#!v0u@9ynJ}dpwbngWe z&qLSi!PeT0WcgJaNWKBwuFKP1y4-2wZOqeF5k?T0X{FOT zViJ#01SUh6bis=qF>Jvr#AFEL*CS>oboOLsWT560VSF!%Fn-N^W>mmSB8>0lbh0Cc zBRMlKAA#ujj6~{*F^b^z=NZu92!qXYjFALJ5$Z{uC*fQopd$#3BrvX?(2OyJS?YU8 zM-X)6KfFTooX%XR7mUDg0wWLxo1d7Dt2xv!hA`OO%e@SjYL4}5f8Ry&+4nW#lzs$?@#;Xee0M+%=)&Kwi literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/huangshan_pine_3.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/huangshan_pine_3.nbt new file mode 100644 index 0000000000000000000000000000000000000000..53d7c1bf237f8342b2327900c85ab3e314bc43ad GIT binary patch literal 478 zcmV<40U`b$iwFP!000000IinWPJ=)Yh6k3}1+Gne4i8s8gy|a?#H}GxAS^cO(+jn0 zn(44R#x`k6$+!PM%qf%r1(=~~?KIgE05FH_vD#U<+BKc6$7(x0xM6&+?Ni&omQZfS zez4=zST~c^N8=#*1n{S>&rjKEZyh<9zcg;Dy4pf=n?pI@ck>O~O@P~~b@o4kHS1Wda83YeJA;rfJ0R+v+- zlrYFJYdVs6w7@IGcpVdn2L;S;9#0rWnAH0P7{xGLeqsy?yfnjPEAfPCnh6+W7#g!? z7#g!-7#eeOWKd8G6y!P7hu%vEbW(<)`B4l*y->h-FC*{@F(_a(!>r9kF7V0)y^#z0 zxrp>LlK$axh=O{>7-Sgg<+)hBmS+p?mgLBUW;taTZvRk*n~Ru^_X^FsVi<0>#B`8h z*5V261jR6G&mPi2fsX!zS7^5&!*FvNW4umm-YG--*$8yJS7;|_0b>}3+r1_-7m8uH z-Y}6q%zR9d-k=QCF#;X$WdvTK{j3>=%d=D`R13o}H0GRPXuZ4+-Se4izO09eqgbiH U?3Z`%zB`2U4QxlyJpl~>08B*X5C8xG literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/scots_pine_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/scots_pine_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..ae5ee8c11dc18169468b04df69fc7630eebf4040 GIT binary patch literal 654 zcmV;90&)ExiwFP!000000Iir^Zrd;rgei%CQk*^&=sA++DTfGpgFQSSUKRSM5Xpu9`w*voyAGy}Gz+^vM zHYl^yA1;R+;{|>)$K{7IXm6nm$~nO7{ixT4nwdx~N+A|x# zpnD2s(0PN?aRNWNmKS8hGvA9evs%D3%-UNf^9C?z?NXgm<{e;AHYkJU-je*FGYMtT zUdQP;fll_rWrH#(8=OvJ(($}vYITDAPzLS29ODGFvY%2`4PfX!*9sUXsMQ>qQl2$n zmON{LXYSnFG2aUV7<5nRm^>$)3uazcz+|0LpRIs#fLW@=of9a7W_ls$Gi6YpIh|aK zJ0DVJXcpp^1nb)UxDpx)eWc42)SZ|0BF z;rMeMzHIm32Q_>??hoNOZ^E>Y(#J) zJ}n=F-K@;bxShgt1h09)GtPDYUr#+e$NamO?#BC<7W{w(du6|F_v;^19HQU7(ue(| zmtW(zQGdUTR%Vfz!|AW5OjE(M6^yQ6x(eo`^@wQ*(`7A&F@R|cI)-5kV321k)XEH} zV*yjru?%AYgZkryKDUW6LKc+Kg<9O%bz(Tr9biy@Bqr-PrdI5qkDm)Yj7(dh(=vWs zg-(|kE-wQZG#ixZ3Vt{pU*V@sjC4;aL;YC5lzPb3@_;dfX%fTDdFC5r^FtXl^O8=R7%q1W7_?g?#sCKG!(PbL z0H$LAeh?79C*F&XjZ< zU`qYr^0I)TIu0->uUG2doj@6#7;c9fz#z|*L7t^r*$=lj3}9&PZDP1AG+^ktvJRJ* z1`M5_?hG9-%qnHjPFRuLQ5KXzxvwg6mly{al<7*y)B=X?1P2(jdntovV3}Dc1k#t;zPWH^rbM$jjF{>BCjM)lCCx&}J>;O~p%*~;# zU~(;PHVj}&XXhAKQ7ig+O?qfSEy|R1xIP=epmRx#tEiGPEI@^O7z_53ng*(LZK zw{I_*rh;h&M(>QhVH(01z)&4m!DJmL#M*MYVOo&$_GTCZ7#hn0Mi+FX+9k$kR_K=@ zwbOjsoiW1tP=?l?b-2E3z@VN=Isq`IkdLdVOWHq@j;qk|fI)p<8PCM>fGNcaLhYL` zFl%T5gZjRC2cx3_gXW7eXuc>z_p__e$vL+bIdi^|bvWN>cII|=Y(d8``53^Ud8bUN zc3ZJGBoCJuTM^4uFuo#IsL;u^yNbSZ^KL72JYZUFr{_o zJj(2SmAr*AsIHZfd_!U^U}#;gLML;ru;(d*_B>_K{-F%*SF2!c8)y17{0$`B;tXh({tPD4U7BFZoB%NHF^!tJ`rC8hy=2+4UQl^v-r{e&FdP*LMpT6pJ#YSkgI@ lGH4$Lu+Aop*O;MRJ)2Ocm496SnVC*oxWAqFY2j59007{YMsolF literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/scots_pine_small_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/scots_pine_small_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..dcd6a0c6de1c949348443319419a8d84435df52a GIT binary patch literal 622 zcmV-!0+Ib6iwFP!000000IitoYTG~%g=e)lS-%T?j$VJ{Axht%sP3kSIF|7$q3zRG zO0!@lXJ+Q?C()gG%yRrW<i^6Zp62f%P0^UQc5hZZoXUYtR5v0Xtp%L9hzu;@f3&zqJJ z>h1u;bv$4^$f0L-N3+8jR8vjI0S4ueGd!-)z)+bD!H)q9kE;U=>J84+JZpQ*le{-V z0}}zm&no$mX2$@g)*I4%S-{kCDCzhHKcPV3G1@{5XNJfT_hU zG5!^2X}!T2)Q7D)L8-+~$+H6tkE<|J?1kZ;U7r4=%j9hTA@r8wWRaam$dL>$O|93CRQco*!kGc`Ym(p4{?UloVPIA<4zuj9N7r$eOXeV~3lKd&d^X(4wv z9Ow8y!gse8zP+{Z&8>y6ZY_K*3p?^(j?>|O-tLsAj?8(Q&H8aW9Q}D&LvGb9@%Ojq zOam}&h4K5lQ5c)vE3r_$vtjA8pXo^V|S*#Vq78CHRFZ7Q}Zz32?A5fS=*;hVDfxC;9zV5I$3^Dy8EfT!6RZAP^MQ>tB?nHKOkfbl>q z20W=QZT=X6DfLvFhYs*~fZ>10S({N-U`oBvJRXe2mHp)eMv7JQq*&TL?*yh4OJf*_ z_4hXe$XVNi4q!YGivdr{r*^OA%;vH4_%kMGd*-nXq0SILZqG)h6Kx~s7kWHPsY4S0 E0Qr$D82|tP literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/stone_pine_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/pine/stone_pine_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..e952b2a95b917c74ed16210848bc7bd1453d1a51 GIT binary patch literal 671 zcmV;Q0$}|giwFP!000000Ik?-ZWA#S1z^wE*LK2RB-TOU7mGmb01d%K5or=-h6-2% zizZHERJq63r=pc2HPJW6kLyR9wJ|Fbj?*qJOIjOaBpUZ|x=ZHu-FBZgr|EY2Fdt7p zHtF;3@NI3@pH7EkIxX8YpXKy(JDcH`G3BW@PoGNDeVVHWXCX|PAFs@z1E$qy&6Ku+Qd0{|-5(*02d1ujAaWwn=h9YGasn`D?72PD zsYPw-90A6GT4bZ`9Scl7hmObzz|?(?z|?&nu4e4)_u8QSUK_MedpSbn1ftIo7<3+H zV-+(3)2gW@YO0-MLgaWNClEOin0j2RXUvZy)<*BH?BxhyJkeGlaw0JG{FL=0#QX@c zeuP**ni0Zy!USOIIV@w>?zj<{*4ohSI7b*E+VVtBAaWuwt+8wOparH?cg+aVmM3xo zkyHAk-OBQ-$>bLcYy;2 zt;1f9CvqY%ty*a3vjwJA3oSgpPIyM2duL|P2r%f(?B(QF{)$CA$BNO;4F?SBPtTN`-kH!d0hrdfwEg9PLH9+^ z1j3ZI9`B3#|1SZiHP4zUW7pQI0Mlx#&T;ehcY55=tGpPk3HSNCzQg_!^%tJ#zsGSE F008u7Qe^-D literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..d261f77d6d40642dfdb7e88c8b14ed815a80f3b1 GIT binary patch literal 1059 zcmV+;1l;={iwFP!000000IizqQWHTCg(oK2*_nt6g5jp`;O&nOVfhADjH{Ir5-Ph; z$g}&y3~rawo|)58tfD~b+tX*d&&-5CbS^l5F*|Fzu8GdMx^|=Yv-8I7pD&ipd_DWp zy=yPlm-FW1+3IU_@xyv`(X6{g({9Mw^`doy@0-WAf9mGX4$z>x4vxt$;I{mxOi1`vAy2wd)9k<&w6j5UU1c?v(@}toAUD` zxZ%aB9d5o%AKv}?=Z9{*QR$l7|J`M310y=ML8rtdoq@p*V?>AfVNB9_VDQ5j(P4fV zV?>AfVT_j#qQe-`VSX4R zI^hpy0GQjg85p)M#)uAE7h`VMWbnh70GJ1vTFf(J0$_%qRy3@OF`~mfGbR8=_b2U% ziSmQir5WUhF;c7K#{(u;i)HMg!82n-hxuWQ=!C-BYryEeoMyV??(TQ369F?)%mgrF z;CaW;L&gNa=!`|cpls|IW?^L_@%)JN(Dw}M4`Tvg&`dR>{Zu`}&M3{~JeL@$Rm!tB zF!dHXv9R_UFj^;Up)(XU(8lFh+FPOf{49!{((K-Jbv$eYS{BX%@-P z6GML(BRcG^+)W;c=fsRj#x$eX6#_kMmD#IQj5Jt z(hSOvW>9_@6AZP)Gs~D}a-Lb{nNHZk56iq}Q0A>VrK~2;`|j@DtGDd2#{AeautE7{d1IJ%S9XNiUGN$So_A{Dha-P{;$(Xo>A2w6Q z1i+x5sWqcLPXLqi%-%;zOzCd&fI;WHRi|_}B|pzxc`tjwAU{lJ+%xQs(+ujfW>5~9 zPBi!t&+M+$3_3S7gYwLnV5k+h@LamTJYdk7z?fjD6#Njg(kz6YzGDcxTl zFz5`|44TE1a%8YYiBuROaKhZp=Qv$riT1z2CeIm^rr?4n#Cbmmj?{$A=3$f z$*qg+LCv7o37SC}J9OncCH3dX&>zj9_nM9j>(UI$&k^yQm}AH9xMM?pG=uuAnOrTF zAI+frXa?oy*wAOqpnY*-*gGeNY-k3po#_a}&V*Bg=Tk!uHG}+Eb(rVpKxfo0ZW@8o dFTY--&qL?0(x2+AmM*>R`4a)ze}L&F005qa9bW(d literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..35f97bab00b190dc7ba2c152652efa8fee637cbe GIT binary patch literal 1292 zcmV+n1@rnJiwFP!000000IizgZWBiog~##E?937hDNtzN0^0uJ2akYw17y;rvKr#Z zYY^q>>rH2nb2u~ST5%*RO7!{MGxy%PJL~n}Typ)*>T2BW#=$vv9$oi#^=WiZKdrCF z%kApp?)7xD{d_sTzuNpgxZ&M)b2Dyt>v7tXtFPC8~rZ+G} zr#I-dm|16G@S~Wbqx>jl);TlyQB2WMeiReRZ=s`@qNDsMX4dHpeiY*~a~Nw;iKRR% zrpvE*z?@b5M1!A0W?Avm(uqg-New0t24nR{=nM@eW>3olrX#U#eT%gtgRv4}FjmNn z7i&ib>lF<;i7@DSAPjo;`4vQuA;WrJG0_k!5r*}lcY2>9Q}f(nq9IoC)7m?MFty%L z``wGZ)0h1t`~*Xj80#Y97_M3`EBRR1U@5~h}CRrg-h!WS)rj>@xQ zy26RNBUAIEa%hdE_8>B>7FM0s9`xCFYY%prQF{j&mP5sS??r>3R{way_=+FZV~Qz$ zRNaxO<)_tSMWp_pihl?YSIkD9B<)bi8njiRIW z@_bG@J77YEIXq|fBo;F4tThs4e}&nuncr!_YMVc3j9hTW$qo#IFJJTk1u6cdk#rTQ5ergLtn z#krvt$gmuq8`gykTbE+G%&576OwEtVGcwp?C6<~S$gqA^OwmzumOuT|0tbk@KZdio=2wU zS@k?JI1@@Nb$v&M&7a5a?)4&(wLgX)L&g&Z=X@Z{P|@)XCJ?5!F4aHCuv#dcNSK;u z^?4mKn8QREtVPj@qFxtZPuO$gq3-nNjx+KZ~0B%&0sg!}6?jN-T9xgbaJWtaM5&OMcX|W2;W< z+40Ogar*v!X4F|-F};C_gkk&iiEHn7PZ)L%M~3x}(uoE?i7>T1tDZ-u=2^`aWNLoY zY(b`0cZ-gCmh@EgMj#CHqjb8=sCps8>V*uem(qzx_-WNN5T=$NrSr_`x$(?UFJzb> zrPF0b+*X zhSePzR(Gq8I&VBTtR0z}j`D*HTbI%)v3i4N)vFer7h>%_VOZT?81jP*%a772u`KIS z*Y{std)E2PsOQVbJRs|`>fmhYruDb6K=VVQ-{#*-*MFJ6^1QjeUD|(1iu}KJFaQ9% CQwM#hbS%l!9yV40J-V5vKr#Z zHi-7=>!rRb9L}9{ttL_w#ro~JXYR+$IPs8DP33NRGwycdkW%XVw0OOIH>M}=R=4Bz zX8HT>)%I@l{(5|Sv;Jd9!<)_eZrtov<91IjKd!cE_SgQg&mlnIt`|X>`hJcyY+UyZ?fq-r+>flJav1O-T3LZFPRcxOs52NTFj&~3;0ot=_o&nnRFfm z{3ynBlpn=p`zdr3V>-%@VkVtMz>i`~NBL3Aq|*ibD8_V@AH`(*7CMSC9py(c*?b2W z~mu2k0MUGGSPc4S^m*hOJ96rlV@17}HUCR!o0{j;hx;BG1OC z94f|iawAp=Fs7r{u9*G^omLKe!r&}d!r;10r#6^M7@Qr`8N_^j8>p#bOs9yNBExd1 zm>P(+I554~e~@84h78uU5XM_qD^@+ibF1!|FkUTMOn*cy)ngCSmwOBAF=W_2|1jx& z9vLr|I+rIV2iDcvqfDm+bkyu1!+K0H=BL$TnJ}Io)$>ZnVzqj#5QgRXyVSmKWWum@ zDIJTY&PrrhA1WP-rFtG2FIG#(7}aBnF&%Z^cqH;;j5>EFM%^VI1=gh)i>2lQ8Ba&` z{Nq4BKMwR5GHjnm#*5XeMJ3F_iPfrCC5)FJRa3=O!l0jlFrFW^A0p$ey|q6Nguy(U zPHiyL-f}W|XfT;DY(G2+^eQrJA3F*3Gcv57k?}lt0ncjBR60Fj*j${5p09-QYO3xV z$aw2g^QClZz>j%WcM@bg&uRvhP7V06SnB?ajHlDOKlg-Tb9yS~s}P38LdJ`w<`fy0 zA7of>oQnC%gz;jvX3%uh3?kDxI_kVY#`COZ`7|&W$gsIkIu=XyGcw+~)ceX)(es5c ztbe{w`iw)y(@|#}GOQL#rzZ@0UK|)TJIJt_BEx1`>6o9^e07BJa;VN+rPBxeSS&TC z$asF#d?DlMv}R}W{6zFmcVN_g9vS9G=~yh4XJlBOl};s0c4D>eNQE%ChgZTBat}ua z{g_T^#2N^LvCMO;$1GM2#Hx*0rZY5{DbG)#4_Tg(K|kGrS&05YhV@TWr`2PHFzC4_ z4CcqyrS1vHcz#+;ABa_t@YC84D`D6^|1|0QJTh$mL5A(CN~a#-NBuuVF_kc0O@}~_ zsr%0v%om%}Gcgy1FswJu#9S1@uwGR<-GNcPij23e)_uMY=+q~5@dDq+y` zKp3xHYQB(Re;-4}>qGV40U0kpO6MHrg549&MNJD~SU*P@Ra0cx`E@R8Q3%6os&p!0 zygaLVA>*xG?U@s!{!W4n%i%@R`xr9pjJpW*4>Iijd=aQSGOQNJuv#dc8t`MD)p?2x z>s4e}uSRuLA0oqYcp2!8%cSQW8Ft29ruP1v3B%?B88#P6$6~2@M~3BD=~Tjae$<|T zjJNid&cs|nz1aD6CF)fOZm+F6P|m*u=lXf0)BoVI*aY<@6n*? eLrp(UKj*1@m_B;HzD?7|a{d86J{|tkF#rIXE}G~7 literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_4.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_4.nbt new file mode 100644 index 0000000000000000000000000000000000000000..1b8af9bf3a1c2014d254158eafbd6b56cbace08a GIT binary patch literal 1011 zcmVb#kVxc?xx@epZXXA`iBqU_y$hJ;B+i)rwI)5 zw!Vp7&5rSUcFzUFOs7zOIrrSXXR|HT#&}~l^JUX@O>K-haHjY$ziG_z&0^JDZRgkB z`*yRvy=p!!*I#Q>f84G&&9+-K?T(z^FIqGCZp;&|x_{DD4}Y#Y`}eA5e%G|(Lic6S zcJtL$V3O|gAI?NAaY;Qr|`@e4;VR%s%O|5GR7&GDtaQd7~>R7 zti}3bOwg$no*Cl+GfmWDo*5H$av2MLW`^(OnWA^blsz-_TqJ6--Wd~W?T6<9Q-WHO zsX#5sSg?jnr|6k#z_^6xA>$LChfJODTxBxm03-YIfRTMg&o<+y0*suy1q|9dl97Ey zKaONfGALt=i8?H+jEOpRrY{Q^w1x*HhgHwy=57^?0}RUiftZ)fa|sxfAIYS8&oNF> z%L69Wm&@psfI&Hw49X8%H^;EOsaR5d2OwkA{#j- z^z}lKL&>08l0khj#w%(SJ;Smp8MKCyLGzLfx@R)RD{9pW&#^C-dC8#6GaaX3JYZ7m z%=X1glfUi@hI{3|eQV;{cQDD>p9-7}VF1VhtsOW_qMpAIYHgVLGuE%b{e@ z`e=1>=W_6KtjM!u&>44Z^7pzDFvzoHQ148~J;D#0yJXPZnT`WYY8Gs!l1Z%(+dC(U z^*JG)E5IN>OefSj6%5LuWYAeD8T8rwRICsB+;k>%(C6+mp(8)1SisBx^GY!LJEmwC h4^4!^ALn0(Urdd?3;*9=uT1!t(@&%0sTH#$004$j`Ktf` literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_5.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/huge/redwood_massive_5.nbt new file mode 100644 index 0000000000000000000000000000000000000000..b405fc5ac8e9143469966a8593a9476b749f4cb0 GIT binary patch literal 965 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 literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..f662868fd4c39e27c4bfcde113068e68202a8dfc GIT binary patch literal 932 zcmV;V16%wbiwFP!000000Iiv8P8&fGgvWSy=V6FLf|PUQ^(Tjja)W5&ERhvsTiyjF zxo7SZ51EmmshMg7NS46*TvOd$Jz5|b04SslT!H-zbo9^3YxB5B8p#1e_zTb`W zFG~E7lpRo!i>o|o@jfpxlg&)Rb9p;BIQRhVAhcQ`)`C&|#S)#+3 zti$|hm?rAoVP=AHfH?tv8bw`!a?*eqI7yHzj_FsMJRqCbquI;?iabm_(X zFed9TKa9ybo-ntyRr=iK^T;eDgXYedd=|^9WKe$?;}lFji_JnZCC_XYj`Z0A2JH@G zyyC1j_lV40GHC9SL9<|tSDY0To^xGnUXnp`XF5*7c)&={O=7&FF0ZJomP4!1$!7&c zHUjC7Ct0NC@^Dbuz4IWz1|U`&@7w#$-1vtUdGIXewxI%N?k2rO7p7a&?W7o+X3MS}L+C8I)C~ z699wewIp-5fI;(;OsPNYd3C9nsborhuIV_1PWE#*8*RZ)EkE`VI*SSuJ-;;d=R=#A z+OwYn4B9WIlh0zm%SZ-ggXwgMah3j9z?5pQ<YE6*}4T z-K=&6&$T>Tg-&G7MaEEFl0kJboh~t!c$N&xk7Q7Om`+gm$)25}hqW9!z?6E(@_a7x zBmKm)UXfYY#MEkcfI*(MI;=mhflf27{|p(5f0Vd5zE0=H-o*bG@3tmhW&Z#-5`m9a GAOHX-L)8=j literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..afa43f11db8376cda9f68fc8149691848b6f4bca GIT binary patch literal 921 zcmV;K17`dmiwFP!000000IivQZWA#O#hqr?Yj0CpN+r(0$6p)*aRamj7e%B=lwB&| zvRsATup^b>*>5UZX{9#$<-PIDv#q49bI~=2ZkvW7wa&RZxaxg(m)yEm|)rFHFxv>RM~+im;wKZ38GTJX(N3%9FV-K%1i&D#+ORIh)bnHUV(Vgz=){6oV_>{IM29i;%&?hi z2Cbbj;>G%`nVc8fUtaYP?Jv#bdT5zNTkxu7t`C@{Vk+?~Fg0M%x-_F}7O%!oGs}i% z(7YHUUMw4$$$6ES)48H%QGt3iqi4|oM)y$8GGFkj0E6bP88mmzpgc1s7`&urmLJWa znQ8{jlrh2JCH1h3X(m?>yT6!M1Wc~a(U1*x)@de}jS>?KUeQpqtgAKb!M32|0i){) z1|9J_C-p>E-lx^fG^A$Dpo}p_yh{100fREHnOr?A^UO=?VOiA-$|_@m!Ao@5-93E- ztP0ui2Bw-BcJF8gXsB7%#qK!Gp#7y8 zw7(b=3|=y?U|1JBH#C!57dtl?6AWI_P_wLy-Ibcj<&d2}j0py>_=uX>J*b)7nZWK! zi`Tj1`;;;D%&@(q8I&=`1cO&KGo`)b0h3!h%RJMO{*4+E0s%COCwdj=2 zx~ZNEL&g{r0E4n|VaSGNP&OD73|ubSu}t_Gi5sBRq9~_7&Hs34(s!!E8iD>W=eaf0Su~{>4+Dbg=Ww!m`(%?+FzH3 zJZmP`A2w5_BVH`?nn5~OhM8&x<@w4mFU_ENF&*(@ebx->vsH)n@Hxp(0~l0~RfpAl vZP2+Ud7hXTikZGosQS&%B+&E$A-65Gm{2sk|+ zGbO>}b-%JmBrA#ax$4#0ZObWy7~0+Prr+=T6hdgbP`zK?_Tky>deg7&me>1t!|v|e zs{eSi{gOiZaJSv{cl&ie9LVMO^$=!1La^1dJ7h27`%uwQOgeVD!JLOMJ@ z9G>Vm`%qsmZ-)MV48DG7gKr<&;H!r=`0}9*zTgI9Xg=L+S6>gW471A^=DY1MKQztw zXv6QHb{^`3nSFo!?{B7aFlMK7*eNliotZd?SnR=;SRgBqDy{ec< z7}T5ygRz^PSYQ%iuoer#VBM`2$uT?0(LW_-;qYa87KPbq2*WfV=d7Tob1>1t)b?A< zDKc2EmM~cN&S57y7}Km~85z{m5(axdI_y+t&!JiE4P@BfK!(k`Vxq&BX)eusLztXq zwWAah9llI+X}2_l$@NAo`05-?bofdR%@$W_M>T}W&1n)f9eY~MJ2GtE6;n@)nq_2I zy^vw`QcQICGCgWekzqZrnCS3jdejUelhdQ_*p;K_YmvihV${w=hSdTY7CSO5cEv=8 zujJ5dv2}%6t_YLMkJ``3Low0eD>*bMTHp?Qmq+4p&Tc_(~^eUKE&?FzBmuFwOK(cWh+X`E}GS;zEYi0vT2dWLO_6JEmEk zxyZ2DL59r^GHh=sJEmEk1<0^nhYZ_wiZNemuOgH4rS_`wWqQ=ELnc=*wd=gT)NWBs zGubN5PDdEl^R1)jkzsKm!*ZyY=Mq)eJU3=b z=s||*QA|9+mx^66kudD8g$(QGk%f5tGPJ7M-??)IC>Qs)~kv!U#fqQ$;DM-qJv2eJ*HXp1~RNSl$}VJTz*P*A2pvj zYKjbt{mfA>WLUkF9rLB~j10@OvJ(lDi%ZQHGAyohM-Gu;H9dFa2N{+hWygG}vl1DW zAFmyCo?bZm85wrQT|l1MK17CTMux?%?8FoFsJbJ=>aOfW!sKe9YKlxQKWcU^9r?Kw znp?s!J<87L>k1gwhsZEJR}edk>oM42pRXdr^gJPUs$u=JH)vd9PsiW$(B6;VE8A|u P_*I8recwY}e<%O|;X5MY literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_4.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/redwood/large/redwood_tall_4.nbt new file mode 100644 index 0000000000000000000000000000000000000000..a9b8c42934cf9a174cd04b51f3d943de6c700315 GIT binary patch literal 631 zcmV--0*L(|iwFP!000000IitqZqq;zMJM&g?z&LgQmM~TKK}3!h&MncY>`D8M_E(R z{^HT`7LOE$vF}tQQsg8$K6__o?L=va0=ZqgiNg>ZBC0)=AKSY~S9jeuuJ`Tj@FDH? zKi2W{rvKJZ^J(Aj;(q928p-x&muU4s#AjXqdsb}kV-hF&uU#72?K;xx##1wXp2iR2 zcA)CE-K6+G!Z*hjzCE_^-LZx5bzz|T%cfs{PkFGv0u{SH730hNtm)6AE~pwcLwtJo zH{%Qpdz`_u#AMIPkOwjBL3t39Jtu}dh+z-PgP59+z=Ig}pgf4lp3;y9G3-Hk5R*NX zArE5MgYqDz<|ptVhCL{cmGKA6N?FSpG9!jPC=X(Ck9|HKFel1dl96?>rv|l4M$3#m zUSVWr&f^SuP!AE~jcWzNOoJg0VzQ?JYXybT=H3|cAjT_<>`zb_ZKkJ&y($@PubvwA zs${g@bFsUfGL!G%v??kx^^TpHw!dVeCCj zBJ`sp+aWKXrOhg6uml$H(c`@O=phdQ=HGuXsitFxe4C9Wa70YJTtMS&}ev z&&U-<~>Cxma~lbG7K~2qkR2|3zYB8eE|L z;#z0HO(Y?d=5XelDF&!e47Q~Z$NJPu{@XxqcuQ z8uBH|$HoWS)dX=_!o+e9x!R-i`edoV@{c|Ey_O^+m2}1ONb0x$g^iwFP!000000IitoZqq;%gx9xS#|3^N@f_U#@DPYMKqhQaM4CjgseotU zVX=!>il*b8Q;|xR+UU#doSidi)y}z%3#Wb`$1!%!Ip4baV}BRj%e&nnZqNPg_+dDm ze{AFD{qbApx=-ihDW1n&944~=xf@*Z%enNfSC8Kn`};V+8{4nDVeAjv=!&!#O- z>4kV0U31&-hxk8&Z>}x)?%IN{uPykV3vQhMvOjLW4|A}8Hm*7yhidwEerosctEpTw z$&B&gr^ie&J>>;c08=eYm0@bal)y9w9Z#4Fm~vrigXcQqrzT9j;yI_|4SoV)aIWOJ z$@r-WgPAr49ZwjXD-Z_fDwf9^^74kfydf`d$SbGg4Ss@Qt~7hNV49WLgCX}|=vOfG zE2rb1;HS+nje)6``E#EMBy6vh=GJb5(|ZA;YpjhGn62yunZM zth$2?>kcxkJ4(kJ{3Oq6=aE^vXSMTIo!lOli4!}I4BL5R*3MFUhzy$x8FoKPr&t*E z8IBCwLuA+fngVOb!< zGF3YM34T-uky*P#i;n8o#*is8%#YGZXQ?cZVOc1hbe6gwWSF1MFjpsXuL;BM(5j>6 zx)I$;j5_Pa<$tHl%rnqo%yVGozt!tu_d8ye;>8?D<-+~^zvAQJLFz9i7{ibP7ytk^ CLvi8& literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..195d49488a3bbc235d603ac8d06ef81cc5dfcb84 GIT binary patch literal 409 zcmV;K0cQRmiwFP!000000Iil$PJ}QJhKFrSfoBuXp}zUx5sWu56}yJW0-<%Io?cjK zVrB<2%_#)pY??@fU010b!sJYVJA(Fe4!cy2;;UF{*eE})w3hv|mzBAC5vga3>0>c53I zw6KKysqO36FvoURf*Sgurmyp5;QON$m`M};>DM`vP1o)RlOaYgjM`uf!R!!|3p|!! z6k^oEXhF?-qaH&rIGg;GiKA55M;XIaL*81AYN2eSa4`$XpT<@5ndgnZ8udSKtG<&8m%usXZJZUekcg#?| zOFi67t>D?f4Ar~DQ=ojP|BFK4DG@V!bZ9<(f@aD5K4TTw^2VFmVaJG3t$&0$@E8i%0f({vd4{uu>k)eU9&K#2+GgNk|hs%W-YF<`wZ(xSXZUsGKhRV))axHFNn4$V{f?Q6J3o}$Msb{I@ s)K7^#RCdfzwJO4+L-*k`lqBiOEA~Ep+L7NFV$Z!R*`^{9gpO|1Ee$ z3vR$Zj&uJKj$^yofLi9D(&O z81;*vl1?M=lRfK#jwTrF#}EvjvlZwx0zcWa5zN^L=4=FWE-|^5E%-46L;0x%xnqXP zs}|&i87ePMC)eWUj2TKtF})iBlXbY>F+=sv>Ev2m@0g)7<(OQnv|~1N%pPi{_so?1 znA{uF7iOqkHG;e_L*>Qk<}Z=)Y$G literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_5.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/large/spruce_5.nbt new file mode 100644 index 0000000000000000000000000000000000000000..4521bd6fd24457e84cfd2834e262512383da5b99 GIT binary patch literal 347 zcmV-h0i^yPiwFP!000000Iii#PJ}QJh6mbG;Mv4e_~82HA&fUL6=w~R1w!jaJ-u{g zH=FLzj!}{ZXuke4^G}=B02S!bbm$z`03b_{4`zpOwQGB9CbM<-cAVZDeCmeR8tTVs z7;$nf+L<&Tt%d9p;JE7gauu3A+F)V%(pqQw1|hpEp`PdMJb}FfwKbi^zX)#rTkw_^ zte|-AhUU$B+iofl<6y=7>4$`$M+s1~jKjmKXU&*W3qtBmmwpJAefxUCuGEm zj*2jnVAkp?7&@;uo<}9)?Wl;ZZAAd%iHSd<27$ z1Vil~$>@8?P_Y!F?>G|z?zzMV007#(s;vM3 literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/small/spruce_small_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/small/spruce_small_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..2a8ba15bff3339a00e798504189789a82a03202a GIT binary patch literal 308 zcmV-40n7d$iwFP!000000IihUO2jY_hX0zRr)$AWpTWbG4Ms)W4Pd&2p??Wq~{&fVEzd~4{jGcPsjhtUi) zx|Xab^?PfPeE@US#c>ts16fvBKDXBCz9D4SC2GHK{RZ_8YNtC({}EoDT6lA6;cZ-4 zA%E&j^J>E!E-Q$^Sm9s8QuuzPfbtqg)7NiC`jv(EA54a04`hRvuCSk&cw2BdFHd$a;XRt>qjKaR3zl$Oe|NbXKSXMiDj1w^OCHe zCC}C@B<2%_#)pY??@fU010b!sJYVJA(Fe4!cy2;;UF{*eE})w3hv|mzBAC5vga3>0>c53I zw6KKysqO36FvoURf*Sgurmyp5;QON$m`M};>DM`vP1o)RlOaYgjM`uf!R!!|3p|!! z6k^oEXhF?-qaH&rIGg;GiKA55M;XIaL*81AYN2eSa4`$XpT<@5ndgnZ8udSKtG<&8m%usXZJZUekcg#?| zOFi67t>D?f4Ar~DQ=ojP|BFK4DG@V!bZ9<(f@aD5K4TTwg-z_p3*;GKHsLm1z{RGc+L76`2y_33R< zyqFyrHcHX}%{S*vJ42}eO0a`#F$AmtKoy|w-2vg^&~#XjZXfRaFuv9J*!C|KR1af6 z;20Y8k#z5khwKAjj=DUJLU%+@0*mLy2iMgI*>wR`ocD18y8wFcT91Dby!mgzTVAjP z^VIhBt54f@RYE@WK97%SX!yQ#4m!#NoZe2EOkh+TJHlj)4rMs6#F!NwHQxmO~o@UX=un0`H>|LS>W7iMB*vVfV^(@2F@?}SGh>8) jE{RTJly5$9hLT7+|BD>#G5sU@Zc6(LGi*vQSO)+A4V#>h literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/small/spruce_small_4.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/spruce/small/spruce_small_4.nbt new file mode 100644 index 0000000000000000000000000000000000000000..9a1f793b52b221bc9ebdbefea37cc33e00662a4f GIT binary patch literal 402 zcmV;D0d4*tiwFP!000000Iil$PJ}QJhKDU}foBuXq5HOnFy6paoHaxi2(26S^wL$# zW_Dm|l!T;^um4Q{nKrEeO0a`#F$AmtATJ=la|eX0L(^eBx_#LBVSKIeq3xe5sP4yp zz%exFC(^w&9422t`cfN z8Lk$oI%^&phT(F~g`WT6gndC7&S%QdoM$bm$0)=3LmAFzNoS5F^)qES9W8ih7>0|b z6V^o;ZZA`2DVEe@lv&bQ+sk_HSE24E!5_+SwJ-_&LmAHJR2}K8v%+T!UAeDaIlUY{{+AbhSn6ejMeFjcM{7osOFhY zSNd-g@rbuW)Q|Tko9Gmq2kBW83jMZ za}es#+n*y~LdA0sJO{_~BzP|K937ninA*5JCIKc^#vYxVPJlss_`)P%oqD|~1E!V> zFV+N@nrE+nwScjHI>6XD>;Y5DsmEl&&j^^>`eJ?1GPS+V>&rc0?EFN5NkXhFcrL!Z zp4$PY)|)2B&miaofr&z_B3m6-#1B~rWy}%^EpgoiMDdY0`dkdIa9R?@X02u2z z2s%+@3rCkr~oH#@g5rUy)|r?9gP(@BC(7IezE*crew==~|vDeIJ- z0oMBqqGK5}uJ!CfYLzjAz(j$$&7@7oV6xzO6qrevpE8GD?`{E8n;+KSnNAXPMnPu+Os#&r{@wz{ z`soCnBrq8;wY;;l&BA#7v;&MCS1&Mw5Gx2eQP4>OlL1qkA9lZCnc6)jn-(7IqcgTMs9)W+rM-116l z%rdonu`>W;qTnY9%q<={^IHb3WyTDELHiHW$%4)(Fq05#w=v#(;Q=t{Ucxf9oO=B< z2|5`t_WoxSm`R9L=7;r4%hdJ+PbUBd^*9zQ39+)EGrnM4>@yC=1c8Zwsf~;E?uGIC zatD~&9`5N30uumJ+rt&lli6rFp*tlvK%eP{h#f$Y7E34lR;aJ}OY{IL6Z zrV|96_yRwy53V^AbXXs>45~4vlLcm1#xMBsdeZ=yS{<_f#r$MJXB3!8h*jptyAy5! zQ=3C?pC1G!00#9hrjrHFqreo;UM~oMspXx0*1s@ppJz-2Ol@53J(^`|u@oKez25qL ziiY1quICcczZla42F;IU(4AExYp?~3JwtW^(+jZ%z@S_(ri_c7S1nV^1zYcoiGrUp zcJDK(63bf`h4Jcn{r(H7pB^x%elp2L3m7!djOkwxi{*VJI$ee7&WDe4f$p164eE=w Y3GbIbqB%Y_%U8|+2KVQ(u3$9)06ZqY5dZ)H literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/willow/large/weeping_willow_big_2.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/willow/large/weeping_willow_big_2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..48ef0cf2b4f033edd21196d73bbcff851fb7fa2f GIT binary patch literal 1229 zcmV;;1Ty;{iwFP!000000Ik~FZX-ny2H^HgPakKmkaz}eIbQG(h&MpqFbfvhIEu|` z*{8=g-BBq1uKIzjWUUf?uBx8y>1vNtoXajg?(etrc`NSnQ*!=?{rk;*_Wtm&-97E! zoxgv3c)I)br~BhC#g!kQj*r{Z`LLZXaR2dea=SmA>u>t}A8*?BAGVWtU;6oQI`1Fu zHn;mOxpMja>GF&1;q3hR>G$n_g!}e&!+rC*;l6&|a9_P{xG&Xk{Brp8U*7cbx(>tJ z`{UhjmlECXi|ppd<7vLE$m=V(zy5{UEth)F+wG^no8|J_U0%}*vlAvaGb^Sv;}tVC zvnVDsqt(1N6O}l%nXDM!Od;y2ib;eCef%%X1m%d^w((+rHvRfss1FjF6& zVrF1uJsFr~P_q`N5@sc8?&AtdoEaEdvj-*&)`qr^1;RwatiZ@|^|43A1Yl&YLd2l>2?n(ar#_j;vUpYCQJoJ*4*pS_V5LmQO%kuM6L=9>bW;d?3tR$z>IROiiyCC z>e1qaHXl3X8ZOOD#(g1;+!G=Z$M^L?)H5r&0x)v_Nx+~z{8p2(o-GrAiGzBy{?+qo z`+NXK?ok;Sd3_2n@;X<-tVGRIGgjY`M&??8K|RGX379mvhMHN4n)}$JVrF3Ee0g9- zs*L+dp1j0mMMti0f zrvNkBqpD&OVKOl2^Ul?*L@uqLF70LYL21x+w&M6^tUf5sD3@juk*n9E^`-<2y1#C9 z6W7q{U(%p;D2-gtl`t!jtB=d-P12x!%rX%ew9i{{GGPiZvd2}ztVFJ<8LJOUgZiK} z=nP=xibSqVm_nFJn0|e<{^fxgtuf6c!en4j&$Z(8Io0~02L?S)kOn;~kOrN@tz4Nf zg)kME(Y~SeM4Y& zgn1sD(Q~Ci-{)F!B4HAdD-&@FVJcx(U`E%_I=5J60cNxgwa?v=h?9wW3K6FgW@^Sd zCrBgb-4k&FFsP?kCK0(ZQF9^U^t!dPga>A{KWlL!VKQL~Fmmjbh|}lGdUherXy34& zU088?J=(b?0)x(4Y@C%Cmv;UMEtYmBS(>q)t4o7ww&L_$+F2q1Gs>kI-;C9}E%Q7d z##urdw1-=9B4HAdD-&@FVd}paySC3Sz@Xn9m&wKOB=)SnKYwvOJkU~gs@KV1Ka^!VVef0Fbkk4*e0U@QOt%EohN literal 0 HcmV?d00001 diff --git a/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/willow/small/weeping_willow_small_1.nbt b/TerraForgedMod/src/main/resources/data/terraforged/structures/trees/willow/small/weeping_willow_small_1.nbt new file mode 100644 index 0000000000000000000000000000000000000000..5295f786fba17f823ba9e4acaa4fbca3133f2361 GIT binary patch literal 866 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 literal 0 HcmV?d00001