Compare commits

...

15 Commits

39 changed files with 839 additions and 87 deletions

View File

@ -13,6 +13,7 @@ val mod_id: String by project
val handle_deps: String by project
val use_commit_hash_in_version: String by project
val handleDeps = handle_deps.toBoolean()
val caffeine_cache_version: String by project
plugins {
java
@ -142,14 +143,11 @@ dependencies {
implementation("thedarkcolour:kotlinforforge-neoforge:$kotlin_for_forge_version")
jarJar("ru.dbotthepony.kommons:kommons:[$kommons_version,)") { setTransitive(false) }
implementation("ru.dbotthepony.kommons:kommons:[$kommons_version,)") { setTransitive(false) }
jarJar(implementation("com.github.ben-manes.caffeine:caffeine:[$caffeine_cache_version,)")!!)
jarJar("ru.dbotthepony.kommons:kommons-gson:[$kommons_version,)") { setTransitive(false) }
implementation("ru.dbotthepony.kommons:kommons-gson:[$kommons_version,)") { setTransitive(false) }
jarJar("ru.dbotthepony.kommons:kommons-guava:[$kommons_version,)") { setTransitive(false) }
implementation("ru.dbotthepony.kommons:kommons-guava:[$kommons_version,)") { setTransitive(false) }
jarJar(implementation("ru.dbotthepony.kommons:kommons:[$kommons_version,)") { setTransitive(false) })
jarJar(implementation("ru.dbotthepony.kommons:kommons-gson:[$kommons_version,)") { setTransitive(false) })
jarJar(implementation("ru.dbotthepony.kommons:kommons-guava:[$kommons_version,)") { setTransitive(false) })
compileOnly("yalter.mousetweaks:MouseTweaks:2.23:api")
annotationProcessor("org.spongepowered:mixin:${mixin_version}:processor")
@ -228,6 +226,10 @@ minecraft {
// Log4j console level
systemProperty("forge.logging.console.level", "debug")
dependencies {
runtime("com.github.ben-manes.caffeine:caffeine:[$caffeine_cache_version,)")
}
}
getByName("client") {

View File

@ -23,6 +23,7 @@ neogradle.subsystems.parchment.minecraftVersion=1.21.1
neogradle.subsystems.parchment.mappingsVersion=2024.11.17
kommons_version=3.1.3
caffeine_cache_version=3.1.5
jei_version=19.16.4.171
jupiter_version=5.9.2

View File

@ -9,6 +9,8 @@ import net.minecraft.tags.BlockTags
import net.minecraft.util.valueproviders.ClampedNormalFloat
import net.minecraft.util.valueproviders.ClampedNormalInt
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.util.valueproviders.UniformInt
import net.minecraft.world.level.levelgen.GenerationStep
import net.minecraft.world.level.levelgen.VerticalAnchor
@ -21,18 +23,26 @@ import net.minecraft.world.level.levelgen.placement.HeightRangePlacement
import net.minecraft.world.level.levelgen.placement.InSquarePlacement
import net.minecraft.world.level.levelgen.placement.PlacedFeature
import net.minecraft.world.level.levelgen.placement.RarityFilter
import net.minecraft.world.level.levelgen.structure.templatesystem.AlwaysTrueTest
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockStateMatchTest
import net.minecraft.world.level.levelgen.structure.templatesystem.TagMatchTest
import net.neoforged.neoforge.common.world.BiomeModifier
import net.neoforged.neoforge.registries.NeoForgeRegistries
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.world.EllipsoidPlacement
import ru.dbotthepony.mc.otm.data.world.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.data.world.OneOfFloatProvider
import ru.dbotthepony.mc.otm.worldgen.placement.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.registry.game.MBlocks
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
import ru.dbotthepony.mc.otm.worldgen.placement.AbstractEnormousPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousEllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
private object ConfiguredFeatures {
val TRITANIUM_ORE = key("tritanium_ore")
val TRITANIUM_ORE_SMALL = key("tritanium_ore_small")
val DILITHIUM = key("dilithium")
val BLACK_HOLE = key("black_hole")
@ -52,6 +62,8 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
)
context.register(ConfiguredFeatures.TRITANIUM_ORE, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 9)))
//context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 3)))
context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(MWorldGenFeatures.DEBUG_PLACEMENT, DebugPlacerFeature.Config(MBlocks.TRITANIUM_ORE.defaultBlockState())))
}
run {
@ -71,7 +83,7 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
private object PlacedFeatures {
val NORMAL_TRITANIUM = key("normal_tritanium")
val DEEP_TRITANIUM = key("deep_tritanium")
val CLOUD_TITANIUM = key("cloud_tritanium")
val WORM_TRITANIUM = key("worm_tritanium")
val DILITHIUM = key("dilithium")
val BLACK_HOLE = key("black_hole")
@ -104,20 +116,23 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
)
))
context.register(PlacedFeatures.CLOUD_TITANIUM, PlacedFeature(
ore,
context.register(PlacedFeatures.WORM_TRITANIUM, PlacedFeature(
configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE_SMALL),
listOf(
RarityFilter.onAverageOnceEvery(16),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(10), 15.0)),
EllipsoidPlacement(
x = ClampedNormalInt.of(0f, 6f, Int.MIN_VALUE, Int.MAX_VALUE),
y = ClampedNormalInt.of(0f, 12f, Int.MIN_VALUE, Int.MAX_VALUE),
z = ClampedNormalInt.of(0f, 6f, Int.MIN_VALUE, Int.MAX_VALUE),
count = ClampedNormalInt.of(60f, 60f, 40, 160),
xLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
yLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
zLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
WormPlacement(
parameters = AbstractEnormousPlacement.Parameters(
chunkScanRange = 12,
seedMix = 9284343575495L,
placementModifiers = listOf(
RarityFilter.onAverageOnceEvery(10),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(120), 15.0)),
)
),
wormLength = UniformInt.of(40, 200),
wormTurnChance = BooleanProvider.onceEvery(4),
wormTurnXY = WormPlacement.normalDistributedTurnRate(60f),
wormTurnXZ = WormPlacement.normalDistributedTurnRate(60f),
)
)
))
@ -126,22 +141,31 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
run {
val ore = configured.getOrThrow(ConfiguredFeatures.DILITHIUM)
val ringularity = OneOfFloatProvider.of(
ClampedNormalFloat.of(0.4f, 0.2f, -2f, 2f),
ClampedNormalFloat.of(-0.4f, 0.2f, -2f, 2f),
)
context.register(PlacedFeatures.DILITHIUM, PlacedFeature(
ore,
listOf(
RarityFilter.onAverageOnceEvery(12),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)),
EllipsoidPlacement(
x = ClampedNormalInt.of(0f, 8f, Int.MIN_VALUE, Int.MAX_VALUE),
y = ClampedNormalInt.of(0f, 20f, Int.MIN_VALUE, Int.MAX_VALUE),
z = ClampedNormalInt.of(0f, 8f, Int.MIN_VALUE, Int.MAX_VALUE),
count = ClampedNormalInt.of(200f, 200f, 200, 600),
xLength = ClampedNormalFloat.of(11f, 4f, 8f, 14f),
// allow crystals to generate as far as standard deviation allows
// to increase chance for player to discover crystal vein
yLength = ConstantFloat.of(60f),
zLength = ClampedNormalFloat.of(11f, 4f, 8f, 14f),
EnormousEllipsoidPlacement(
parameters = AbstractEnormousPlacement.Parameters(
chunkScanRange = 5,
seedMix = 237483209523709234L,
placementModifiers = listOf(
RarityFilter.onAverageOnceEvery(120),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)),
),
),
x = ringularity,
y = ringularity,
z = ringularity,
count = UniformInt.of(8000, 28000),
xLength = UniformFloat.of(30f, 70f),
yLength = UniformFloat.of(40f, 90f),
zLength = UniformFloat.of(30f, 70f),
)
)
))
@ -179,7 +203,7 @@ fun registerBiomeModifiers(context: BootstrapContext<BiomeModifier>) {
HolderSet.direct(
placed.getOrThrow(PlacedFeatures.NORMAL_TRITANIUM),
placed.getOrThrow(PlacedFeatures.DEEP_TRITANIUM),
placed.getOrThrow(PlacedFeatures.CLOUD_TITANIUM),
placed.getOrThrow(PlacedFeatures.WORM_TRITANIUM),
placed.getOrThrow(PlacedFeatures.DILITHIUM),
),
GenerationStep.Decoration.UNDERGROUND_ORES

View File

@ -11,6 +11,8 @@ fun addBlockModels(provider: MatteryBlockModelProvider) {
resourceCubeAll(MBlocks.DEEPSLATE_TRITANIUM_ORE)
resourceCubeAll(MBlocks.TRITANIUM_INGOT_BLOCK)
resourceCubeAll(MBlocks.WITHERED_STEEL_BLOCK)
resourceCubeAll(MBlocks.DILITHIUM_ORE)
resourceCubeAll(MBlocks.DEEPSLATE_DILITHIUM_ORE)
resourceCubeAll(MBlocks.DILITHIUM_CRYSTAL_BLOCK)

View File

@ -32,6 +32,20 @@ fun addBlockStates(provider: MatteryBlockStateProvider) {
provider.ore(MBlocks.TRITANIUM_RAW_BLOCK)
provider.block(MBlocks.TRITANIUM_INGOT_BLOCK)
provider.block(MBlocks.WITHERED_STEEL_BLOCK)
provider.exec {
provider.getVariantBuilder(MBlocks.ROFLITE_ALLOY_BLOCK).forAllStates {
val side1 = modLocation("block/resource/roflite_alloy_1")
val side2 = modLocation("block/resource/roflite_alloy_2")
val top = modLocation("block/resource/roflite_alloy_top")
val a = provider.models().cube(MBlocks.ROFLITE_ALLOY_BLOCK.registryName!!.path, top, top, side1, side1, side2, side2).texture("particle", side1)
val b = provider.models().cube("${MBlocks.ROFLITE_ALLOY_BLOCK.registryName!!.path}_b", top, top, side2, side2, side1, side1).texture("particle", side2)
return@forAllStates arrayOf(ConfiguredModel.builder().modelFile(a).buildLast(), ConfiguredModel.builder().modelFile(b).buildLast())
}
}
provider.ore(MBlocks.DILITHIUM_ORE)
provider.ore(MBlocks.DEEPSLATE_DILITHIUM_ORE)
provider.block(MBlocks.DILITHIUM_CRYSTAL_BLOCK)

View File

@ -35,6 +35,9 @@ fun addItemModels(provider: MatteryItemModelProvider) {
provider.block(MItems.TRITANIUM_RAW_BLOCK)
provider.block(MItems.TRITANIUM_INGOT_BLOCK)
provider.block(MItems.WITHERED_STEEL_BLOCK)
provider.block(MItems.ROFLITE_ALLOY_BLOCK)
provider.block(MItems.DILITHIUM_ORE)
provider.block(MItems.DEEPSLATE_DILITHIUM_ORE)
provider.block(MItems.DILITHIUM_CRYSTAL_BLOCK)
@ -68,6 +71,7 @@ fun addItemModels(provider: MatteryItemModelProvider) {
provider.generated(MItems.PILL_HEAL)
provider.generated(MItems.PILL_NOT_NORMAL)
provider.generated(MItems.NUTRIENT_PASTE)
provider.generated(MItems.IMPERFECT_BREAD)
provider.generated(MItems.REDSTONE_INTERACTOR)
provider.generated(MItems.ESSENCE_DRIVE)
@ -100,6 +104,7 @@ fun addItemModels(provider: MatteryItemModelProvider) {
provider.resource(MItems.DILITHIUM_CRYSTAL)
provider.resource(MItems.WITHERED_STEEL)
provider.resource(MItems.ROFLITE_ALLOY_INGOT)
provider.generated(MItems.EXOPACK_PROBE)

View File

@ -612,6 +612,8 @@ private fun blocks(provider: MatteryLanguageProvider) {
add(MBlocks.HOLO_SIGN, "desc", "Multi-line, colored and glowing, text sign")
add(MBlocks.TRITANIUM_INGOT_BLOCK, "Tritanium Plating Block")
add(MBlocks.WITHERED_STEEL_BLOCK, "Withered Steel Block")
add(MBlocks.ROFLITE_ALLOY_BLOCK, "Roflite Alloy Block")
addBlock(MBlocks.ENERGY_COUNTER.values, "Energy Counter")
addBlock(MBlocks.ENERGY_COUNTER.values, "desc", "Restricts energy flow direction;")
@ -741,6 +743,7 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.ESSENCE_SERVO, "desc2", "Can be used as a tool to pump essence manually")
add(MItems.NUTRIENT_PASTE, "Nutrient Paste")
add(MItems.IMPERFECT_BREAD, "Imperfect Bread")
add(MItems.FLUID_CAPSULE, "Fluid Capsule")
add(MItems.FLUID_CAPSULE, "named", "Fluid Capsule (%s)")
@ -788,6 +791,7 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.TRITANIUM_INGOT, "Tritanium Ingot")
add(MItems.DILITHIUM_CRYSTAL, "Dilithium Crystal")
add(MItems.WITHERED_STEEL, "Withered Steel Ingot")
add(MItems.ROFLITE_ALLOY_INGOT, "Roflite Alloy Ingot")
add(MItems.TRITANIUM_NUGGET, "Tritanium Nugget")
add(MItems.MATTER_IO_PORT, "Matter IO Port")
add(MItems.MATTER_TRANSFORM_MATRIX, "Matter Transformation Matrix")

View File

@ -616,6 +616,8 @@ private fun blocks(provider: MatteryLanguageProvider) {
add(MBlocks.HOLO_SIGN, "desc", "Многострочная, крашеная и светящееся, табличка")
add(MBlocks.TRITANIUM_INGOT_BLOCK, "Блок слитков тритана")
add(MBlocks.WITHERED_STEEL_BLOCK, "Блок иссушенной стали")
add(MBlocks.ROFLITE_ALLOY_BLOCK, "Блок рофлитового сплава")
addBlock(MBlocks.ENERGY_COUNTER.values, "Счётчик энергии")
addBlock(MBlocks.ENERGY_COUNTER.values, "desc", "Ограничивает направление передачи энергии;")
@ -734,6 +736,7 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.ESSENCE_SERVO, "desc2", "Может использоваться как инструмент для ручной перекачки эссенции")
add(MItems.NUTRIENT_PASTE, "Питательная паста")
add(MItems.IMPERFECT_BREAD, "Несовершенный хлеб")
add(MItems.FLUID_CAPSULE, "Жидкостная капсула")
add(MItems.FLUID_CAPSULE, "named", "Жидкостная капсула (%s)")
@ -781,6 +784,7 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.TRITANIUM_INGOT, "Тритановый слиток")
add(MItems.DILITHIUM_CRYSTAL, "Дилитевый кристалл")
add(MItems.WITHERED_STEEL, "Слиток иссушенной стали")
add(MItems.ROFLITE_ALLOY_INGOT, "Слиток рофлитового сплава")
add(MItems.TRITANIUM_NUGGET, "Тритановый самородок")
add(MItems.MATTER_IO_PORT, "Порт ввода/вывода материи")
add(MItems.MATTER_TRANSFORM_MATRIX, "Матрица преобразования материи")

View File

@ -50,6 +50,8 @@ fun addDecorativeLoot(lootTables: LootTables) {
lootTables.dropsSelf(MBlocks.TRITANIUM_INGOT_BLOCK) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.TRITANIUM_BARS) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.WITHERED_STEEL_BLOCK) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.TRITANIUM_HULL) { condition(ExplosionCondition.survivesExplosion()) }
lootTables.dropsSelf(MBlocks.GENERATOR_BLOCK) { condition(ExplosionCondition.survivesExplosion()) }

View File

@ -93,4 +93,24 @@ fun addShapelessRecipes(consumer: RecipeOutput) {
hammerRecipe(MItems.TRITANIUM_PLATE, MItemTags.TRITANIUM_INGOTS, consumer)
hammerRecipe(MItems.IRON_PLATE, Tags.Items.INGOTS_IRON, consumer)
hammerRecipe(MItems.GOLD_PLATE, Tags.Items.INGOTS_GOLD, consumer)
ShapelessRecipeBuilder(RecipeCategory.BUILDING_BLOCKS, MItems.WITHERED_STEEL_BLOCK, 1)
.requires(Ingredient.of(MItems.WITHERED_STEEL), 9)
.unlockedBy(MItems.WITHERED_STEEL)
.save(consumer)
ShapelessRecipeBuilder(RecipeCategory.MISC, MItems.WITHERED_STEEL, 9)
.requires(Ingredient.of(MItems.WITHERED_STEEL_BLOCK))
.unlockedBy(MItems.WITHERED_STEEL_BLOCK)
.save(consumer)
ShapelessRecipeBuilder(RecipeCategory.BUILDING_BLOCKS, MItems.ROFLITE_ALLOY_BLOCK, 1)
.requires(Ingredient.of(MItems.ROFLITE_ALLOY_INGOT), 9)
.unlockedBy(MItems.ROFLITE_ALLOY_INGOT)
.save(consumer)
ShapelessRecipeBuilder(RecipeCategory.MISC, MItems.ROFLITE_ALLOY_INGOT, 9)
.requires(Ingredient.of(MItems.ROFLITE_ALLOY_BLOCK))
.unlockedBy(MItems.ROFLITE_ALLOY_BLOCK)
.save(consumer)
}

View File

@ -11,7 +11,11 @@ import ru.dbotthepony.mc.otm.registry.game.MItems
import ru.dbotthepony.mc.otm.registry.MRegistry
fun addConstructionTags(tagsProvider: TagsProvider) {
tagsProvider.blocks.Appender(BlockTags.BEACON_BASE_BLOCKS).add(MBlocks.TRITANIUM_INGOT_BLOCK)
tagsProvider.blocks.Appender(BlockTags.BEACON_BASE_BLOCKS)
.add(MBlocks.TRITANIUM_INGOT_BLOCK)
.add(MBlocks.DILITHIUM_CRYSTAL_BLOCK)
.add(MBlocks.WITHERED_STEEL_BLOCK)
.add(MBlocks.ROFLITE_ALLOY_BLOCK)
tagsProvider.blocks.Appender(MBlockTags.MULTIBLOCK_STRUCTURE)
.add(MBlockTags.MULTIBLOCK_HARD_STRUCTURE, MBlockTags.MULTIBLOCK_SOFT_STRUCTURE)

View File

@ -51,6 +51,8 @@ fun addMineableTags(tagsProvider: TagsProvider) {
*MBlocks.ENERGY_SERVO.values.toTypedArray(),
MBlocks.TRITANIUM_INGOT_BLOCK,
MBlocks.WITHERED_STEEL_BLOCK,
MBlocks.METAL_JUNK,
MBlocks.METAL_MESH,
MBlocks.TRITANIUM_BARS,
@ -96,6 +98,7 @@ fun addMineableTags(tagsProvider: TagsProvider) {
MBlocks.DILITHIUM_ORE,
MBlocks.DEEPSLATE_DILITHIUM_ORE,
MBlocks.DILITHIUM_CRYSTAL_BLOCK,
MBlocks.ROFLITE_ALLOY_BLOCK,
MBlocks.GRAVITATION_STABILIZER,
MBlocks.GRAVITATION_STABILIZER_LENS,

View File

@ -21,6 +21,8 @@ fun addResourceTags(tagsProvider: TagsProvider) {
tagsProvider.dusts.add("tritanium", MItems.TRITANIUM_DUST)
tagsProvider.ingots.add("tritanium", MItems.TRITANIUM_INGOT)
tagsProvider.ingots.add("withered_steel", MItems.WITHERED_STEEL)
tagsProvider.ingots.add("roflite_alloy", MItems.ROFLITE_ALLOY_INGOT)
tagsProvider.wires.add("copper", MItems.COPPER_WIRING)
tagsProvider.wires.add("gold", MItems.GOLD_WIRING)
@ -32,6 +34,10 @@ fun addResourceTags(tagsProvider: TagsProvider) {
tagsProvider.storageBlocksAsItem.add("tritanium", MItems.TRITANIUM_INGOT_BLOCK)
tagsProvider.storageBlocksAsBlock.add("tritanium", MBlocks.TRITANIUM_INGOT_BLOCK)
tagsProvider.storageBlocksAsItem.add("withered_steel", MItems.WITHERED_STEEL_BLOCK)
tagsProvider.storageBlocksAsBlock.add("withered_steel", MBlocks.WITHERED_STEEL_BLOCK)
tagsProvider.storageBlocksAsItem.add("roflite_alloy", MItems.ROFLITE_ALLOY_BLOCK)
tagsProvider.storageBlocksAsBlock.add("roflite_alloy", MBlocks.ROFLITE_ALLOY_BLOCK)
tagsProvider.singleDropOre(
MBlocks.TRITANIUM_ORE,

View File

@ -20,6 +20,8 @@ fun addTags(tagsProvider: TagsProvider) {
tagsProvider.items.Appender(ItemTags.BEACON_PAYMENT_ITEMS)
.add(MItems.TRITANIUM_INGOT)
.add(MItems.DILITHIUM_CRYSTAL)
.add(MItems.WITHERED_STEEL)
.add(MItems.ROFLITE_ALLOY_INGOT)
tagsProvider.items.Appender(ItemTags.MEAT).add(MItems.NUTRIENT_PASTE)

View File

@ -86,6 +86,7 @@ import ru.dbotthepony.mc.otm.registry.data.MHeightProviders
import ru.dbotthepony.mc.otm.registry.data.MItemFunctionTypes
import ru.dbotthepony.mc.otm.registry.data.MLootItemConditions
import ru.dbotthepony.mc.otm.registry.data.MLootNumberProviders
import ru.dbotthepony.mc.otm.registry.data.MNumberProviders
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
import ru.dbotthepony.mc.otm.server.MCommands
@ -124,6 +125,7 @@ object OverdriveThatMatters {
CommandArgumentTypes.register(MOD_BUS)
MHeightProviders.register(MOD_BUS)
MPlacementModifiers.register(MOD_BUS)
MNumberProviders.register(MOD_BUS)
MLootNumberProviders.register(MOD_BUS)
StorageStack.register(MOD_BUS)

View File

@ -678,3 +678,15 @@ fun RandomSource.nextNormalDoubles(stddev: Double, mean: Double): DoublePair {
fun RandomSource.nextNormalDouble(stddev: Double, mean: Double): Double {
return nextGaussian() * stddev + mean
}
fun RandomSource.nextFloat(min: Float, max: Float): Float {
require(max >= min) { "Min is bigger than max: $min vs $max" }
if (min == max) return min
return min + nextFloat() * (max - min)
}
fun RandomSource.nextDouble(min: Double, max: Double): Double {
require(max >= min) { "Min is bigger than max: $min vs $max" }
if (min == max) return min
return min + nextDouble() * (max - min)
}

View File

@ -436,7 +436,9 @@ operator fun Direction.times(int: Int): Vec3i = this.normal.multiply(int)
operator fun Vec3i.times(double: Double): Vector = Vector(x * double, y * double, z * double)
fun Vec3.toIntVector() = Vec3i(x.toInt(), y.toInt(), z.toInt())
fun Vec3.toBlockPos() = BlockPos(x.toInt(), y.toInt(), z.toInt())
fun Vec3.roundToIntVector() = Vec3i(x.roundToInt(), y.roundToInt(), z.roundToInt())
fun Vec3.roundToBlockPos() = BlockPos(x.roundToInt(), y.roundToInt(), z.roundToInt())
fun BlockPos.asVector(): Vector {
return Vector(x + 0.5, y + 0.5, z + 0.5)

View File

@ -53,3 +53,5 @@ fun <S : Comparable<S>> Codec<S>.maxRange(max: S, exclusive: Boolean = false) =
ComparableCodec(this, max = max, maxExclusive = exclusive)
fun <S : Comparable<S>> Codec<S>.inRange(min: S, minExclusive: Boolean = false, max: S, maxExclusive: Boolean = false) =
ComparableCodec(this, min, max, minExclusive, maxExclusive)
fun <S : Comparable<S>> Codec<S>.inRange(min: S, max: S) =
ComparableCodec(this, min, max, false, false)

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.mc.otm.data.world
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.UniformFloat
class BooleanProvider(val sampler: FloatProvider, val threshold: Float, val lessThan: Boolean = true) {
fun sample(random: RandomSource): Boolean {
val sampled = sampler.sample(random)
if (lessThan) {
return sampled < threshold
} else {
return sampled >= threshold
}
}
companion object {
fun onceEvery(samples: Int): BooleanProvider {
return BooleanProvider(UniformFloat.of(0f, samples.toFloat()), 1f, lessThan = true)
}
val CODEC: Codec<BooleanProvider> = RecordCodecBuilder.create {
it.group(
FloatProvider.CODEC.fieldOf("sampler").forGetter(BooleanProvider::sampler),
Codec.FLOAT.fieldOf("threshold").forGetter(BooleanProvider::threshold),
Codec.BOOL.optionalFieldOf("less_than", true).forGetter(BooleanProvider::lessThan)
).apply(it, ::BooleanProvider)
}
}
}

View File

@ -0,0 +1,38 @@
package ru.dbotthepony.mc.otm.data.world
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.FloatProviderType
import ru.dbotthepony.mc.otm.registry.data.MNumberProviders
class OneOfFloatProvider(private val provider: OneOfProvider<FloatProvider>) : FloatProvider() {
override fun sample(random: RandomSource): Float {
return provider.select(random).sample(random)
}
override fun getMinValue(): Float {
return provider.children.minOf { it.minValue }
}
override fun getMaxValue(): Float {
return provider.children.minOf { it.maxValue }
}
override fun getType(): FloatProviderType<*> {
return MNumberProviders.ONE_OF_FLOAT
}
companion object {
@JvmStatic
fun of(vararg providers: FloatProvider): OneOfFloatProvider {
return OneOfFloatProvider(OneOfProvider.of(*providers))
}
@JvmStatic
fun of(providers: Collection<FloatProvider>): OneOfFloatProvider {
return OneOfFloatProvider(OneOfProvider.of(providers))
}
val CODEC = OneOfProvider.createCodec(FloatProvider.CODEC, ::OneOfFloatProvider, OneOfFloatProvider::provider)
}
}

View File

@ -0,0 +1,38 @@
package ru.dbotthepony.mc.otm.data.world
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.IntProviderType
import ru.dbotthepony.mc.otm.registry.data.MNumberProviders
class OneOfIntProvider(private val provider: OneOfProvider<IntProvider>) : IntProvider() {
override fun sample(random: RandomSource): Int {
return provider.select(random).sample(random)
}
override fun getMinValue(): Int {
return provider.children.minOf { it.minValue }
}
override fun getMaxValue(): Int {
return provider.children.minOf { it.maxValue }
}
override fun getType(): IntProviderType<*> {
return MNumberProviders.ONE_OF_INT
}
companion object {
@JvmStatic
fun of(vararg providers: IntProvider): OneOfIntProvider {
return OneOfIntProvider(OneOfProvider.of(*providers))
}
@JvmStatic
fun of(providers: Collection<IntProvider>): OneOfIntProvider {
return OneOfIntProvider(OneOfProvider.of(providers))
}
val CODEC = OneOfProvider.createCodec(IntProvider.CODEC, ::OneOfIntProvider, OneOfIntProvider::provider)
}
}

View File

@ -0,0 +1,85 @@
package ru.dbotthepony.mc.otm.data.world
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.IntProvider
import ru.dbotthepony.mc.otm.core.random
import java.util.Optional
class OneOfProvider<T> private constructor(val children: List<T>, val selector: Optional<IntProvider> = Optional.empty()) {
fun select(random: RandomSource): T {
if (selector.isEmpty) {
return children.random(random)
}
val index = selector.get().sample(random)
if (index !in children.indices) {
throw RuntimeException("Provided selector ($selector) provided index out of bounds: $index (bounds: ${children.indices})")
}
return children[index]
}
class Builder<T> {
private var selector: IntProvider? = null
private val inputs = ArrayList<T>()
fun indexSelector(selector: IntProvider): Builder<T> {
this.selector = selector
return this
}
fun add(vararg provider: T): Builder<T> {
inputs.addAll(provider)
return this
}
fun copy(): Builder<T> {
val new = Builder<T>()
new.selector = selector
new.inputs.addAll(inputs)
return new
}
fun build(): OneOfProvider<T> {
return OneOfProvider(ImmutableList.copyOf(inputs), Optional.ofNullable(selector))
}
}
companion object {
@JvmStatic
fun <T> of(vararg providers: T): OneOfProvider<T> {
return OneOfProvider(ImmutableList.copyOf(providers))
}
@JvmStatic
fun <T> of(providers: Collection<T>): OneOfProvider<T> {
return OneOfProvider(ImmutableList.copyOf(providers))
}
@JvmStatic
fun <T> createCodec(childrenCodec: Codec<T>): MapCodec<OneOfProvider<T>> {
return RecordCodecBuilder.mapCodec {
it.group(
Codec.list(childrenCodec, 1, Int.MAX_VALUE).fieldOf("children").forGetter<OneOfProvider<T>> { it.children },
IntProvider.POSITIVE_CODEC.optionalFieldOf("selector").forGetter<OneOfProvider<T>> { it.selector },
).apply(it, ::OneOfProvider)
}.validate {
if (it.selector.isEmpty || it.selector.get().minValue in it.children.indices && it.selector.get().maxValue in it.children.indices) {
return@validate DataResult.success(it)
} else {
return@validate DataResult.error { "Provided number sampler ${it.selector.get()} can produce values outside of children list index range (list size: ${it.children.size})" }
}
}
}
fun <S, T> createCodec(childrenCodec: Codec<T>, to: (OneOfProvider<T>) -> S, from: (S) -> OneOfProvider<T>): MapCodec<S> {
return createCodec(childrenCodec).xmap(to, from)
}
}
}

View File

@ -0,0 +1,25 @@
package ru.dbotthepony.mc.otm.item.consumables
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.level.Level
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.food.FoodProperties
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
class ImperfectBread(properties: Item.Properties) : Item(properties) {
override fun finishUsingItem(stack: ItemStack, level: Level, entity: LivingEntity): ItemStack {
if (entity is Player) {
entity.addEffect(MobEffectInstance(MobEffects.POISON, 80, 0))
}
return super.finishUsingItem(stack, level, entity)
}
}
val IMPERFECT_BREAD_FOOD: FoodProperties = FoodProperties.Builder()
.nutrition(5)
.saturationModifier(0.6f)
.build()

View File

@ -93,10 +93,12 @@ object MNames {
// building blocks
const val TRITANIUM_BLOCK = "tritanium_block"
const val TRITANIUM_BLOCK_YELLOW_CLASSIC = "tritanium_block_yellow_classic"
const val TRITANIUM_STAIRS = "tritanium_stairs"
const val TRITANIUM_SLAB = "tritanium_slab"
const val TRITANIUM_WALL = "tritanium_wall"
const val TRITANIUM_STRIPED_BLOCK = "tritanium_striped_block"
const val TRITANIUM_CLASSIC_STRIPED_BLOCK = "tritanium_classic_striped_block"
const val TRITANIUM_STRIPED_STAIRS = "tritanium_striped_stairs"
const val TRITANIUM_STRIPED_SLAB = "tritanium_striped_slab"
const val TRITANIUM_STRIPED_WALL = "tritanium_striped_wall"
@ -128,6 +130,8 @@ object MNames {
const val PILL_HEAL = "pill_heal"
const val PILL_NOT_NORMAL = "pill_not_normal"
const val IMPERFECT_BREAD = "imperfect_bread"
const val BATTERY_CRUDE = "battery_crude"
const val BATTERY_BASIC = "battery_basic"
const val BATTERY_NORMAL = "battery_normal"
@ -205,8 +209,12 @@ object MNames {
const val TRITANIUM_INGOT = "tritanium_ingot"
const val TRITANIUM_INGOT_BLOCK = "tritanium_ingot_block"
const val WITHERED_STEEL_BLOCK = "withered_steel_block"
const val WITHERED_STEEL = "withered_steel"
const val ROFLITE_ALLOY_INGOT = "roflite_alloy_ingot"
const val ROFLITE_ALLOY_BLOCK = "roflite_alloy_block"
const val MATTER_IO_PORT = "matter_io_port"
const val CARBON_MESH = "carbon_mesh"

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.levelgen.heightproviders.HeightProviderType
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.data.world.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.worldgen.placement.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
object MHeightProviders {

View File

@ -0,0 +1,22 @@
package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.util.valueproviders.FloatProviderType
import net.minecraft.util.valueproviders.IntProviderType
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.data.world.OneOfFloatProvider
import ru.dbotthepony.mc.otm.data.world.OneOfIntProvider
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
object MNumberProviders {
private val floats = MDeferredRegister(BuiltInRegistries.FLOAT_PROVIDER_TYPE)
private val ints = MDeferredRegister(BuiltInRegistries.INT_PROVIDER_TYPE)
fun register(bus: IEventBus) {
floats.register(bus)
ints.register(bus)
}
val ONE_OF_FLOAT by floats.register("one_of_float") { FloatProviderType { OneOfFloatProvider.CODEC } }
val ONE_OF_INT by ints.register("one_of_int") { IntProviderType { OneOfIntProvider.CODEC } }
}

View File

@ -3,8 +3,10 @@ package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.data.world.EllipsoidPlacement
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousEllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
object MPlacementModifiers {
private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE)
@ -14,4 +16,6 @@ object MPlacementModifiers {
}
val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } }
val ENORMOUS_ELLIPSOID_PLACEMENT by registry.register("enormous_ellipsoid") { PlacementModifierType { EnormousEllipsoidPlacement.CODEC } }
val WORM_PLACEMENT by registry.register("worm") { PlacementModifierType { WormPlacement.CODEC } }
}

View File

@ -2,8 +2,10 @@ package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
object MWorldGenFeatures {
private val registry = MDeferredRegister(BuiltInRegistries.FEATURE)
@ -13,4 +15,5 @@ object MWorldGenFeatures {
}
val BLACK_HOLE_PLACER by registry.register("black_hole_placer") { BlackHolePlacerFeature }
val DEBUG_PLACEMENT by registry.register("debug") { DebugPlacerFeature }
}

View File

@ -252,6 +252,14 @@ object MBlocks {
Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).sound(SoundType.METAL).explosionResistance(400f).destroyTime(3f).requiresCorrectToolForDrops())
}
val WITHERED_STEEL_BLOCK: Block by registry.register(MNames.WITHERED_STEEL_BLOCK) {
Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GREEN).sound(SoundType.NETHERITE_BLOCK).explosionResistance(400f).destroyTime(3f).requiresCorrectToolForDrops())
}
val ROFLITE_ALLOY_BLOCK: Block by registry.register(MNames.ROFLITE_ALLOY_BLOCK) {
Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.NETHERITE_BLOCK).explosionResistance(400f).destroyTime(3f).requiresCorrectToolForDrops())
}
val METAL_JUNK: MatteryBlock by registry.register(MNames.METAL_JUNK) {
MatteryBlock(BlockBehaviour.Properties.of().sound(SoundType.NETHERITE_BLOCK).mapColor(MapColor.COLOR_GRAY).explosionResistance(45f).destroyTime(3f).requiresCorrectToolForDrops())
.addSimpleDescription()

View File

@ -237,6 +237,10 @@ private fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
accept(MItems.DILITHIUM_CRYSTAL_BLOCK)
accept(MItems.WITHERED_STEEL)
accept(MItems.WITHERED_STEEL_BLOCK)
accept(MItems.ROFLITE_ALLOY_INGOT)
accept(MItems.ROFLITE_ALLOY_BLOCK)
accept(MItems.TRITANIUM_TOOLS)
accept(MItems.TRITANIUM_SHIELD)
@ -281,6 +285,7 @@ private fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
base(MItems.CARGO_CRATE_MINECARTS)
accept(MItems.NUTRIENT_PASTE)
accept(MItems.IMPERFECT_BREAD)
// exo
accept(MItems.EXOPACK_PROBE)

View File

@ -43,16 +43,12 @@ import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.item.BatteryItem
import ru.dbotthepony.mc.otm.item.ChestUpgraderItem
import ru.dbotthepony.mc.otm.item.CrudeBatteryItem
import ru.dbotthepony.mc.otm.item.consumables.EssenceCapsuleItem
import ru.dbotthepony.mc.otm.item.EssenceServoItem
import ru.dbotthepony.mc.otm.item.FluidCapsuleItem
import ru.dbotthepony.mc.otm.item.block.FluidTankItem
import ru.dbotthepony.mc.otm.item.GravitationalDisruptorItem
import ru.dbotthepony.mc.otm.item.consumables.HealPillItem
import ru.dbotthepony.mc.otm.item.MatteryItem
import ru.dbotthepony.mc.otm.item.MinecartCargoCrateItem
import ru.dbotthepony.mc.otm.item.consumables.PillItem
import ru.dbotthepony.mc.otm.item.consumables.PillType
import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem
import ru.dbotthepony.mc.otm.item.ProceduralBatteryItem
import ru.dbotthepony.mc.otm.item.QuantumBatteryItem
@ -62,7 +58,7 @@ import ru.dbotthepony.mc.otm.item.addSimpleDescription
import ru.dbotthepony.mc.otm.item.armor.PortableGravitationStabilizerItem
import ru.dbotthepony.mc.otm.item.armor.TritaniumArmorItem
import ru.dbotthepony.mc.otm.item.block.EnergyCableItem
import ru.dbotthepony.mc.otm.item.consumables.NotNormalPill
import ru.dbotthepony.mc.otm.item.consumables.*
import ru.dbotthepony.mc.otm.item.exopack.ExopackProbeItem
import ru.dbotthepony.mc.otm.item.exopack.ExopackSlotUpgradeItem
import ru.dbotthepony.mc.otm.item.exopack.ExopackUpgradeItem
@ -301,6 +297,9 @@ object MItems {
val TRITANIUM_NUGGET: Item by registry.register(MNames.TRITANIUM_NUGGET) { Item(DEFAULT_PROPERTIES) }
val TRITANIUM_INGOT: Item by registry.register(MNames.TRITANIUM_INGOT) { Item(DEFAULT_PROPERTIES) }
val TRITANIUM_INGOT_BLOCK: BlockItem by registry.register(MNames.TRITANIUM_INGOT_BLOCK) { BlockItem(MBlocks.TRITANIUM_INGOT_BLOCK, DEFAULT_PROPERTIES) }
val WITHERED_STEEL_BLOCK: BlockItem by registry.register(MNames.WITHERED_STEEL_BLOCK) { BlockItem(MBlocks.WITHERED_STEEL_BLOCK, DEFAULT_PROPERTIES) }
val TRITANIUM_BARS: BlockItem by registry.register(MNames.TRITANIUM_BARS) { BlockItem(MBlocks.TRITANIUM_BARS, DEFAULT_PROPERTIES) }
val METAL_RAILING: BlockItem by registry.register(MNames.METAL_RAILING) { BlockItem(MBlocks.METAL_RAILING, DEFAULT_PROPERTIES) }
@ -426,6 +425,8 @@ object MItems {
val PILL_HEAL: Item by registry.register(MNames.PILL_HEAL) { HealPillItem() }
val PILL_NOT_NORMAL: Item by registry.register(MNames.PILL_NOT_NORMAL) { NotNormalPill() }
val IMPERFECT_BREAD: Item by registry.register(MNames.IMPERFECT_BREAD) { ImperfectBread(Item.Properties().food(IMPERFECT_BREAD_FOOD)) }
val PILLS = SupplierList(
MItems::PILL_ANDROID,
MItems::PILL_HUMANE,
@ -665,6 +666,9 @@ object MItems {
val BREAD_MONSTER_SPAWN_EGG: Item by registry.register(MNames.BREAD_MONSTER_SPAWN_EGG){ SpawnEggItem(MEntityTypes.BREAD_MONSTER, 0xFFFFFF, 0xFFFFFF, Item.Properties())}
val ROFLITE_ALLOY_INGOT: Item by registry.register(MNames.ROFLITE_ALLOY_INGOT) { Item(DEFAULT_PROPERTIES) }
val ROFLITE_ALLOY_BLOCK: BlockItem by registry.register(MNames.ROFLITE_ALLOY_BLOCK) { BlockItem(MBlocks.ROFLITE_ALLOY_BLOCK, DEFAULT_PROPERTIES) }
init {
MRegistry.registerItems(registry)
}

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.worldgen.feature
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.levelgen.feature.Feature
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
import ru.dbotthepony.mc.otm.registry.game.MBlocks
object DebugPlacerFeature : Feature<DebugPlacerFeature.Config>(BlockState.CODEC.xmap(::Config, Config::blockState)) {
data class Config(val blockState: BlockState) : FeatureConfiguration
override fun place(context: FeaturePlaceContext<Config>): Boolean {
return context.level().setBlock(context.origin(), context.config().blockState, Block.UPDATE_CLIENTS)
}
}

View File

@ -0,0 +1,109 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Scheduler
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.HashCommon
import net.minecraft.Util
import net.minecraft.core.BlockPos
import net.minecraft.core.SectionPos
import net.minecraft.util.RandomSource
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import ru.dbotthepony.mc.otm.data.codec.inRange
import ru.dbotthepony.mc.otm.data.codec.minRange
import java.time.Duration
import java.util.stream.Stream
import kotlin.math.sqrt
/**
* Enormous placement base, which allows it to span over several chunks without issues.
*
* MUST come as first placement modifier, other placement modifiers (such as rarity and
* shuffle of center point within chunks) must be provided inside [Parameters.placementModifiers] list, in same order as if they were
* *before* this placement
*/
abstract class AbstractEnormousPlacement(val parameters: Parameters) : PlacementModifier() {
data class Parameters(
/**
* How many chunks away to look for actual placements
*
* Too small value will cause placement cutoffs
*/
val chunkScanRange: Int,
val seedMix: Long,
/**
* Baseline placement modifiers, dictating how to appear in chunk
*/
val placementModifiers: List<PlacementModifier>,
)
private class GeneratedChunk(positions: Stream<BlockPos>) {
// TODO: memory inefficient
private val positions = ArrayList<BlockPos>()
init {
positions.forEach { this.positions.add(it) }
}
// TODO: this is primitive implementation, need to implement better one
fun getPositions(pos: ChunkPos): Stream<BlockPos> {
return positions.stream().filter {
SectionPos.blockToSectionCoord(it.x) == pos.x &&
SectionPos.blockToSectionCoord(it.z) == pos.z
}
}
}
protected abstract fun getPositions(center: BlockPos, random: RandomSource): Stream<BlockPos>
private val chunkCache = Caffeine.newBuilder()
.scheduler(Scheduler.systemScheduler())
.executor(Util.backgroundExecutor())
.maximumSize(16384L)
.expireAfterWrite(Duration.ofMinutes(5))
.softValues()
.build<ChunkPos, GeneratedChunk>()
private fun computeChunk(context: PlacementContext, pos: ChunkPos): GeneratedChunk {
// TODO: Maybe switch to something else other than original LCG PRNG? Original form of LCG is kinda bad for this kind of workload
val random = RandomSource.create(context.level.seed + HashCommon.murmurHash3(pos.x + pos.z * 31) + parameters.seedMix)
var stream = Stream.of(BlockPos(pos.minBlockX, 0, pos.minBlockZ))
parameters.placementModifiers.forEach { modifier -> stream = stream.flatMap { modifier.getPositions(context, random, it).sequential() } }
return GeneratedChunk(stream.flatMap { getPositions(it, random) })
}
final override fun getPositions(context: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> {
val cPos = ChunkPos(pos)
val instances = ArrayList<GeneratedChunk>()
for (x in -parameters.chunkScanRange .. parameters.chunkScanRange) {
for (z in -parameters.chunkScanRange .. parameters.chunkScanRange) {
// floor, so chunk scan range of 1 will give square instead of diamond
val radius = sqrt(x.toDouble() * x + z.toDouble() * z).toInt()
if (radius <= parameters.chunkScanRange) {
val thisPos = ChunkPos(cPos.x + x, cPos.z + z)
instances.add(chunkCache.get(thisPos) { computeChunk(context, thisPos) })
}
}
}
return instances.stream().flatMap { it.getPositions(cPos) }
}
companion object {
val PARAMETERS_CODEC: MapCodec<Parameters> = RecordCodecBuilder.mapCodec {
it.group(
Codec.INT.minRange(0).fieldOf("chunk_scan_range").forGetter(Parameters::chunkScanRange),
Codec.LONG.fieldOf("seed_mix").forGetter(Parameters::seedMix),
CODEC.listOf().fieldOf("placement").forGetter(Parameters::placementModifiers),
).apply(it, ::Parameters)
}
}
}

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data.world
package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
@ -19,16 +19,20 @@ import java.util.stream.Stream
import kotlin.math.PI
import kotlin.math.roundToInt
// aka "cloud placement"
/**
* Regular ellipsoid ("cloud") placement, suitable to be used as non-primary placement modifier
*
* This placement modifier is designed to be terminal; other placement modifiers after this MUST NOT be placed
*/
data class EllipsoidPlacement(
val x: IntProvider,
val z: IntProvider,
val y: IntProvider,
val count: IntProvider,
val xLength: FloatProvider,
val zLength: FloatProvider,
val yLength: FloatProvider,
) : PlacementModifier() {
override val x: FloatProvider,
override val z: FloatProvider,
override val y: FloatProvider,
override val count: IntProvider,
override val xLength: FloatProvider,
override val zLength: FloatProvider,
override val yLength: FloatProvider,
) : PlacementModifier(), IEllipsoidPlacement {
init {
require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" }
require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" }
@ -40,34 +44,7 @@ data class EllipsoidPlacement(
random: RandomSource,
position: BlockPos
): Stream<BlockPos> {
var count = count.sample(random)
if (count <= 0) {
return Stream.empty()
}
val xLength = xLength.sample(random)
val zLength = zLength.sample(random)
val yLength = yLength.sample(random)
val xPow = xLength * xLength
val zPow = zLength * zLength
val yPow = yLength * yLength
count = minOf(count, (xLength * zLength * yLength * PI * (4.0 / 3.0)).roundToInt())
count = 600
return Stream.generate { position + BlockPos(this.x.sample(random), this.y.sample(random), this.z.sample(random)) }
.limit(count * 10L) // failsafe
.filter {
val (ellipsoidX, ellipsoidY, ellipsoidZ) = it - position
(ellipsoidX * ellipsoidX) / xPow +
(ellipsoidY * ellipsoidY) / yPow +
(ellipsoidZ * ellipsoidZ) / zPow <= 1.0f
}
.distinct()
.limit(count.toLong())
return getEllipsoidPositions(random, position)
}
override fun type(): PlacementModifierType<*> {
@ -78,9 +55,9 @@ data class EllipsoidPlacement(
val CODEC: MapCodec<EllipsoidPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
IntProvider.CODEC.fieldOf("x").forGetter(EllipsoidPlacement::x),
IntProvider.CODEC.fieldOf("y").forGetter(EllipsoidPlacement::y),
IntProvider.CODEC.fieldOf("z").forGetter(EllipsoidPlacement::z),
FloatProvider.CODEC.fieldOf("x").forGetter(EllipsoidPlacement::x),
FloatProvider.CODEC.fieldOf("y").forGetter(EllipsoidPlacement::y),
FloatProvider.CODEC.fieldOf("z").forGetter(EllipsoidPlacement::z),
IntProvider.CODEC.fieldOf("count").forGetter(EllipsoidPlacement::count),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("x_size").forGetter(EllipsoidPlacement::xLength),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("z_size").forGetter(EllipsoidPlacement::zLength),

View File

@ -0,0 +1,60 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import java.util.stream.Stream
/**
* Enormous ellipsoid ("cloud") placement, suitable to be used as ONLY primary placement modifier
*
* This placement modifier is designed to be terminal; other placement modifiers after this MUST NOT be placed
*
* @see AbstractEnormousPlacement
*/
class EnormousEllipsoidPlacement(
parameters: Parameters,
override val x: FloatProvider,
override val z: FloatProvider,
override val y: FloatProvider,
override val count: IntProvider,
override val xLength: FloatProvider,
override val zLength: FloatProvider,
override val yLength: FloatProvider,
) : AbstractEnormousPlacement(parameters), IEllipsoidPlacement {
init {
require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" }
require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" }
require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" }
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.ENORMOUS_ELLIPSOID_PLACEMENT
}
override fun getPositions(center: BlockPos, random: RandomSource): Stream<BlockPos> {
return getEllipsoidPositions(random, center)
}
companion object {
val CODEC: MapCodec<EnormousEllipsoidPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
PARAMETERS_CODEC.forGetter(EnormousEllipsoidPlacement::parameters),
FloatProvider.CODEC.fieldOf("x").forGetter(EnormousEllipsoidPlacement::x),
FloatProvider.CODEC.fieldOf("y").forGetter(EnormousEllipsoidPlacement::y),
FloatProvider.CODEC.fieldOf("z").forGetter(EnormousEllipsoidPlacement::z),
IntProvider.CODEC.fieldOf("count").forGetter(EnormousEllipsoidPlacement::count),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("x_size").forGetter(EnormousEllipsoidPlacement::xLength),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("z_size").forGetter(EnormousEllipsoidPlacement::zLength),
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("y_size").forGetter(EnormousEllipsoidPlacement::yLength),
).apply(it, ::EnormousEllipsoidPlacement)
}
}
}
}

View File

@ -0,0 +1,81 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.IntProvider
import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.math.plus
import java.util.stream.Stream
interface IEllipsoidPlacement {
/**
* X position within ellipsoid sampler, expected to be in range -1 .. 1
*/
val x: FloatProvider
/**
* Z position within ellipsoid sampler, expected to be in range -1 .. 1
*/
val z: FloatProvider
/**
* Y position within ellipsoid sampler, expected to be in range -1 .. 1
*/
val y: FloatProvider
/**
* Total amount of block positions to generate
*/
val count: IntProvider
/**
* Ellipsoid size sampler on X axis
*/
val xLength: FloatProvider
/**
* Ellipsoid size sampler on Z axis
*/
val zLength: FloatProvider
/**
* Ellipsoid size sampler on Y axis
*/
val yLength: FloatProvider
fun getEllipsoidPositions(random: RandomSource, position: BlockPos): Stream<BlockPos> {
val count = count.sample(random)
if (count <= 0)
return Stream.empty()
val xLength = xLength.sample(random)
val zLength = zLength.sample(random)
val yLength = yLength.sample(random)
val xPow = xLength * xLength
val zPow = zLength * zLength
val yPow = yLength * yLength
return Stream.generate {
val x = this.x.sample(random) * xLength
val y = this.y.sample(random) * yLength
val z = this.z.sample(random) * zLength
BlockPos(x.toInt(), y.toInt(), z.toInt())
}
.limit(count.toLong() * 10)
.filter {
val (ellipsoidX, ellipsoidY, ellipsoidZ) = it
(ellipsoidX * ellipsoidX) / xPow +
(ellipsoidY * ellipsoidY) / yPow +
(ellipsoidZ * ellipsoidZ) / zPow <= 1.0f
}
.distinct()
.limit(count.toLong())
.map { it + position }
}
}

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.data.world
package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec

View File

@ -0,0 +1,125 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.ClampedNormalFloat
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.math.toBlockPos
import ru.dbotthepony.mc.otm.core.nextDouble
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import java.util.stream.Stream
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
class WormPlacement(
parameters: Parameters,
val wormLength: IntProvider,
val wormTurnChance: BooleanProvider,
val wormTurnXZ: FloatProvider,
val wormTurnXY: FloatProvider,
) : AbstractEnormousPlacement(parameters) {
override fun getPositions(center: BlockPos, random: RandomSource): Stream<BlockPos> {
var position = Vector.ZERO
val maxDistance = wormLength.sample(random)
// determine initial worm facing angle
var xzRotation = random.nextDouble(-PI / 2.0, PI / 2.0)
var xyRotation = random.nextDouble(-PI / 16.0, PI / 16.0)
var xzSin = sin(xzRotation)
var xzCos = cos(xzRotation)
var xySin = sin(xyRotation)
val positions = ArrayList<BlockPos>()
var prevPos = position.toBlockPos()
positions.add(prevPos)
for (traveledDistance in 0 .. maxDistance) {
// wormy turn
if (wormTurnChance.sample(random)) {
// wormy angle
// TODO: smooth turning, instead of snapping to new angle make it gradually face new angle
xzRotation += wormTurnXZ.sample(random)
xyRotation += wormTurnXY.sample(random)
xzSin = sin(xzRotation)
xzCos = cos(xzRotation)
xySin = sin(xyRotation)
}
// advance worm
position += Vector(xzCos, xySin, xzSin)
val calc = position.toBlockPos()
if (calc != prevPos) {
// if worm made a wurm, add new position to set
prevPos = calc
positions.add(calc)
}
}
return positions.stream().map { it + center }
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.WORM_PLACEMENT
}
companion object {
private fun increment(value: Float): Float {
var i = 1f
while (true) {
val enlarge = Float.MIN_VALUE * i
val sum = value + enlarge
if (sum != value) {
return sum
} else {
i += 1f
}
}
}
fun uniformTurnRate(degrees: Float): FloatProvider {
return uniformTurnRateRadians(degrees * DEGREES_TO_RADIANS)
}
fun uniformTurnRateRadians(rad: Float): FloatProvider {
require(rad >= 0f) { "Provided value must be non-negative, $rad given" }
if (rad == 0f) return ConstantFloat.ZERO
return UniformFloat.of(-rad, increment(rad))
}
fun normalDistributedTurnRate(deviation: Float): FloatProvider {
return normalDistributedTurnRateRadians(deviation * DEGREES_TO_RADIANS)
}
fun normalDistributedTurnRateRadians(deviation: Float): FloatProvider {
require(deviation >= 0f) { "Provided value must be non-negative, $deviation given" }
if (deviation == 0f) return ConstantFloat.ZERO
return ClampedNormalFloat.of(0f, deviation, -PI.toFloat() * 2f, PI.toFloat() * 2f)
}
private const val DEGREES_TO_RADIANS = PI.toFloat() / 180f
val CODEC: MapCodec<WormPlacement> = RecordCodecBuilder.mapCodec {
it.group(
PARAMETERS_CODEC.forGetter(WormPlacement::parameters),
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::wormLength),
BooleanProvider.CODEC.fieldOf("turn_chance").forGetter(WormPlacement::wormTurnChance),
FloatProvider.CODEC.fieldOf("turn_xz").forGetter(WormPlacement::wormTurnXZ),
FloatProvider.CODEC.fieldOf("turn_xy").forGetter(WormPlacement::wormTurnXY),
).apply(it, ::WormPlacement)
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B