Compare commits
98 Commits
1.21
...
worldgen-p
Author | SHA1 | Date | |
---|---|---|---|
3cdcb411f7 | |||
174a86b33c | |||
f451ea4df4 | |||
74d48a7dc3 | |||
5b463e8adb | |||
0743ea2c9a | |||
347a5465cc | |||
526578f76c | |||
ac1c9a1ad4 | |||
9cfbab6c3c | |||
07e6369454 | |||
ddab73bad1 | |||
a03c2d5eb4 | |||
7ca0ba1ce5 | |||
7a5a979169 | |||
e2369b3a24 | |||
2e104dcbce | |||
159125fb4b | |||
727111cf4a | |||
d08a928e04 | |||
398bd532f4 | |||
7e570747c2 | |||
d683ea1e38 | |||
f74dbbd84a | |||
2993ae61ca | |||
9577e205e7 | |||
ba83b89476 | |||
bf6fec7753 | |||
0af5fb7301 | |||
a4e40bd464 | |||
07b295ce45 | |||
ba492e0cee | |||
dca02893a4 | |||
ab6e3ad87f | |||
78c0a5d717 | |||
e150b57b65 | |||
1e6e38ea7d | |||
1acd105925 | |||
5d05fe3bb4 | |||
bc81103e38 | |||
18f9bc2654 | |||
03a1ab9197 | |||
055ca7ec43 | |||
ca67e796da | |||
9ee561d0fd | |||
e399fa8b68 | |||
d55fbbe62f | |||
b6b3cbed81 | |||
97c4922765 | |||
d2e7971726 | |||
e7f2e0551a | |||
84ca0c2a24 | |||
326490b73c | |||
ed04a8c4d2 | |||
611f4055e9 | |||
00ff8fa91e | |||
3ee539bc9d | |||
44b0bcd776 | |||
cc456958e1 | |||
5f76aa1661 | |||
bd8c5ed97b | |||
674c875249 | |||
7736762c86 | |||
274b5c059a | |||
45d7b30b8a | |||
abeeda8139 | |||
e179756995 | |||
df949a6861 | |||
88c3fe24b0 | |||
25a312b56f | |||
be97560f01 | |||
d06c32aec2 | |||
3ea9a7f386 | |||
8b0edc5d5f | |||
19e405d4f4 | |||
e1d3b36dab | |||
157d2c5498 | |||
01054a05d2 | |||
a89371007f | |||
70ac0f2c7f | |||
0ef34a00cc | |||
cbc95e8e5b | |||
0c15c93dcd | |||
92846cbad0 | |||
ab42d1c1fb | |||
b8468f7828 | |||
01a6ca27bf | |||
db3f22f3fc | |||
4da5ef81f7 | |||
536f69930c | |||
988f078d24 | |||
f6abe0f038 | |||
97d30d3a19 | |||
cc9ebef018 | |||
f9c8130e26 | |||
c0812687cb | |||
cd38e1a154 | |||
a903b9cff8 |
@ -209,9 +209,6 @@ dependencies {
|
||||
// runtimeOnly("curse.maven:integrated-tunnels-251389:4344632")
|
||||
|
||||
implementation("mekanism:Mekanism:${mc_version}-${mekanism_version}")
|
||||
|
||||
implementation("curse.maven:iron-chests-228756:5491156")
|
||||
implementation("curse.maven:iron-shulker-boxes-314911:5491246")
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,8 +268,6 @@ minecraft {
|
||||
|
||||
mixin {
|
||||
config("$mod_id.mixins.json")
|
||||
config("$mod_id.ironchest.mixins.json")
|
||||
config("$mod_id.ironshulkerbox.mixins.json")
|
||||
// config("$mod_id.ad_astra.mixins.json")
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@ import ru.dbotthepony.mc.otm.datagen.tags.addStructureTags
|
||||
import ru.dbotthepony.mc.otm.datagen.tags.addSuspiciousTags
|
||||
import ru.dbotthepony.mc.otm.datagen.tags.addTags
|
||||
import ru.dbotthepony.mc.otm.matter.MatterDataProvider
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistries
|
||||
import ru.dbotthepony.mc.otm.registry.game.MBlocks
|
||||
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
|
||||
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
|
||||
@ -560,6 +561,7 @@ object DataGen {
|
||||
val registrySetBuilder = RegistrySetBuilder()
|
||||
.add(Registries.DAMAGE_TYPE, ::registerDamageTypes)
|
||||
.add(Registries.CONFIGURED_FEATURE, ::registerConfiguredFeatures)
|
||||
.add(MRegistries.CONFIGURED_FEATURE, ::registerEnhancedConfiguredFeatures)
|
||||
.add(Registries.PLACED_FEATURE, ::registerPlacedFeatures)
|
||||
.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, ::registerBiomeModifiers)
|
||||
|
||||
|
@ -7,14 +7,15 @@ import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.tags.BiomeTags
|
||||
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.UniformFloat
|
||||
import net.minecraft.util.valueproviders.UniformInt
|
||||
import net.minecraft.world.level.levelgen.GenerationStep
|
||||
import net.minecraft.world.level.levelgen.VerticalAnchor
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
|
||||
import net.minecraft.world.level.levelgen.feature.Feature
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.ReplaceBlockConfiguration
|
||||
import net.minecraft.world.level.levelgen.heightproviders.VeryBiasedToBottomHeight
|
||||
import net.minecraft.world.level.levelgen.placement.CountPlacement
|
||||
import net.minecraft.world.level.levelgen.placement.HeightRangePlacement
|
||||
@ -25,20 +26,40 @@ 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.util.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.registry.MRegistries
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.StandardDeviationHeightProvider
|
||||
import ru.dbotthepony.mc.otm.registry.game.MBlocks
|
||||
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
|
||||
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacedFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.BlackHolePlacerFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.placement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EllipsoidPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedChainPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedCountPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedSplitPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.WormPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.wrap
|
||||
|
||||
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")
|
||||
|
||||
private fun key(name: String): ResourceKey<ConfiguredFeature<*, *>> {
|
||||
return ResourceKey.create(Registries.CONFIGURED_FEATURE, modLocation(name))
|
||||
}
|
||||
|
||||
private fun ekey(name: String): ResourceKey<EnhancedFeature.Configured<*, *>> {
|
||||
return ResourceKey.create(MRegistries.CONFIGURED_FEATURE, modLocation(name))
|
||||
}
|
||||
}
|
||||
|
||||
fun registerEnhancedConfiguredFeatures(context: BootstrapContext<EnhancedFeature.Configured<*, *>>) {
|
||||
}
|
||||
|
||||
fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>>) {
|
||||
@ -52,6 +73,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.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target)))
|
||||
//context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(MWorldGenFeatures.DEBUG_PLACEMENT, DebugPlacerFeature.Config(MBlocks.TRITANIUM_ORE.defaultBlockState())))
|
||||
}
|
||||
|
||||
run {
|
||||
@ -60,7 +83,7 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
|
||||
OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_DILITHIUM_ORE.defaultBlockState()),
|
||||
)
|
||||
|
||||
context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 3)))
|
||||
context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target)))
|
||||
}
|
||||
|
||||
context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(
|
||||
@ -71,7 +94,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")
|
||||
|
||||
@ -82,6 +105,12 @@ private object PlacedFeatures {
|
||||
|
||||
fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
||||
val configured = context.lookup(Registries.CONFIGURED_FEATURE)
|
||||
val econfigured = context.lookup(MRegistries.CONFIGURED_FEATURE)
|
||||
|
||||
val ringularity = OneOfFloatProvider.of(
|
||||
ClampedNormalFloat.of(0.4f, 0.2f, -2f, 2f),
|
||||
ClampedNormalFloat.of(-0.4f, 0.2f, -2f, 2f),
|
||||
)
|
||||
|
||||
run {
|
||||
val ore = configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE)
|
||||
@ -89,7 +118,7 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
||||
context.register(PlacedFeatures.NORMAL_TRITANIUM, PlacedFeature(
|
||||
ore,
|
||||
listOf(
|
||||
CountPlacement.of(UniformInt.of(2, 6)),
|
||||
CountPlacement.of(UniformInt.of(1, 3)),
|
||||
InSquarePlacement.spread(),
|
||||
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(10), 15.0))
|
||||
)
|
||||
@ -98,53 +127,93 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
||||
context.register(PlacedFeatures.DEEP_TRITANIUM, PlacedFeature(
|
||||
ore,
|
||||
listOf(
|
||||
CountPlacement.of(UniformInt.of(4, 8)),
|
||||
CountPlacement.of(UniformInt.of(3, 5)),
|
||||
InSquarePlacement.spread(),
|
||||
HeightRangePlacement.of(VeryBiasedToBottomHeight.of(VerticalAnchor.aboveBottom(4), VerticalAnchor.absolute(0), 16))
|
||||
)
|
||||
))
|
||||
|
||||
context.register(PlacedFeatures.CLOUD_TITANIUM, PlacedFeature(
|
||||
ore,
|
||||
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),
|
||||
context.register(
|
||||
PlacedFeatures.WORM_TRITANIUM,
|
||||
EnhancedPlacedFeature.configure(
|
||||
chunkScanRange = 24,
|
||||
seedMix = 9284343575495L,
|
||||
root = EnhancedChainPlacement(
|
||||
RarityFilter.onAverageOnceEvery(140).wrap(),
|
||||
InSquarePlacement.spread().wrap(),
|
||||
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(-40), 15.0)).wrap(),
|
||||
|
||||
EnhancedSplitPlacement(
|
||||
EnhancedSplitPlacement.Mode.COMBINE,
|
||||
// "heart"
|
||||
EllipsoidPlacement(
|
||||
count = UniformInt.of(600, 900),
|
||||
xLength = UniformFloat.of(9f, 12f),
|
||||
yLength = UniformFloat.of(9f, 12f),
|
||||
zLength = UniformFloat.of(9f, 12f),
|
||||
x = ringularity,
|
||||
y = ringularity,
|
||||
z = ringularity,
|
||||
),
|
||||
// "branches"
|
||||
EnhancedChainPlacement(
|
||||
EnhancedCountPlacement(UniformInt.of(2, 7)),
|
||||
WormPlacement(
|
||||
length = UniformInt.of(60, 120),
|
||||
turnRateXY = WormPlacement.normalDistributedTurnRate(2f, 3f),
|
||||
initialAngleXY = WormPlacement.normalDistributedTurnRate(3f, 4f),
|
||||
turnRateXZ = WormPlacement.normalDistributedTurnRate(30f),
|
||||
turnSpeedXZ = WormPlacement.constantTurnRate(10f),
|
||||
turnSpeedXY = WormPlacement.constantTurnRate(4f),
|
||||
turnChanceXY = BooleanProvider.Unbiased(6),
|
||||
turnChanceXZ = BooleanProvider.BiasedLinear(0.6f, 5),
|
||||
maxTravelUp = 90,
|
||||
maxTravelDown = 10,
|
||||
),
|
||||
EllipsoidPlacement(
|
||||
count = UniformInt.of(3, 6),
|
||||
xLength = ConstantFloat.of(4f),
|
||||
yLength = ConstantFloat.of(4f),
|
||||
zLength = ConstantFloat.of(4f),
|
||||
x = ringularity,
|
||||
y = ringularity,
|
||||
z = ringularity,
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE_SMALL).placement()
|
||||
)
|
||||
)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
run {
|
||||
val ore = configured.getOrThrow(ConfiguredFeatures.DILITHIUM)
|
||||
|
||||
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),
|
||||
context.register(
|
||||
PlacedFeatures.DILITHIUM,
|
||||
EnhancedPlacedFeature.configure(
|
||||
chunkScanRange = 6,
|
||||
seedMix = 237483209523709234L,
|
||||
root = EnhancedChainPlacement(
|
||||
RarityFilter.onAverageOnceEvery(120).wrap(),
|
||||
InSquarePlacement.spread().wrap(),
|
||||
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)).wrap(),
|
||||
EllipsoidPlacement(
|
||||
x = ringularity,
|
||||
y = ringularity,
|
||||
z = ringularity,
|
||||
count = UniformInt.of(8000, 16000),
|
||||
xLength = UniformFloat.of(30f, 70f),
|
||||
yLength = UniformFloat.of(40f, 90f),
|
||||
zLength = UniformFloat.of(30f, 70f),
|
||||
rotateXZ = BooleanProvider.AlwaysTrue,
|
||||
),
|
||||
ore.placement()
|
||||
)
|
||||
)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
val blackHole = configured.getOrThrow(ConfiguredFeatures.BLACK_HOLE)
|
||||
@ -179,7 +248,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
|
||||
|
@ -128,17 +128,13 @@ fun addItemModels(provider: MatteryItemModelProvider) {
|
||||
provider.generated(MItems.BREAD_MONSTER_SPAWN_EGG, modLocation("item/egg/bread_monster"))
|
||||
provider.generated(MItems.LOADER_SPAWN_EGG, modLocation("item/egg/loader"))
|
||||
|
||||
provider.capacitorWithGauge(MItems.BATTERY_CRUDE, 10, "battery_gauge_", modLocation("item/battery_tier0"))
|
||||
provider.capacitorWithGauge(MItems.BATTERY_BASIC, 10, "battery_gauge_", modLocation("item/battery_tier1"))
|
||||
provider.capacitorWithGauge(MItems.BATTERY_NORMAL, 10, "battery_gauge_", modLocation("item/battery_tier2"))
|
||||
provider.capacitorWithGauge(MItems.BATTERY_DENSE, 10, "battery_gauge_", modLocation("item/battery_tier3"))
|
||||
provider.capacitorWithGauge(MItems.BATTERY_CAPACITOR, 10, "battery_gauge_", modLocation("item/battery_tier4"))
|
||||
provider.generatedTiered(MItems.BATTERIES, "battery_tier")
|
||||
provider.generated(MItems.BATTERY_CREATIVE)
|
||||
provider.capacitorWithGauge(MItems.PROCEDURAL_BATTERY, 9, "battery_procedural_gauge_", modLocation("item/battery_procedural"))
|
||||
provider.generated(MItems.PROCEDURAL_BATTERY, modLocation("item/battery_procedural"))
|
||||
|
||||
provider.capacitorWithGauge(MItems.MATTER_CAPACITOR_BASIC, 8, "matter_capacitor_gauge_", modLocation("item/matter_capacitor_tier1"))
|
||||
provider.capacitorWithGauge(MItems.MATTER_CAPACITOR_NORMAL, 8, "matter_capacitor_gauge_", modLocation("item/matter_capacitor_tier2"))
|
||||
provider.capacitorWithGauge(MItems.MATTER_CAPACITOR_DENSE, 8, "matter_capacitor_gauge_", modLocation("item/matter_capacitor_tier3"))
|
||||
provider.generated(MItems.MATTER_CAPACITOR_BASIC, modLocation("item/matter_capacitor_tier1"))
|
||||
provider.generated(MItems.MATTER_CAPACITOR_NORMAL, modLocation("item/matter_capacitor_tier2"))
|
||||
provider.generated(MItems.MATTER_CAPACITOR_DENSE, modLocation("item/matter_capacitor_tier3"))
|
||||
provider.generated(MItems.MATTER_CAPACITOR_CREATIVE)
|
||||
|
||||
provider.generated(MItems.MachineUpgrades.Basic.BLANK, modLocation("item/machine_upgrade_tier1"))
|
||||
@ -186,8 +182,8 @@ fun addItemModels(provider: MatteryItemModelProvider) {
|
||||
provider.upgrade(MItems.MachineUpgrades.Creative.MATTER_STORAGE_FLAT_SMALL, "matter", "creative")
|
||||
|
||||
|
||||
provider.capacitorWithGauge(MItems.QUANTUM_BATTERY, 10, "battery_gauge_")
|
||||
provider.capacitorWithGauge(MItems.QUANTUM_CAPACITOR, 10, "battery_gauge_")
|
||||
provider.generated(MItems.QUANTUM_BATTERY)
|
||||
provider.generated(MItems.QUANTUM_CAPACITOR)
|
||||
provider.generated(MItems.QUANTUM_BATTERY_CREATIVE)
|
||||
|
||||
provider.generated(MItems.PATTERN_DRIVE_NORMAL, modLocation("item/pattern_drive_tier1"))
|
||||
|
@ -143,33 +143,6 @@ class MatteryItemModelProvider(event: GatherDataEvent) : ItemModelProvider(event
|
||||
.texture("layer1", modLocation("item/machine_upgrade_icon_$upgradeType"))
|
||||
}
|
||||
|
||||
fun capacitorWithGauge(item: Item, fillTextures: Int, gaugePrefix: String, baseTexture: ResourceLocation? = null) = exec {
|
||||
val path = item.registryName!!.path
|
||||
val texture = baseTexture ?: modLocation("item/$path")
|
||||
|
||||
val empty = withExistingParent("${path}_empty", GENERATED)
|
||||
.texture("layer0", texture)
|
||||
|
||||
val basic = withExistingParent(path, GENERATED)
|
||||
.texture("layer0", texture)
|
||||
.texture("layer1", modLocation("item/${gaugePrefix}$fillTextures"))
|
||||
.override()
|
||||
.predicate(modLocation("capacitor_gauge"), 0f)
|
||||
.model(empty)
|
||||
.end()
|
||||
|
||||
for (i in 1 .. fillTextures) {
|
||||
val model = withExistingParent("${path}_fill_$i", GENERATED)
|
||||
.texture("layer0", texture)
|
||||
.texture("layer1", modLocation("item/${gaugePrefix}$i"))
|
||||
|
||||
basic.override()
|
||||
.predicate(modLocation("capacitor_gauge"), i.toFloat() / fillTextures.toFloat())
|
||||
.model(model)
|
||||
.end()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ARMOR_TRIM_MATERIALS = listOf("quartz", "iron", "netherite", "redstone", "copper", "gold", "emerald", "diamond", "lapis", "amethyst")
|
||||
val GENERATED = ResourceLocation("minecraft", "item/generated")
|
||||
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.regular.entity.CopperChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(CopperChestBlockEntity.class)
|
||||
public abstract class CopperChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.copper(containerId, inventory, ((CopperChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.regular.entity.CrystalChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(CrystalChestBlockEntity.class)
|
||||
public abstract class CrystalChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.crystal(containerId, inventory, ((CrystalChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.regular.entity.DiamondChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(DiamondChestBlockEntity.class)
|
||||
public abstract class DiamondChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.diamond(containerId, inventory, ((DiamondChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.regular.entity.DirtChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(DirtChestBlockEntity.class)
|
||||
public abstract class DirtChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.dirt(containerId, inventory, ((DirtChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.regular.entity.GoldChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(GoldChestBlockEntity.class)
|
||||
public abstract class GoldChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.gold(containerId, inventory, ((GoldChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.regular.entity.IronChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(IronChestBlockEntity.class)
|
||||
public abstract class IronChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.iron(containerId, inventory, ((IronChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.regular.entity.ObsidianChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(ObsidianChestBlockEntity.class)
|
||||
public abstract class ObsidianChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.obsidian(containerId, inventory, ((ObsidianChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.trapped.entity.TrappedCopperChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(TrappedCopperChestBlockEntity.class)
|
||||
public abstract class TrappedCopperChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.copper(containerId, inventory, ((TrappedCopperChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.trapped.entity.TrappedCrystalChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(TrappedCrystalChestBlockEntity.class)
|
||||
public abstract class TrappedCrystalChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.crystal(containerId, inventory, ((TrappedCrystalChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.trapped.entity.TrappedDiamondChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(TrappedDiamondChestBlockEntity.class)
|
||||
public abstract class TrappedDiamondChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.diamond(containerId, inventory, ((TrappedDiamondChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.trapped.entity.TrappedDirtChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(TrappedDirtChestBlockEntity.class)
|
||||
public abstract class TrappedDirtChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.dirt(containerId, inventory, ((TrappedDirtChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.trapped.entity.TrappedGoldChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(TrappedGoldChestBlockEntity.class)
|
||||
public abstract class TrappedGoldChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.gold(containerId, inventory, ((TrappedGoldChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.trapped.entity.TrappedIronChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(TrappedIronChestBlockEntity.class)
|
||||
public abstract class TrappedIronChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.iron(containerId, inventory, ((TrappedIronChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironchest;
|
||||
|
||||
import com.progwml6.ironchest.common.block.trapped.entity.TrappedObsidianChestBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.MatteryIronChestMenu;
|
||||
|
||||
@Mixin(TrappedObsidianChestBlockEntity.class)
|
||||
public abstract class TrappedObsidianChestBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronChestMenu.obsidian(containerId, inventory, ((TrappedObsidianChestBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
|
||||
|
||||
import com.progwml6.ironshulkerbox.common.block.entity.CopperShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
|
||||
|
||||
@Mixin(CopperShulkerBoxBlockEntity.class)
|
||||
public abstract class CopperShulkerBoxBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronShulkerBoxMenu.copper(containerId, inventory, ((CopperShulkerBoxBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
|
||||
|
||||
import com.progwml6.ironshulkerbox.common.block.entity.CrystalShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
|
||||
|
||||
@Mixin(CrystalShulkerBoxBlockEntity.class)
|
||||
public abstract class CrystalShulkerBoxBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronShulkerBoxMenu.crystal(containerId, inventory, ((CrystalShulkerBoxBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
|
||||
|
||||
import com.progwml6.ironshulkerbox.common.block.entity.DiamondShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
|
||||
|
||||
@Mixin(DiamondShulkerBoxBlockEntity.class)
|
||||
public abstract class DiamondShulkerBoxBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronShulkerBoxMenu.diamond(containerId, inventory, ((DiamondShulkerBoxBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
|
||||
|
||||
import com.progwml6.ironshulkerbox.common.block.entity.GoldShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
|
||||
|
||||
@Mixin(GoldShulkerBoxBlockEntity.class)
|
||||
public abstract class GoldShulkerBoxBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronShulkerBoxMenu.gold(containerId, inventory, ((GoldShulkerBoxBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
|
||||
|
||||
import com.progwml6.ironshulkerbox.common.block.entity.IronShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
|
||||
|
||||
@Mixin(IronShulkerBoxBlockEntity.class)
|
||||
public abstract class IronShulkerBoxBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronShulkerBoxMenu.iron(containerId, inventory, ((IronShulkerBoxBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.mixin.ironshulkerbox;
|
||||
|
||||
import com.progwml6.ironshulkerbox.common.block.entity.ObsidianShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu;
|
||||
|
||||
@Mixin(ObsidianShulkerBoxBlockEntity.class)
|
||||
public abstract class ObsidianShulkerBoxBlockEntityMixin {
|
||||
@Overwrite(remap = false)
|
||||
public AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
|
||||
return MatteryIronShulkerBoxMenu.obsidian(containerId, inventory, ((ObsidianShulkerBoxBlockEntity)(Object) this));
|
||||
}
|
||||
}
|
@ -6,10 +6,8 @@ import net.neoforged.bus.api.EventPriority
|
||||
import net.neoforged.fml.common.Mod
|
||||
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
|
||||
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent
|
||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescription
|
||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescriptions
|
||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchManager
|
||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResult
|
||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResults
|
||||
import ru.dbotthepony.mc.otm.player.android.feature.EnderTeleporterFeature
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
|
||||
@ -44,10 +42,6 @@ import ru.dbotthepony.mc.otm.client.render.blockentity.BatteryBankRenderer
|
||||
import ru.dbotthepony.mc.otm.client.render.blockentity.MatterBatteryBankRenderer
|
||||
import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded
|
||||
import ru.dbotthepony.mc.otm.compat.curios.onCuriosSlotModifiersUpdated
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.IronChestMenuTypes
|
||||
import ru.dbotthepony.mc.otm.compat.ironchest.isIronChestLoaded
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.IronShulkersMenuTypes
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.isIronShulkersLoaded
|
||||
import ru.dbotthepony.mc.otm.compat.vanilla.MatteryChestMenu
|
||||
import ru.dbotthepony.mc.otm.compat.vanilla.VanillaMenuTypes
|
||||
import ru.dbotthepony.mc.otm.config.PlayerConfig
|
||||
@ -60,6 +54,7 @@ import ru.dbotthepony.mc.otm.config.ServerConfig
|
||||
import ru.dbotthepony.mc.otm.config.ToolsConfig
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||
import ru.dbotthepony.mc.otm.data.FlywheelMaterials
|
||||
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
|
||||
import ru.dbotthepony.mc.otm.data.world.DecimalProvider
|
||||
import ru.dbotthepony.mc.otm.entity.WitheredSkeletonSpawnHandler
|
||||
import ru.dbotthepony.mc.otm.item.ChestUpgraderItem
|
||||
@ -93,11 +88,14 @@ 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
|
||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
import ru.dbotthepony.mc.otm.server.triggers.KillAsAndroidTrigger
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
|
||||
import thedarkcolour.kotlinforforge.neoforge.forge.DIST
|
||||
import thedarkcolour.kotlinforforge.neoforge.forge.FORGE_BUS
|
||||
import thedarkcolour.kotlinforforge.neoforge.forge.LOADING_CONTEXT
|
||||
@ -133,24 +131,20 @@ 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)
|
||||
VanillaMenuTypes.register(MOD_BUS)
|
||||
|
||||
if (isIronChestLoaded) {
|
||||
IronChestMenuTypes.register(MOD_BUS)
|
||||
}
|
||||
|
||||
if (isIronShulkersLoaded) {
|
||||
IronShulkersMenuTypes.register(MOD_BUS)
|
||||
}
|
||||
|
||||
MCreativeTabs.initialize(MOD_BUS)
|
||||
|
||||
MOD_BUS.addListener(::registerNetworkPackets)
|
||||
|
||||
DecimalProvider.register(MOD_BUS)
|
||||
BooleanProvider.register(MOD_BUS)
|
||||
EnhancedFeature.register(MOD_BUS)
|
||||
EnhancedPlacement.register(MOD_BUS)
|
||||
AndroidResearchDescriptions.register(MOD_BUS)
|
||||
AndroidResearchResults.register(MOD_BUS)
|
||||
|
||||
|
@ -348,17 +348,6 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
|
||||
private var cache: BlockCapabilityCache<T, in Direction?>? = null
|
||||
private val listeners = Listenable.Impl<T?>()
|
||||
|
||||
private val lookupProvider = Supplier<T?> {
|
||||
if (isRemoved)
|
||||
return@Supplier null
|
||||
|
||||
val get = cache?.capability
|
||||
provider = Supplier { get }
|
||||
return@Supplier get
|
||||
}
|
||||
|
||||
private var provider: Supplier<T?> = lookupProvider
|
||||
|
||||
override fun addListener(listener: Consumer<T?>): Listenable.L {
|
||||
return listeners.addListener(listener)
|
||||
}
|
||||
@ -369,21 +358,23 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
|
||||
}
|
||||
|
||||
override fun get(): T? {
|
||||
return provider.get()
|
||||
if (isRemoved)
|
||||
return null
|
||||
|
||||
return cache?.capability
|
||||
}
|
||||
|
||||
val isPresent: Boolean
|
||||
get() = get() != null
|
||||
get() = cache?.capability != null
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = get() == null
|
||||
get() = cache?.capability == null
|
||||
|
||||
fun rebuildCache() {
|
||||
if (!SERVER_IS_LIVE) return
|
||||
val level = level as? ServerLevel
|
||||
|
||||
val creationVersion = ++currentVersion
|
||||
provider = lookupProvider
|
||||
|
||||
if (level == null) {
|
||||
cache = null
|
||||
@ -397,21 +388,12 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
|
||||
{ !isRemoved || creationVersion != currentVersion },
|
||||
// IllegalStateException("Do not call getCapability on an invalid cache or from the invalidation listener!")
|
||||
// what a shame.
|
||||
{
|
||||
provider = lookupProvider
|
||||
if (SERVER_IS_LIVE) onceServer { if (!isRemoved && creationVersion == currentVersion) listeners.accept(get()) }
|
||||
})
|
||||
{ if (SERVER_IS_LIVE) onceServer { if (!isRemoved && creationVersion == currentVersion) listeners.accept(cache?.capability) } })
|
||||
|
||||
onceServer {
|
||||
if (!isRemoved && creationVersion == currentVersion) listeners.accept(get())
|
||||
if (!isRemoved && creationVersion == currentVersion) listeners.accept(cache?.capability)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeCache() {
|
||||
cache = null
|
||||
currentVersion++
|
||||
provider = Supplier { null }
|
||||
}
|
||||
}
|
||||
|
||||
val syncher = SynchableGroup()
|
||||
@ -442,7 +424,6 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
|
||||
|
||||
override fun setRemoved() {
|
||||
super.setRemoved()
|
||||
capabilityCaches.forEach { it.removeCache() }
|
||||
unsubscribe()
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ abstract class EnergyCableBlockEntity(type: BlockEntityType<*>, blockPos: BlockP
|
||||
|
||||
val sides get() = energySides
|
||||
|
||||
val currentlyTransferringTo = ObjectArraySet<Pair<Node, CableSide>>()
|
||||
val currentlyTransferringTo = ObjectArraySet<Pair<Node, RelativeSide>>()
|
||||
|
||||
override fun onNeighbour(link: Link) {
|
||||
if (link is DirectionLink) {
|
||||
|
@ -74,8 +74,8 @@ private class LinkedPriorityQueue<T : Comparable<T>> {
|
||||
}
|
||||
|
||||
class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableGraph>() {
|
||||
private val livelyNodes = HashSet<Pair<EnergyCableBlockEntity.Node, EnergyCableBlockEntity.CableSide>>()
|
||||
private val livelyNodesList = ArrayList<Pair<EnergyCableBlockEntity.Node, EnergyCableBlockEntity.CableSide>>()
|
||||
private val livelyNodes = HashSet<Pair<EnergyCableBlockEntity.Node, RelativeSide>>()
|
||||
private val livelyNodesList = ArrayList<Pair<EnergyCableBlockEntity.Node, RelativeSide>>()
|
||||
|
||||
fun addLivelyNode(node: EnergyCableBlockEntity.Node) {
|
||||
when (contains(node)) {
|
||||
@ -83,7 +83,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
||||
ContainsStatus.DOES_NOT_BELONG -> throw IllegalArgumentException("$node does not belong to $this")
|
||||
ContainsStatus.CONTAINS -> {
|
||||
for (dir in RelativeSide.entries) {
|
||||
val pair = node to node.sides[dir]!!
|
||||
val pair = node to dir
|
||||
|
||||
if (livelyNodes.add(pair)) {
|
||||
livelyNodesList.add(pair)
|
||||
@ -589,7 +589,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
||||
|
||||
override fun onNodeRemoved(node: EnergyCableBlockEntity.Node) {
|
||||
for (dir in RelativeSide.entries) {
|
||||
val pair = node to node.sides[dir]!!
|
||||
val pair = node to dir
|
||||
|
||||
if (livelyNodes.remove(pair)) {
|
||||
check(livelyNodesList.remove(pair))
|
||||
@ -617,7 +617,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
||||
|
||||
override fun onNodeAdded(node: EnergyCableBlockEntity.Node) {
|
||||
for (dir in RelativeSide.entries) {
|
||||
val pair = node to node.sides[dir]!!
|
||||
val pair = node to dir
|
||||
check(livelyNodes.add(pair))
|
||||
livelyNodesList.add(pair)
|
||||
}
|
||||
@ -649,7 +649,8 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
||||
continue
|
||||
|
||||
try {
|
||||
val (node, side) = pair
|
||||
val (node, relSide) = pair
|
||||
val side = node.sides[relSide]!!
|
||||
|
||||
if (!side.isEnabled) {
|
||||
indicesToRemove.add(i)
|
||||
|
@ -29,7 +29,6 @@ import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
|
||||
import ru.dbotthepony.mc.otm.util.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.util.countingLazy
|
||||
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
|
||||
import ru.dbotthepony.mc.otm.util.math.toDecimal
|
||||
import java.util.function.BooleanSupplier
|
||||
|
||||
class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||
@ -198,25 +197,16 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||
|
||||
if (matter.storedMatter.isPositive) {
|
||||
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
|
||||
val matterTransferred = matter.extractMatter(capability.receiveMatter(rate.coerceAtMost(matter.storedMatter), true), true)
|
||||
val matterRatio = matterTransferred / rate
|
||||
val matterRatio = matter.extractMatter(capability.receiveMatter(rate.coerceAtMost(matter.storedMatter), true), true) / rate
|
||||
|
||||
if (matterRatio == Decimal.ZERO && matterTransferred > Decimal.ZERO) {
|
||||
// transferring very little matter, transfer this time for free
|
||||
val minRatio = minOf(matterRatio, energyRatio)
|
||||
|
||||
if (minRatio > Decimal.ZERO) {
|
||||
isWorking = true
|
||||
matter.extractMatter(capability.receiveMatter(matter.storedMatter, false), false)
|
||||
energy.extractEnergy(energyRate * minRatio, false)
|
||||
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
|
||||
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
|
||||
slot.setChanged()
|
||||
} else {
|
||||
val minRatio = minOf(matterRatio, energyRatio)
|
||||
|
||||
if (minRatio > Decimal.ZERO) {
|
||||
isWorking = true
|
||||
energy.extractEnergy(energyRate * minRatio, false)
|
||||
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
|
||||
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
|
||||
slot.setChanged()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (spitItemsWhenCantWork) {
|
||||
@ -272,28 +262,20 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||
initialCapacity = initialCapacity ?: it.storedMatter
|
||||
|
||||
hasCapacitors = true
|
||||
val rate = MachinesConfig.MatterBottler.RATE * (Decimal.ONE + upgrades.speedBonus.toDecimal())
|
||||
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (Decimal.ONE + upgrades.speedBonus.toDecimal())
|
||||
val rate = MachinesConfig.MatterBottler.RATE * (1.0 + upgrades.speedBonus)
|
||||
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (1.0 + upgrades.speedBonus)
|
||||
|
||||
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
|
||||
val matterExtracted = matter.receiveMatter(it.extractMatterChecked(rate, true), true)
|
||||
val matterRatio = matterExtracted / rate
|
||||
val matterRatio = matter.receiveMatter(it.extractMatterChecked(rate, true), true) / rate
|
||||
|
||||
if (matterRatio == Decimal.ZERO && matterExtracted > Decimal.ZERO) {
|
||||
// very little matter extracted, extract this one for free
|
||||
val minRatio = minOf(energyRatio, matterRatio)
|
||||
|
||||
if (minRatio > Decimal.ZERO) {
|
||||
any = true
|
||||
matter.receiveMatter(it.extractMatterChecked(matterExtracted, false), false)
|
||||
energy.extractEnergy(energyRate * energyRatio, false)
|
||||
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
|
||||
|
||||
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
|
||||
} else {
|
||||
val minRatio = minOf(energyRatio, matterRatio)
|
||||
|
||||
if (minRatio > Decimal.ZERO) {
|
||||
any = true
|
||||
energy.extractEnergy(energyRate * energyRatio, false)
|
||||
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
|
||||
|
||||
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
|
@ -31,7 +31,7 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
|
||||
import java.util.Collections.emptyIterator
|
||||
import java.util.stream.Stream
|
||||
|
||||
internal val isCosmeticArmorLoaded by lazy {
|
||||
val isCosmeticArmorLoaded by lazy {
|
||||
ModList.get().isLoaded("cosmeticarmorreworked")
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ import java.util.Collections.emptyIterator
|
||||
import java.util.stream.Stream
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
internal val isCuriosLoaded by lazy {
|
||||
val isCuriosLoaded by lazy {
|
||||
ModList.get().isLoaded(CuriosApi.MODID)
|
||||
}
|
||||
|
||||
|
@ -1,66 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.compat.ironchest
|
||||
|
||||
import com.progwml6.ironchest.IronChests
|
||||
import com.progwml6.ironchest.common.block.IronChestsTypes
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.flag.FeatureFlags
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.neoforged.bus.api.IEventBus
|
||||
import net.neoforged.fml.ModList
|
||||
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
|
||||
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.compat.vanilla.VanillaChestScreen
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
|
||||
import ru.dbotthepony.mc.otm.menu.makeSlots
|
||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||
|
||||
internal val isIronChestLoaded by lazy {
|
||||
ModList.get().isLoaded(IronChests.MODID)
|
||||
}
|
||||
|
||||
object IronChestMenuTypes {
|
||||
private val registrar = MDeferredRegister(Registries.MENU, OverdriveThatMatters.MOD_ID)
|
||||
|
||||
val IRON by registrar.register("ironchest_iron") { MenuType(MatteryIronChestMenu::iron, FeatureFlags.VANILLA_SET) }
|
||||
val GOLD by registrar.register("ironchest_gold") { MenuType(MatteryIronChestMenu::gold, FeatureFlags.VANILLA_SET) }
|
||||
val DIAMOND by registrar.register("ironchest_diamond") { MenuType(MatteryIronChestMenu::diamond, FeatureFlags.VANILLA_SET) }
|
||||
val COPPER by registrar.register("ironchest_copper") { MenuType(MatteryIronChestMenu::copper, FeatureFlags.VANILLA_SET) }
|
||||
val CRYSTAL by registrar.register("ironchest_crystal") { MenuType(MatteryIronChestMenu::crystal, FeatureFlags.VANILLA_SET) }
|
||||
val OBSIDIAN by registrar.register("ironchest_obsidian") { MenuType(MatteryIronChestMenu::obsidian, FeatureFlags.VANILLA_SET) }
|
||||
val DIRT by registrar.register("ironchest_dirt") { MenuType(MatteryIronChestMenu::dirt, FeatureFlags.VANILLA_SET) }
|
||||
|
||||
private fun provider(level: Level, pos: BlockPos, state: BlockState, blockEntity: BlockEntity?, context: Void?): IQuickStackContainer? {
|
||||
val container = blockEntity as? Container ?: return null
|
||||
return IQuickStackContainer.Simple(makeSlots(container, ::MatteryMenuSlot))
|
||||
}
|
||||
|
||||
fun registerCapabilities(event: RegisterCapabilitiesEvent) {
|
||||
IronChestsTypes.entries.map { IronChestsTypes.get(it) }.forEach {
|
||||
event.registerBlock(MatteryCapability.QUICK_STACK_CONTAINER, ::provider, *it.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
registrar.register(bus)
|
||||
bus.addListener(this::registerScreens)
|
||||
bus.addListener(this::registerCapabilities)
|
||||
}
|
||||
|
||||
private fun registerScreens(event: RegisterMenuScreensEvent) {
|
||||
event.register(IRON, ::VanillaChestScreen)
|
||||
event.register(GOLD, ::VanillaChestScreen)
|
||||
event.register(DIAMOND, ::VanillaChestScreen)
|
||||
event.register(COPPER, ::VanillaChestScreen)
|
||||
event.register(CRYSTAL, ::VanillaChestScreen)
|
||||
event.register(OBSIDIAN, ::VanillaChestScreen)
|
||||
event.register(DIRT, ::VanillaChestScreen)
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.compat.ironchest
|
||||
|
||||
import com.progwml6.ironchest.common.block.IronChestsTypes
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.SimpleContainer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import ru.dbotthepony.mc.otm.compat.vanilla.AbstractVanillaChestMenu
|
||||
import ru.dbotthepony.mc.otm.container.EnhancedContainer
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
|
||||
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
|
||||
import ru.dbotthepony.mc.otm.menu.makeSlots
|
||||
|
||||
class MatteryIronChestMenu(
|
||||
type: MenuType<*>, containerId: Int,
|
||||
inventory: Inventory, chestType: IronChestsTypes,
|
||||
container: Container = EnhancedContainer.Simple(chestType.size),
|
||||
) : AbstractVanillaChestMenu(type, containerId, inventory, container) {
|
||||
override val columns = chestType.rowLength
|
||||
override val rows = chestType.size / chestType.rowLength
|
||||
|
||||
override val containerSlots = if (chestType == IronChestsTypes.DIRT) makeSlots(container, ::MDirtChestSlot) else makeSlots(container, ::MatteryMenuSlot)
|
||||
|
||||
init {
|
||||
container.startOpen(player)
|
||||
addStorageSlot(containerSlots)
|
||||
addInventorySlots()
|
||||
}
|
||||
|
||||
override val quickMoveToStorage = QuickMoveInput.create(this, playerCombinedInventorySlots, containerSlots)
|
||||
override val quickMoveFromStorage = QuickMoveInput.create(this, containerSlots, playerInventorySlots, false)
|
||||
|
||||
class MDirtChestSlot(container: Container, slot: Int) : MatteryMenuSlot(container, slot) {
|
||||
override fun mayPlace(stack: ItemStack): Boolean = stack.isEmpty || stack.`is`(Items.DIRT)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun iron(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.IRON.size), chestType: IronChestsTypes = IronChestsTypes.IRON): MatteryIronChestMenu {
|
||||
return MatteryIronChestMenu(IronChestMenuTypes.IRON, containerId, inventory, chestType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun gold(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.GOLD.size), chestType: IronChestsTypes = IronChestsTypes.GOLD): MatteryIronChestMenu {
|
||||
return MatteryIronChestMenu(IronChestMenuTypes.GOLD, containerId, inventory, chestType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun diamond(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.DIAMOND.size), chestType: IronChestsTypes = IronChestsTypes.DIAMOND): MatteryIronChestMenu {
|
||||
return MatteryIronChestMenu(IronChestMenuTypes.DIAMOND, containerId, inventory, chestType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun copper(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.COPPER.size), chestType: IronChestsTypes = IronChestsTypes.COPPER): MatteryIronChestMenu {
|
||||
return MatteryIronChestMenu(IronChestMenuTypes.COPPER, containerId, inventory, chestType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun crystal(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.CRYSTAL.size), chestType: IronChestsTypes = IronChestsTypes.CRYSTAL): MatteryIronChestMenu {
|
||||
return MatteryIronChestMenu(IronChestMenuTypes.CRYSTAL, containerId, inventory, chestType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun obsidian(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.OBSIDIAN.size), chestType: IronChestsTypes = IronChestsTypes.OBSIDIAN): MatteryIronChestMenu {
|
||||
return MatteryIronChestMenu(IronChestMenuTypes.OBSIDIAN, containerId, inventory, chestType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun dirt(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronChestsTypes.DIRT.size), chestType: IronChestsTypes = IronChestsTypes.DIRT): MatteryIronChestMenu {
|
||||
return MatteryIronChestMenu(IronChestMenuTypes.DIRT, containerId, inventory, chestType, container)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.compat.ironshulkerbox
|
||||
|
||||
import com.progwml6.ironshulkerbox.IronShulkerBoxes
|
||||
import com.progwml6.ironshulkerbox.common.block.IronShulkerBoxesTypes
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.flag.FeatureFlags
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.neoforged.bus.api.IEventBus
|
||||
import net.neoforged.fml.ModList
|
||||
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
|
||||
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.compat.ironshulkerbox.MatteryIronShulkerBoxMenu.Slot
|
||||
import ru.dbotthepony.mc.otm.compat.vanilla.VanillaChestScreen
|
||||
import ru.dbotthepony.mc.otm.menu.makeSlots
|
||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||
import kotlin.collections.toTypedArray
|
||||
|
||||
internal val isIronShulkersLoaded by lazy {
|
||||
ModList.get().isLoaded(IronShulkerBoxes.MODID)
|
||||
}
|
||||
|
||||
object IronShulkersMenuTypes {
|
||||
private val registrar = MDeferredRegister(Registries.MENU, OverdriveThatMatters.MOD_ID)
|
||||
|
||||
val IRON by registrar.register("ironshulkerbox_iron") { MenuType(MatteryIronShulkerBoxMenu::iron, FeatureFlags.VANILLA_SET) }
|
||||
val GOLD by registrar.register("ironshulkerbox_gold") { MenuType(MatteryIronShulkerBoxMenu::gold, FeatureFlags.VANILLA_SET) }
|
||||
val DIAMOND by registrar.register("ironshulkerbox_diamond") { MenuType(MatteryIronShulkerBoxMenu::diamond, FeatureFlags.VANILLA_SET) }
|
||||
val COPPER by registrar.register("ironshulkerbox_copper") { MenuType(MatteryIronShulkerBoxMenu::copper, FeatureFlags.VANILLA_SET) }
|
||||
val CRYSTAL by registrar.register("ironshulkerbox_crystal") { MenuType(MatteryIronShulkerBoxMenu::crystal, FeatureFlags.VANILLA_SET) }
|
||||
val OBSIDIAN by registrar.register("ironshulkerbox_obsidian") { MenuType(MatteryIronShulkerBoxMenu::obsidian, FeatureFlags.VANILLA_SET) }
|
||||
|
||||
private fun provider(level: Level, pos: BlockPos, state: BlockState, blockEntity: BlockEntity?, context: Void?): IQuickStackContainer? {
|
||||
val container = blockEntity as? Container ?: return null
|
||||
return IQuickStackContainer.Simple(makeSlots(container, ::Slot))
|
||||
}
|
||||
|
||||
fun registerCapabilities(event: RegisterCapabilitiesEvent) {
|
||||
val colors = DyeColor.entries.toMutableList<DyeColor?>().also { it.add(0, null) }
|
||||
|
||||
IronShulkerBoxesTypes.entries.forEach { type ->
|
||||
event.registerBlock(MatteryCapability.QUICK_STACK_CONTAINER, ::provider, *colors.map { IronShulkerBoxesTypes.get(type, it) }.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
registrar.register(bus)
|
||||
bus.addListener(this::registerScreens)
|
||||
bus.addListener(this::registerCapabilities)
|
||||
}
|
||||
|
||||
private fun registerScreens(event: RegisterMenuScreensEvent) {
|
||||
event.register(IRON, ::VanillaChestScreen)
|
||||
event.register(GOLD, ::VanillaChestScreen)
|
||||
event.register(DIAMOND, ::VanillaChestScreen)
|
||||
event.register(COPPER, ::VanillaChestScreen)
|
||||
event.register(CRYSTAL, ::VanillaChestScreen)
|
||||
event.register(OBSIDIAN, ::VanillaChestScreen)
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.compat.ironshulkerbox
|
||||
|
||||
import com.progwml6.ironshulkerbox.common.block.IronShulkerBoxesTypes
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.SimpleContainer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.compat.vanilla.AbstractVanillaChestMenu
|
||||
import ru.dbotthepony.mc.otm.container.EnhancedContainer
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
|
||||
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
|
||||
import ru.dbotthepony.mc.otm.menu.makeSlots
|
||||
|
||||
class MatteryIronShulkerBoxMenu(
|
||||
type: MenuType<*>, containerId: Int,
|
||||
inventory: Inventory, boxType: IronShulkerBoxesTypes,
|
||||
container: Container = EnhancedContainer.Simple(boxType.size)
|
||||
) : AbstractVanillaChestMenu(type, containerId, inventory, container) {
|
||||
override val rows: Int = boxType.size / boxType.rowLength
|
||||
override val columns: Int = boxType.rowLength
|
||||
|
||||
override val containerSlots = makeSlots(container, ::Slot)
|
||||
|
||||
init {
|
||||
container.startOpen(player)
|
||||
addStorageSlot(containerSlots)
|
||||
addInventorySlots()
|
||||
}
|
||||
|
||||
override val quickMoveToStorage = QuickMoveInput.create(this, playerCombinedInventorySlots, containerSlots)
|
||||
override val quickMoveFromStorage = QuickMoveInput.create(this, containerSlots, playerInventorySlots, false)
|
||||
|
||||
class Slot(container: Container, slot: Int) : MatteryMenuSlot(container, slot) {
|
||||
override fun mayPlace(stack: ItemStack): Boolean {
|
||||
return super.mayPlace(stack) && stack.item.canFitInsideContainerItems()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun iron(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.IRON.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.IRON): MatteryIronShulkerBoxMenu {
|
||||
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.IRON, containerId, inventory, boxType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun gold(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.GOLD.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.GOLD): MatteryIronShulkerBoxMenu {
|
||||
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.GOLD, containerId, inventory, boxType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun diamond(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.DIAMOND.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.DIAMOND): MatteryIronShulkerBoxMenu {
|
||||
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.DIAMOND, containerId, inventory, boxType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun copper(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.COPPER.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.COPPER): MatteryIronShulkerBoxMenu {
|
||||
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.COPPER, containerId, inventory, boxType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun crystal(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.CRYSTAL.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.CRYSTAL): MatteryIronShulkerBoxMenu {
|
||||
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.CRYSTAL, containerId, inventory, boxType, container)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun obsidian(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(IronShulkerBoxesTypes.OBSIDIAN.size), boxType: IronShulkerBoxesTypes = IronShulkerBoxesTypes.OBSIDIAN): MatteryIronShulkerBoxMenu {
|
||||
return MatteryIronShulkerBoxMenu(IronShulkersMenuTypes.OBSIDIAN, containerId, inventory, boxType, container)
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import net.minecraft.world.inventory.Slot
|
||||
import net.neoforged.fml.ModList
|
||||
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
|
||||
|
||||
internal val isItemBordersLoaded by lazy {
|
||||
val isItemBordersLoaded by lazy {
|
||||
ModList.get().isLoaded("itemborders")
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -0,0 +1,142 @@
|
||||
package ru.dbotthepony.mc.otm.data.world
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.neoforged.bus.api.IEventBus
|
||||
import ru.dbotthepony.mc.otm.data.codec.inRange
|
||||
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
|
||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistries
|
||||
|
||||
interface BooleanProvider {
|
||||
interface Type<T : BooleanProvider> {
|
||||
val codec: MapCodec<T>
|
||||
}
|
||||
|
||||
fun instance(): Instance
|
||||
val type: Type<*>
|
||||
|
||||
fun interface Instance {
|
||||
fun sample(random: RandomSource): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Each time boolean is sampled, there is a fixed chance for it to test true
|
||||
*/
|
||||
data class Unbiased(val chance: Float) : BooleanProvider, Instance {
|
||||
constructor(chance: Int) : this(1f / chance)
|
||||
|
||||
override fun instance(): Instance {
|
||||
return this
|
||||
}
|
||||
|
||||
override val type: Type<*>
|
||||
get() = Companion
|
||||
|
||||
override fun sample(random: RandomSource): Boolean {
|
||||
return random.nextFloat() <= chance
|
||||
}
|
||||
|
||||
companion object : Type<Unbiased> {
|
||||
override val codec: MapCodec<Unbiased> = RecordCodecBuilder.mapCodec {
|
||||
it.group(Codec.FLOAT.inRange(0f, 1f).fieldOf("chance").forGetter(Unbiased::chance)).apply(it, ::Unbiased)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Successful roll chance is specified as follows:
|
||||
* ```
|
||||
* base_chance * failures
|
||||
* ```
|
||||
*
|
||||
* `failures` starts at 1, and increases each time roll failed, eventually reaching 100%
|
||||
* if no successful rolls have been made.
|
||||
*
|
||||
* Once roll is successful, `failures` is reset back to 1.
|
||||
*/
|
||||
data class BiasedLinear(val baseChance: Float) : BooleanProvider {
|
||||
constructor(middle: Float, at: Int) : this(middle / at)
|
||||
|
||||
private class I(private val baseChance: Float) : Instance {
|
||||
var lastSuccess = 1f
|
||||
|
||||
override fun sample(random: RandomSource): Boolean {
|
||||
val success = random.nextFloat() <= lastSuccess * baseChance
|
||||
|
||||
if (success)
|
||||
lastSuccess = 1f
|
||||
else
|
||||
lastSuccess += 1f
|
||||
|
||||
return success
|
||||
}
|
||||
}
|
||||
|
||||
override fun instance(): Instance {
|
||||
return I(baseChance)
|
||||
}
|
||||
|
||||
override val type: Type<*>
|
||||
get() = Companion
|
||||
|
||||
companion object : Type<BiasedLinear> {
|
||||
override val codec: MapCodec<BiasedLinear> = RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
Codec.FLOAT.inRange(0f, 1f).fieldOf("base_chance").forGetter(BiasedLinear::baseChance),
|
||||
).apply(it, ::BiasedLinear)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AlwaysTrue : BooleanProvider, Instance, Type<AlwaysTrue> {
|
||||
override fun instance(): Instance {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun sample(random: RandomSource): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override val type: Type<*>
|
||||
get() = this
|
||||
|
||||
override val codec: MapCodec<AlwaysTrue> = MapCodec.unit(this)
|
||||
}
|
||||
|
||||
object AlwaysFalse : BooleanProvider, Instance, Type<AlwaysFalse> {
|
||||
override fun instance(): Instance {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun sample(random: RandomSource): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override val type: Type<*>
|
||||
get() = this
|
||||
|
||||
override val codec: MapCodec<AlwaysFalse> = MapCodec.unit(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val registrar = MDeferredRegister(MRegistries.BOOLEAN_PROVIDER)
|
||||
|
||||
init {
|
||||
registrar.register("unbiased") { Unbiased.Companion }
|
||||
registrar.register("linear_bias") { BiasedLinear.Companion }
|
||||
registrar.register("true") { AlwaysTrue }
|
||||
registrar.register("false") { AlwaysFalse }
|
||||
}
|
||||
|
||||
val CODEC: Codec<BooleanProvider> by lazy {
|
||||
MBuiltInRegistries.BOOLEAN_PROVIDER.byNameCodec().dispatch({ it.type }, { it.codec })
|
||||
}
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
registrar.register(bus)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.data.world
|
||||
|
||||
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.PlacementContext
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
|
||||
import ru.dbotthepony.mc.otm.util.math.component1
|
||||
import ru.dbotthepony.mc.otm.util.math.component2
|
||||
import ru.dbotthepony.mc.otm.util.math.component3
|
||||
import ru.dbotthepony.mc.otm.util.math.minus
|
||||
import ru.dbotthepony.mc.otm.util.math.plus
|
||||
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
|
||||
import java.util.stream.Stream
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
// aka "cloud placement"
|
||||
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() {
|
||||
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 getPositions(
|
||||
context: PlacementContext,
|
||||
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())
|
||||
}
|
||||
|
||||
override fun type(): PlacementModifierType<*> {
|
||||
return MPlacementModifiers.ELLIPSOID_PLACEMENT
|
||||
}
|
||||
|
||||
companion object {
|
||||
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),
|
||||
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),
|
||||
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("y_size").forGetter(EllipsoidPlacement::yLength),
|
||||
).apply(it, ::EllipsoidPlacement)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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.util.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)
|
||||
}
|
||||
}
|
||||
}
|
@ -163,13 +163,6 @@ class MatterDustItem : Item(Properties().stacksTo(64)), IMatterItem {
|
||||
return matter(stack) >= ItemsConfig.MATTER_DUST_CAPACITY
|
||||
}
|
||||
|
||||
fun makeStack(matterValue: Decimal = Decimal.ONE, count: Int = 1): ItemStack {
|
||||
val stack = ItemStack(this, count)
|
||||
matter(stack, matterValue)
|
||||
|
||||
return stack
|
||||
}
|
||||
|
||||
override fun overrideStackedOnOther(
|
||||
pStack: ItemStack,
|
||||
pSlot: Slot,
|
||||
|
@ -1,7 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.network
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.UUIDUtil
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
@ -11,7 +10,6 @@ import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import ru.dbotthepony.kommons.math.RGBAColor
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||
import ru.dbotthepony.mc.otm.util.math.RelativeSide
|
||||
import ru.dbotthepony.mc.otm.util.readDecimal
|
||||
import ru.dbotthepony.mc.otm.util.writeDecimal
|
||||
import ru.dbotthepony.mc.otm.util.readBlockType
|
||||
@ -37,10 +35,6 @@ object StreamCodecs {
|
||||
val RESOURCE_LOCATION = ResourceLocation.STREAM_CODEC.wrap()
|
||||
val BLOCK_STATE: MatteryStreamCodec<ByteBuf, BlockState> = ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY).wrap()
|
||||
val BLOCK_TYPE = MatteryStreamCodec.Of(FriendlyByteBuf::writeBlockType, FriendlyByteBuf::readBlockType)
|
||||
val RELATIVE_SIDE = MatteryStreamCodec.Enum<FriendlyByteBuf, RelativeSide>(RelativeSide::class.java)
|
||||
val DIRECTION = MatteryStreamCodec.Enum<FriendlyByteBuf, Direction>(Direction::class.java)
|
||||
val RELATIVE_SIDE_NULLABLE = RELATIVE_SIDE.nullable()
|
||||
val DIRECTION_NULLABLE = DIRECTION.nullable()
|
||||
|
||||
val RGBA: MatteryStreamCodec<ByteBuf, RGBAColor> = StreamCodec.of<ByteBuf, RGBAColor>(
|
||||
{ s, v -> s.writeFloat(v.red); s.writeFloat(v.green); s.writeFloat(v.blue); s.writeFloat(v.alpha) },
|
||||
|
@ -1,19 +1,8 @@
|
||||
package ru.dbotthepony.mc.otm.network.syncher
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.ints.Int2ReferenceLinkedOpenHashMap
|
||||
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap
|
||||
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap
|
||||
import it.unimi.dsi.fastutil.ints.IntArraySet
|
||||
import it.unimi.dsi.fastutil.ints.IntCollection
|
||||
import it.unimi.dsi.fastutil.ints.IntIterator
|
||||
import it.unimi.dsi.fastutil.ints.IntSet
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceCollection
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceSet
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.mc.otm.util.IDAllocator
|
||||
@ -21,39 +10,29 @@ import java.io.Closeable
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* Constructs a new [DynamicSynchableGroup] where [T] is itself an [ISynchable]
|
||||
*/
|
||||
fun <T : ISynchable> DynamicSynchableGroup(reader: RegistryFriendlyByteBuf.() -> T, writer: T.(RegistryFriendlyByteBuf) -> Unit): DynamicSynchableGroup<T> {
|
||||
return DynamicSynchableGroup(reader, { this }, writer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncher group/set, which deals with synchables of only one type, and which are created and removed
|
||||
* on remote (e.g. server adding and removing synchables at will), which makes it distinct from
|
||||
* [SynchableGroup], in which attached synchables are created/removed manually on both sides.
|
||||
*/
|
||||
class DynamicSynchableGroup<T : Any>(
|
||||
class DynamicSynchableGroup<T : ISynchable>(
|
||||
/**
|
||||
* Constructs new [T] instance locally, when remote created one.
|
||||
* Data written by [writer] must be read here, if there is any.
|
||||
*/
|
||||
private val reader: RegistryFriendlyByteBuf.() -> T,
|
||||
|
||||
/**
|
||||
* Gets an [ISynchable] out of value [T] for synching state
|
||||
*/
|
||||
private val synchable: T.() -> ISynchable,
|
||||
|
||||
/**
|
||||
* Allows to write additional data to network stream during
|
||||
* first-time networking of [T] to remote
|
||||
*/
|
||||
private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {},
|
||||
) : ISynchable, ReferenceSet<T> {
|
||||
private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {}
|
||||
) : ISynchable, MutableSet<T> {
|
||||
constructor(factory: () -> T) : this({ factory() }, {})
|
||||
|
||||
private inner class RemoteState(val listener: Runnable) : IRemoteState {
|
||||
private inner class RemoteSlot(val slot: Slot<T>, fromConstructor: Boolean) : Runnable, Closeable {
|
||||
val remoteState = synchable(slot.value).createRemoteState(this)
|
||||
val remoteState = slot.synchable.createRemoteState(this)
|
||||
val isDirty = AtomicBoolean(true)
|
||||
var isRemoved = false
|
||||
private set
|
||||
@ -112,7 +91,6 @@ class DynamicSynchableGroup<T : Any>(
|
||||
checkNotNull(removed)
|
||||
check(removed.slot == element)
|
||||
removed.close()
|
||||
removals.add(element.id)
|
||||
listener.run()
|
||||
}
|
||||
|
||||
@ -136,7 +114,7 @@ class DynamicSynchableGroup<T : Any>(
|
||||
firstTime.forEach {
|
||||
stream.writeByte(ADD_ENTRY)
|
||||
stream.writeVarInt(it.slot.id)
|
||||
writer(it.slot.value, stream)
|
||||
writer(it.slot.synchable, stream)
|
||||
}
|
||||
|
||||
firstTime.clear()
|
||||
@ -199,12 +177,11 @@ class DynamicSynchableGroup<T : Any>(
|
||||
}
|
||||
}
|
||||
|
||||
private class Slot<T : Any>(val value: T, val id: Int)
|
||||
private data class Slot<T : Any>(val synchable: T, val id: Int)
|
||||
|
||||
private var defaultReturnValue: T? = null
|
||||
private val remoteStates = ArrayList<RemoteState>()
|
||||
private val value2slot = Reference2ObjectLinkedOpenHashMap<T, Slot<T>>()
|
||||
private val id2slot = Int2ReferenceLinkedOpenHashMap<Slot<T>>()
|
||||
private val value2slot = HashMap<T, Slot<T>>()
|
||||
private val id2slot = Int2ObjectOpenHashMap<Slot<T>>()
|
||||
private val idAllocator = IDAllocator()
|
||||
|
||||
override val hasRemotes: Boolean
|
||||
@ -222,7 +199,7 @@ class DynamicSynchableGroup<T : Any>(
|
||||
REMOVE_ENTRY -> {
|
||||
val id = stream.readVarInt()
|
||||
val slot = checkNotNull(id2slot.remove(id)) { "No such slot with ID: $id" }
|
||||
check(value2slot.remove(slot.value) == slot)
|
||||
check(value2slot.remove(slot.synchable) == value2slot)
|
||||
remoteStates.forEach { it.remove(slot) }
|
||||
}
|
||||
|
||||
@ -235,7 +212,7 @@ class DynamicSynchableGroup<T : Any>(
|
||||
|
||||
if (id2slot.containsKey(id)) {
|
||||
val slot = id2slot.remove(id)!!
|
||||
check(value2slot.remove(slot.value) == slot)
|
||||
check(value2slot.remove(slot.synchable) == value2slot)
|
||||
remoteStates.forEach { it.remove(slot) }
|
||||
}
|
||||
|
||||
@ -248,7 +225,7 @@ class DynamicSynchableGroup<T : Any>(
|
||||
SYNC_ENTRY -> {
|
||||
val id = stream.readVarInt()
|
||||
val slot = checkNotNull(id2slot.get(id)) { "No such slot with ID: $id" }
|
||||
synchable(slot.value).read(stream)
|
||||
slot.synchable.read(stream)
|
||||
}
|
||||
|
||||
CLEAR -> {
|
||||
@ -310,8 +287,8 @@ class DynamicSynchableGroup<T : Any>(
|
||||
return value2slot.isEmpty()
|
||||
}
|
||||
|
||||
override fun iterator(): ObjectIterator<T> {
|
||||
return object : ObjectIterator<T> {
|
||||
override fun iterator(): MutableIterator<T> {
|
||||
return object : MutableIterator<T> {
|
||||
private val parent = value2slot.values.iterator()
|
||||
private var last: KOptional<Slot<T>> = KOptional()
|
||||
|
||||
@ -322,7 +299,7 @@ class DynamicSynchableGroup<T : Any>(
|
||||
override fun next(): T {
|
||||
val slot = parent.next()
|
||||
last = KOptional(slot)
|
||||
return slot.value
|
||||
return slot.synchable
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
@ -335,15 +312,15 @@ class DynamicSynchableGroup<T : Any>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeBySlot(slot: Slot<T>) {
|
||||
value2slot.remove(slot.value)
|
||||
id2slot.remove(slot.id)
|
||||
override fun remove(element: T): Boolean {
|
||||
if (element !in value2slot) {
|
||||
return false
|
||||
}
|
||||
|
||||
val slot = value2slot.remove(element)!!
|
||||
checkNotNull(id2slot.remove(slot.id))
|
||||
remoteStates.forEach { it.remove(slot) }
|
||||
idAllocator.release(slot.id)
|
||||
}
|
||||
|
||||
override fun remove(element: T): Boolean {
|
||||
removeBySlot(value2slot.remove(element) ?: return false)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -367,341 +344,6 @@ class DynamicSynchableGroup<T : Any>(
|
||||
return any
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of this synchable group as an [Int2ReferenceMap]. Changes made to [DynamicSynchableGroup] are reflected in this map
|
||||
* and vice versa. Entries can be put at arbitrary keys, but negative keys should be avoided, since they are not efficiently
|
||||
* networked, and hence will create a lot of network traffic if synchables with negative keys get frequently updated.
|
||||
*/
|
||||
val asMap: Int2ReferenceMap<T> by lazy {
|
||||
object : Int2ReferenceMap<T> {
|
||||
override fun isEmpty(): Boolean {
|
||||
return this@DynamicSynchableGroup.isEmpty()
|
||||
}
|
||||
|
||||
override fun remove(key: Int): T? {
|
||||
val slot = id2slot.remove(key) ?: return null
|
||||
removeBySlot(slot)
|
||||
return slot.value
|
||||
}
|
||||
|
||||
override fun getOrDefault(key: Int, defaultValue: T): T {
|
||||
return super.getOrDefault(key, defaultValue)
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = this@DynamicSynchableGroup.size
|
||||
|
||||
override fun put(key: Int, value: T): T? {
|
||||
val slot = Slot(value, key)
|
||||
val oldSlot = id2slot.put(key, slot)
|
||||
|
||||
if (oldSlot?.value === value)
|
||||
return value
|
||||
|
||||
if (oldSlot != null) {
|
||||
remoteStates.forEach { it.remove(oldSlot) }
|
||||
value2slot.remove(oldSlot.value)
|
||||
} else {
|
||||
idAllocator.allocate(key)
|
||||
}
|
||||
|
||||
value2slot[value] = slot
|
||||
remoteStates.forEach { it.add(slot) }
|
||||
|
||||
return oldSlot?.value
|
||||
}
|
||||
|
||||
override fun get(key: Int): T? {
|
||||
return id2slot.get(key)?.value ?: defaultReturnValue
|
||||
}
|
||||
|
||||
override fun containsKey(key: Int): Boolean {
|
||||
return id2slot.containsKey(key)
|
||||
}
|
||||
|
||||
override fun defaultReturnValue(rv: T?) {
|
||||
defaultReturnValue = rv
|
||||
}
|
||||
|
||||
override fun defaultReturnValue(): T? {
|
||||
return defaultReturnValue
|
||||
}
|
||||
|
||||
override fun putAll(from: Map<out Int, T>) {
|
||||
from.forEach { (k, v) ->
|
||||
put(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
override fun containsValue(value: T): Boolean {
|
||||
return value2slot.containsKey(value)
|
||||
}
|
||||
|
||||
private val entrySet: ObjectSet<Int2ReferenceMap.Entry<T>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
object : ObjectSet<Int2ReferenceMap.Entry<T>> {
|
||||
inner class Entry(private val ikey: Int) : Int2ReferenceMap.Entry<T> {
|
||||
override val value: T
|
||||
get() = id2slot[ikey]?.value ?: throw NoSuchElementException()
|
||||
|
||||
override fun setValue(newValue: T): T? {
|
||||
return put(ikey, newValue)
|
||||
}
|
||||
|
||||
override fun getIntKey(): Int {
|
||||
return ikey
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return this === other || other is Int2ReferenceMap.Entry<*> && other.intKey == ikey && other.value === id2slot[ikey]?.value
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return ikey * 31 + (id2slot[ikey] as Slot<T>?).hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Map.Entry[$ikey, ${id2slot[ikey]?.value}]"
|
||||
}
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = id2slot.size
|
||||
|
||||
override fun add(element: Int2ReferenceMap.Entry<T>): Boolean {
|
||||
return put(element.intKey, element.value) != null
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<Int2ReferenceMap.Entry<T>>): Boolean {
|
||||
var any = false
|
||||
elements.forEach { any = add(it) || any }
|
||||
return any
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
this@DynamicSynchableGroup.clear()
|
||||
}
|
||||
|
||||
override fun iterator(): ObjectIterator<Int2ReferenceMap.Entry<T>> {
|
||||
return object : ObjectIterator<Int2ReferenceMap.Entry<T>> {
|
||||
private val parent = id2slot.int2ReferenceEntrySet().iterator()
|
||||
private var last: Slot<T>? = null
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return parent.hasNext()
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
if (last == null)
|
||||
throw NoSuchElementException()
|
||||
|
||||
parent.remove()
|
||||
removeBySlot(last!!)
|
||||
last = null
|
||||
}
|
||||
|
||||
override fun next(): Int2ReferenceMap.Entry<T> {
|
||||
val value = parent.next()
|
||||
last = value.value
|
||||
return Entry(value.intKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(element: Int2ReferenceMap.Entry<T>): Boolean {
|
||||
val slot = id2slot.get(element.intKey)
|
||||
|
||||
if (slot?.value === element.value) {
|
||||
this@DynamicSynchableGroup.removeBySlot(slot)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<Int2ReferenceMap.Entry<T>>): Boolean {
|
||||
var any = false
|
||||
elements.forEach { any = remove(it) || any }
|
||||
return any
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<Int2ReferenceMap.Entry<T>>): Boolean {
|
||||
if (elements.isEmpty()) {
|
||||
val isNotEmpty = id2slot.isNotEmpty()
|
||||
this@DynamicSynchableGroup.clear()
|
||||
return isNotEmpty
|
||||
}
|
||||
|
||||
var any = false
|
||||
val itr = id2slot.int2ReferenceEntrySet().iterator()
|
||||
|
||||
for (entry in itr) {
|
||||
if (elements.none { it.intKey == entry.intKey && it.value === entry.value.value }) {
|
||||
any = true
|
||||
itr.remove()
|
||||
removeBySlot(entry.value)
|
||||
}
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
|
||||
override fun contains(element: Int2ReferenceMap.Entry<T>): Boolean {
|
||||
return id2slot.get(element.intKey)?.value === element.value
|
||||
}
|
||||
|
||||
override fun containsAll(elements: Collection<Int2ReferenceMap.Entry<T>>): Boolean {
|
||||
return elements.all { contains(it) }
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return id2slot.isEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun int2ReferenceEntrySet(): ObjectSet<Int2ReferenceMap.Entry<T>> {
|
||||
return entrySet
|
||||
}
|
||||
|
||||
override val keys: IntSet by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
object : IntSet {
|
||||
override fun add(key: Int): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun addAll(c: IntCollection): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<Int>): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
this@DynamicSynchableGroup.clear()
|
||||
}
|
||||
|
||||
override fun iterator(): IntIterator {
|
||||
return object : IntIterator {
|
||||
private val parent = id2slot.keys.iterator()
|
||||
private var last = -1
|
||||
private var hasLast = false
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return parent.hasNext()
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
if (!hasLast)
|
||||
throw NoSuchElementException()
|
||||
|
||||
hasLast = false
|
||||
val slot = id2slot.get(last)
|
||||
parent.remove()
|
||||
removeBySlot(slot)
|
||||
}
|
||||
|
||||
override fun nextInt(): Int {
|
||||
last = parent.nextInt()
|
||||
hasLast = true
|
||||
return last
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(k: Int): Boolean {
|
||||
removeBySlot(id2slot.get(k) ?: return false)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun removeAll(c: IntCollection): Boolean {
|
||||
val itr = c.iterator()
|
||||
var any = false
|
||||
|
||||
while (itr.hasNext()) {
|
||||
any = remove(itr.nextInt()) || any
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<Int>): Boolean {
|
||||
val itr = elements.iterator()
|
||||
var any = false
|
||||
|
||||
while (itr.hasNext()) {
|
||||
any = remove(itr.next()) || any
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
|
||||
override fun retainAll(c: IntCollection): Boolean {
|
||||
val itr = id2slot.values.iterator()
|
||||
var any = false
|
||||
|
||||
for (slot in itr) {
|
||||
if (!c.contains(slot.id)) {
|
||||
itr.remove()
|
||||
check(value2slot.remove(slot.value) == slot)
|
||||
remoteStates.forEach { it.remove(slot) }
|
||||
any = true
|
||||
}
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<Int>): Boolean {
|
||||
val itr = id2slot.values.iterator()
|
||||
var any = false
|
||||
|
||||
for (slot in itr) {
|
||||
if (!elements.contains(slot.id)) {
|
||||
itr.remove()
|
||||
check(value2slot.remove(slot.value) == slot)
|
||||
remoteStates.forEach { it.remove(slot) }
|
||||
any = true
|
||||
}
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
|
||||
override fun contains(key: Int): Boolean {
|
||||
return id2slot.containsKey(key)
|
||||
}
|
||||
|
||||
override fun containsAll(c: IntCollection): Boolean {
|
||||
return id2slot.keys.containsAll(c)
|
||||
}
|
||||
|
||||
override fun containsAll(elements: Collection<Int>): Boolean {
|
||||
return id2slot.keys.containsAll(elements)
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return id2slot.isEmpty()
|
||||
}
|
||||
|
||||
override fun toArray(a: IntArray?): IntArray {
|
||||
return id2slot.keys.toArray(a)
|
||||
}
|
||||
|
||||
override fun toIntArray(): IntArray {
|
||||
return id2slot.keys.toIntArray()
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = id2slot.size
|
||||
}
|
||||
}
|
||||
|
||||
override val values: ReferenceCollection<T>
|
||||
get() = this@DynamicSynchableGroup
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val END = 0
|
||||
private const val ADD_ENTRY = 1
|
||||
|
@ -51,6 +51,15 @@ object MBuiltInRegistries {
|
||||
val ANDROID_FEATURE by Delegate(MRegistries.ANDROID_FEATURE) { sync(true) }
|
||||
val STACK_TYPE by Delegate(MRegistries.STACK_TYPE)
|
||||
|
||||
val BOOLEAN_PROVIDER by Delegate(MRegistries.BOOLEAN_PROVIDER) {
|
||||
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "false"))
|
||||
}
|
||||
|
||||
val FEATURE by Delegate(MRegistries.FEATURE)
|
||||
val PLACEMENT_MODIFIER by Delegate(MRegistries.PLACEMENT_MODIFIER) {
|
||||
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "passthrough"))
|
||||
}
|
||||
|
||||
val ITEM_FILTER by Delegate(MRegistries.ITEM_FILTER) {
|
||||
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "empty"))
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import net.minecraft.resources.ResourceKey
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||
import ru.dbotthepony.mc.otm.util.ResourceLocation
|
||||
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
|
||||
import ru.dbotthepony.mc.otm.data.world.DecimalProvider
|
||||
import ru.dbotthepony.mc.otm.matter.AbstractRegistryAction
|
||||
import ru.dbotthepony.mc.otm.matter.IMatterFunction
|
||||
@ -13,6 +14,8 @@ import ru.dbotthepony.mc.otm.player.android.AndroidFeatureType
|
||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescription
|
||||
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResult
|
||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
|
||||
|
||||
object MRegistries {
|
||||
private fun <T> k(name: String): ResourceKey<Registry<T>> {
|
||||
@ -27,5 +30,9 @@ object MRegistries {
|
||||
val ANDROID_RESEARCH_RESULT = k<AndroidResearchResult.Type<*>>("android_research_result")
|
||||
val ANDROID_FEATURE = k<AndroidFeatureType<*>>("android_feature")
|
||||
val STACK_TYPE = k<StorageStack.Type<*>>("stack_type")
|
||||
val BOOLEAN_PROVIDER = k<BooleanProvider.Type<*>>("boolean_provider")
|
||||
val FEATURE = k<EnhancedFeature<*>>("feature")
|
||||
val CONFIGURED_FEATURE = k<EnhancedFeature.Configured<*, *>>("configured_feature")
|
||||
val PLACEMENT_MODIFIER = k<EnhancedPlacement.Type<*>>("placement_modifier")
|
||||
val ITEM_FILTER = k<ItemFilter.Type<*>>("item_filter")
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
|
||||
import ru.dbotthepony.mc.otm.block.decorative.ComputerTerminalBlock
|
||||
import ru.dbotthepony.mc.otm.block.decorative.StarChairBlock
|
||||
import ru.dbotthepony.mc.otm.block.decorative.TritaniumPressurePlate
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.capability.matteryEnergy
|
||||
import ru.dbotthepony.mc.otm.client.MatteryGUI
|
||||
import ru.dbotthepony.mc.otm.util.ResourceLocation
|
||||
@ -318,21 +317,6 @@ object MRegistry : IBlockItemRegistryAcceptor {
|
||||
val tag = stack.getOrDefault(MDataComponentTypes.Configurator.CONFIGURATION, CompoundTag())
|
||||
if (tag.isEmpty) 0f else 1f
|
||||
}
|
||||
|
||||
for (item in MItems.MATTER_CAPACITORS) {
|
||||
ItemProperties.register(item, ResourceLocation(OverdriveThatMatters.MOD_ID, "capacitor_gauge")) { stack, _, _, _ ->
|
||||
val cap = stack.getCapability(MatteryCapability.MATTER_ITEM) ?: return@register 1f
|
||||
(cap.storedMatter / cap.maxStoredMatter).toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
for (item in MItems.ALL_BATTERIES.toMutableList().also { it.add(MItems.PROCEDURAL_BATTERY) }) {
|
||||
ItemProperties.register(item, ResourceLocation(OverdriveThatMatters.MOD_ID, "capacitor_gauge")) { stack, _, _, _ ->
|
||||
val energy = stack.matteryEnergy ?: return@register 1f
|
||||
|
||||
(energy.batteryLevel / energy.maxBatteryLevel).toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.server.world.placement.StandardDeviationHeightProvider
|
||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||
|
||||
object MHeightProviders {
|
||||
|
@ -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 } }
|
||||
}
|
@ -3,15 +3,37 @@ 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.registry.MRegistries
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.ChainPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EllipsoidPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedChainPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedCountPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedSplitPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.SplitPlacement
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.WormPlacement
|
||||
|
||||
object MPlacementModifiers {
|
||||
private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE)
|
||||
private val enhancedRegistry = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
registry.register(bus)
|
||||
enhancedRegistry.register(bus)
|
||||
}
|
||||
|
||||
val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } }
|
||||
val WORM_PLACEMENT by registry.register("worm") { PlacementModifierType { WormPlacement.CODEC } }
|
||||
val SPLIT by registry.register("split") { PlacementModifierType { SplitPlacement.CODEC} }
|
||||
val CHAIN by registry.register("chain") { PlacementModifierType { ChainPlacement.CODEC } }
|
||||
|
||||
init {
|
||||
enhancedRegistry.register("feature") { EnhancedFeature.ConfiguredWrapper.Companion }
|
||||
enhancedRegistry.register("ellipsoid") { EllipsoidPlacement.Companion }
|
||||
enhancedRegistry.register("worm") { WormPlacement.Companion }
|
||||
enhancedRegistry.register("split") { EnhancedSplitPlacement.Companion }
|
||||
enhancedRegistry.register("chain") { EnhancedChainPlacement.Companion }
|
||||
enhancedRegistry.register("count") { EnhancedCountPlacement.Companion }
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@ 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.server.world.EnhancedPlacedFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.BlackHolePlacerFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.DebugPlacerFeature
|
||||
|
||||
object MWorldGenFeatures {
|
||||
private val registry = MDeferredRegister(BuiltInRegistries.FEATURE)
|
||||
@ -13,4 +16,6 @@ object MWorldGenFeatures {
|
||||
}
|
||||
|
||||
val BLACK_HOLE_PLACER by registry.register("black_hole_placer") { BlackHolePlacerFeature }
|
||||
val DEBUG_PLACEMENT by registry.register("debug") { DebugPlacerFeature }
|
||||
val ENHANCED_FEATURE by registry.register("enhanced_feature") { EnhancedPlacedFeature }
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ private fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
|
||||
|
||||
accept(MItems.GRILL.values)
|
||||
|
||||
accept(MItems.MATTER_DUST.makeStack())
|
||||
// accept(MItems.MATTER_DUST)
|
||||
|
||||
accept(MItems.TRITANIUM_ORE)
|
||||
accept(MItems.DEEPSLATE_TRITANIUM_ORE)
|
||||
|
@ -0,0 +1,180 @@
|
||||
package ru.dbotthepony.mc.otm.server.world
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.Scheduler
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.SectionPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.WorldGenLevel
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
|
||||
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 net.minecraft.world.level.levelgen.placement.PlacedFeature
|
||||
import ru.dbotthepony.kommons.util.XXHash64
|
||||
import ru.dbotthepony.mc.otm.THREAD_LOCAL_RANDOM
|
||||
import ru.dbotthepony.mc.otm.data.codec.minRange
|
||||
import ru.dbotthepony.mc.otm.util.GJRAND64RandomSource
|
||||
import ru.dbotthepony.mc.otm.util.shuffle
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
|
||||
import java.io.DataOutputStream
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.sqrt
|
||||
|
||||
object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
|
||||
RecordCodecBuilder.create {
|
||||
it.group(
|
||||
Codec.INT.minRange(0).fieldOf("chunk_scan_range").forGetter(Config::chunkScanRange),
|
||||
Codec.LONG.fieldOf("seed_mix").forGetter(Config::seedMix),
|
||||
EnhancedPlacement.CODEC.fieldOf("root").forGetter(Config::root),
|
||||
).apply(it, ::Config)
|
||||
}
|
||||
) {
|
||||
private val emptyVariableMap = PlacementVariableMap()
|
||||
|
||||
fun configure(
|
||||
chunkScanRange: Int,
|
||||
seedMix: Long,
|
||||
root: EnhancedPlacement
|
||||
): PlacedFeature {
|
||||
return PlacedFeature(Holder.direct(ConfiguredFeature(EnhancedPlacedFeature, Config(chunkScanRange, seedMix, root))), listOf())
|
||||
}
|
||||
|
||||
class Config(
|
||||
val chunkScanRange: Int,
|
||||
val seedMix: Long,
|
||||
val root: EnhancedPlacement,
|
||||
) : FeatureConfiguration {
|
||||
private class GeneratedChunk : EnhancedPlacementContext.Placer {
|
||||
private data class Placement(val context: EnhancedPlacementContext, val positions: TreeSet<PlacementPos>, val feature: EnhancedFeature.Configured<*, *>)
|
||||
private val placed = LinkedList<Placement>()
|
||||
|
||||
override fun place(context: EnhancedPlacementContext, positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>) {
|
||||
if (positions.isNotEmpty()) {
|
||||
placed.add(Placement(context, TreeSet<PlacementPos>().also { it.addAll(positions) }, feature))
|
||||
}
|
||||
}
|
||||
|
||||
fun place(context: FeaturePlaceContext<*>): Boolean {
|
||||
var any = false
|
||||
val pos = ChunkPos(context.origin())
|
||||
|
||||
for ((eContext, positions, feature) in placed) {
|
||||
val itr = positions.tailSet(PlacementPos(BlockPos(pos.minBlockX, Int.MIN_VALUE, pos.minBlockZ), emptyVariableMap)).iterator()
|
||||
|
||||
if (!itr.hasNext())
|
||||
continue
|
||||
|
||||
val result = TreeSet<PlacementPos>()
|
||||
|
||||
for (placementPos in itr) {
|
||||
if (SectionPos.blockToSectionCoord(placementPos.pos.x) != pos.x || SectionPos.blockToSectionCoord(placementPos.pos.z) != pos.z)
|
||||
break
|
||||
|
||||
result.add(placementPos)
|
||||
}
|
||||
|
||||
if (result.isNotEmpty()) {
|
||||
any = feature.place(eContext.push(context.level()), result, positions) || any
|
||||
}
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
}
|
||||
|
||||
private val level2cache = Caffeine.newBuilder()
|
||||
.scheduler(Scheduler.systemScheduler())
|
||||
.executor(Util.backgroundExecutor())
|
||||
.expireAfterAccess(Duration.ofMinutes(10))
|
||||
.weakKeys()
|
||||
.build<ServerLevel, Cache<ChunkPos, GeneratedChunk>>()
|
||||
|
||||
private fun getCache(level: WorldGenLevel): Cache<ChunkPos, GeneratedChunk> {
|
||||
return level2cache.get(level.level) {
|
||||
Caffeine.newBuilder()
|
||||
.scheduler(Scheduler.systemScheduler())
|
||||
.executor(Util.backgroundExecutor())
|
||||
.maximumSize(16384L)
|
||||
.expireAfterWrite(Duration.ofMinutes(5))
|
||||
.softValues()
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun evaluate(context: FeaturePlaceContext<*>, chunkPos: ChunkPos): GeneratedChunk {
|
||||
val bytes = FastByteArrayOutputStream()
|
||||
val dataStream = DataOutputStream(bytes)
|
||||
|
||||
dataStream.writeLong(seedMix)
|
||||
dataStream.writeInt(chunkPos.x)
|
||||
dataStream.writeInt(chunkPos.z)
|
||||
|
||||
val hash = XXHash64()
|
||||
hash.update(bytes.array, 0, bytes.length)
|
||||
|
||||
val random = GJRAND64RandomSource(context.level().seed, hash.digestAsLong())
|
||||
|
||||
val chunk = GeneratedChunk()
|
||||
val enhancedContext = EnhancedPlacementContext(context.level(), context.chunkGenerator(), random, chunkPos, chunk)
|
||||
root.evaluate(enhancedContext, listOf(PlacementPos(BlockPos(chunkPos.minBlockX, 0, chunkPos.minBlockZ), PlacementVariableMap())))
|
||||
return chunk
|
||||
}
|
||||
|
||||
fun place(context: FeaturePlaceContext<*>): Boolean {
|
||||
val cache = getCache(context.level())
|
||||
val chunkPos = ChunkPos(context.origin())
|
||||
val chunkPositions = ArrayList<ChunkPos>()
|
||||
|
||||
for (x in -chunkScanRange .. chunkScanRange) {
|
||||
for (z in -chunkScanRange .. 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 <= chunkScanRange) {
|
||||
val thisPos = ChunkPos(chunkPos.x + x, chunkPos.z + z)
|
||||
chunkPositions.add(thisPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val withIndex = ObjectArrayList(chunkPositions.withIndex().iterator())
|
||||
// evaluate chunks in random order, so in multithreaded environments we experience less congestion
|
||||
// between threads which lookup cache while it is being populated on neighbouring chunks
|
||||
withIndex.shuffle(THREAD_LOCAL_RANDOM)
|
||||
|
||||
data class P(val i: Int, val v: GeneratedChunk) : Comparable<P> {
|
||||
override fun compareTo(other: P): Int {
|
||||
return i.compareTo(other.i)
|
||||
}
|
||||
}
|
||||
|
||||
val instances = ArrayList<P>()
|
||||
|
||||
withIndex.forEach { (i, it) ->
|
||||
instances.add(P(i, cache.get(it) { evaluate(context, it) }))
|
||||
}
|
||||
|
||||
instances.sort()
|
||||
var any = false
|
||||
instances.forEach { any = it.v.place(context) }
|
||||
return any
|
||||
}
|
||||
}
|
||||
|
||||
override fun place(context: FeaturePlaceContext<Config>): Boolean {
|
||||
return context.config().place(context)
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package ru.dbotthepony.mc.otm.server.world
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.WorldGenLevel
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementContext
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
|
||||
import java.util.*
|
||||
|
||||
class EnhancedPlacementContext {
|
||||
fun interface Placer {
|
||||
fun place(context: EnhancedPlacementContext, positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>)
|
||||
}
|
||||
|
||||
private data class SharedState(
|
||||
val level: WorldGenLevel,
|
||||
val generator: ChunkGenerator,
|
||||
val random: RandomSource,
|
||||
val origin: ChunkPos,
|
||||
val placer: Placer,
|
||||
val vanillaContext: PlacementContext,
|
||||
)
|
||||
|
||||
private val state: SharedState
|
||||
|
||||
val level: WorldGenLevel
|
||||
get() = state.level
|
||||
val generator: ChunkGenerator
|
||||
get() = state.generator
|
||||
val random: RandomSource
|
||||
get() = state.random
|
||||
val origin: ChunkPos
|
||||
get() = state.origin
|
||||
private val placer: Placer
|
||||
get() = state.placer
|
||||
val vanillaContext: PlacementContext
|
||||
get() = state.vanillaContext
|
||||
|
||||
val variables: PlacementVariableMap
|
||||
|
||||
constructor(
|
||||
level: WorldGenLevel,
|
||||
generator: ChunkGenerator,
|
||||
random: RandomSource,
|
||||
origin: ChunkPos,
|
||||
placer: Placer,
|
||||
) {
|
||||
state = SharedState(
|
||||
level = level,
|
||||
generator = generator,
|
||||
random = random,
|
||||
origin = origin,
|
||||
placer = placer,
|
||||
vanillaContext = PlacementContext(level, generator, Optional.empty())
|
||||
)
|
||||
|
||||
variables = PlacementVariableMap()
|
||||
}
|
||||
|
||||
private constructor(parent: EnhancedPlacementContext) {
|
||||
this.state = parent.state
|
||||
this.variables = parent.variables.push()
|
||||
}
|
||||
|
||||
private constructor(parent: EnhancedPlacementContext, context: WorldGenLevel) {
|
||||
this.state = parent.state.copy(level = context)
|
||||
this.variables = parent.variables.push()
|
||||
}
|
||||
|
||||
fun push(): EnhancedPlacementContext {
|
||||
return EnhancedPlacementContext(this)
|
||||
}
|
||||
|
||||
fun place(positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>) {
|
||||
placer.place(this, positions, feature)
|
||||
}
|
||||
|
||||
fun <FC : FeatureConfiguration> vanillaFeatureContext(config: FC, position: BlockPos): FeaturePlaceContext<FC> {
|
||||
return FeaturePlaceContext(Optional.empty(), level, generator, random, position, config)
|
||||
}
|
||||
|
||||
fun push(context: WorldGenLevel): EnhancedPlacementContext {
|
||||
return EnhancedPlacementContext(this, context)
|
||||
}
|
||||
}
|
25
src/main/kotlin/ru/dbotthepony/mc/otm/server/world/Ext.kt
Normal file
@ -0,0 +1,25 @@
|
||||
package ru.dbotthepony.mc.otm.server.world
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import ru.dbotthepony.mc.otm.util.collect.Vec3iHashStrategy
|
||||
import ru.dbotthepony.mc.otm.server.world.feature.EnhancedFeature
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
|
||||
|
||||
fun PlacementModifier.wrap(): EnhancedPlacement {
|
||||
return EnhancedPlacement.Wrapper(this)
|
||||
}
|
||||
|
||||
data class PlacementPos(val pos: BlockPos, val variables: PlacementVariableMap) : Comparable<PlacementPos> {
|
||||
override fun compareTo(other: PlacementPos): Int {
|
||||
return Vec3iHashStrategy.compare(pos, other.pos)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmName("placement")
|
||||
fun Holder<EnhancedFeature.Configured<*, *>>.placement() = EnhancedFeature.ConfiguredWrapper(this)
|
||||
@JvmName("vanillaPlacement")
|
||||
fun Holder<ConfiguredFeature<*, *>>.placement() = EnhancedFeature.Wrapper.configure(this)
|
||||
fun ConfiguredFeature<*, *>.placement() = EnhancedFeature.Wrapper.configure(this)
|
@ -0,0 +1,9 @@
|
||||
package ru.dbotthepony.mc.otm.server.world
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class PlacementVariable<T>(val name: ResourceLocation) : Comparable<PlacementVariable<T>> {
|
||||
override fun compareTo(other: PlacementVariable<T>): Int {
|
||||
return name.compareTo(other.name)
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package ru.dbotthepony.mc.otm.server.world
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
|
||||
/**
|
||||
* This is a "map stack" of sorts, and when you want to push some data to it
|
||||
* you need to push new map to stack (by calling [push]) and then write new data to
|
||||
* the map returned by latter.
|
||||
*/
|
||||
class PlacementVariableMap {
|
||||
private val parent: PlacementVariableMap?
|
||||
private val variables = Object2ObjectAVLTreeMap<PlacementVariable<*>, KOptional<*>>()
|
||||
|
||||
constructor() {
|
||||
parent = null
|
||||
}
|
||||
|
||||
private constructor(parent: PlacementVariableMap) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
private fun <T> recursiveGet(index: PlacementVariable<T>): KOptional<T>? {
|
||||
return variables[index] as KOptional<T>? ?: parent?.recursiveGet(index)
|
||||
}
|
||||
|
||||
operator fun <T> get(index: PlacementVariable<T>): KOptional<T> {
|
||||
return recursiveGet(index) ?: KOptional()
|
||||
}
|
||||
|
||||
operator fun <T> set(index: PlacementVariable<T>, value: T): PlacementVariableMap {
|
||||
variables[index] = KOptional(value)
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T> remove(index: PlacementVariable<T>): KOptional<T> {
|
||||
val old = variables.put(index, KOptional<T>())
|
||||
return old as KOptional<T>? ?: KOptional()
|
||||
}
|
||||
|
||||
fun remove(index: Collection<PlacementVariable<*>>) {
|
||||
index.forEach { remove(it) }
|
||||
}
|
||||
|
||||
fun push(): PlacementVariableMap {
|
||||
return PlacementVariableMap(this)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other === this || other is PlacementVariableMap && variables == other.variables && parent == other.parent
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return parent.hashCode() * 31 + variables.hashCode() + 5
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.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)
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.feature
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.resources.RegistryFileCodec
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
|
||||
import net.neoforged.bus.api.IEventBus
|
||||
import net.neoforged.neoforge.registries.DataPackRegistryEvent
|
||||
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
|
||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistries
|
||||
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
|
||||
import ru.dbotthepony.mc.otm.server.world.PlacementPos
|
||||
import ru.dbotthepony.mc.otm.server.world.placement.EnhancedPlacement
|
||||
|
||||
abstract class EnhancedFeature<FC>(codec: Codec<FC>) {
|
||||
abstract fun place(context: EnhancedPlacementContext, config: FC, positions: Set<PlacementPos>, allPositions: Set<PlacementPos>): Boolean
|
||||
|
||||
data class Configured<F : EnhancedFeature<FC>, FC>(val feature: F, val config: FC) {
|
||||
fun place(context: EnhancedPlacementContext, positions: Set<PlacementPos>, allPositions: Set<PlacementPos>): Boolean {
|
||||
return feature.place(context, config, positions, allPositions)
|
||||
}
|
||||
|
||||
fun placement(): ConfiguredWrapper {
|
||||
return ConfiguredWrapper(Holder.direct(this))
|
||||
}
|
||||
}
|
||||
|
||||
data class ConfiguredWrapper(val configured: Holder<Configured<*, *>>) : EnhancedPlacement {
|
||||
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
|
||||
context.place(positions, configured.value())
|
||||
return positions
|
||||
}
|
||||
|
||||
override val type: EnhancedPlacement.Type<*>
|
||||
get() = Companion
|
||||
|
||||
companion object : EnhancedPlacement.Type<ConfiguredWrapper> {
|
||||
override val codec: MapCodec<ConfiguredWrapper> by lazy {
|
||||
CODEC.xmap(::ConfiguredWrapper, ConfiguredWrapper::configured).fieldOf("feature")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val codec: MapCodec<Configured<*, FC>> = codec.fieldOf("config").xmap({ Configured(this, it) }, { it.config })
|
||||
fun configure(config: FC) = Configured(this, config).placement()
|
||||
|
||||
object Wrapper : EnhancedFeature<Holder<ConfiguredFeature<*, *>>>(ConfiguredFeature.CODEC) {
|
||||
override fun place(
|
||||
context: EnhancedPlacementContext,
|
||||
config: Holder<ConfiguredFeature<*, *>>,
|
||||
positions: Set<PlacementPos>,
|
||||
allPositions: Set<PlacementPos>
|
||||
): Boolean {
|
||||
var any = false
|
||||
positions.forEach { any = config.value().place(context.level, context.generator, context.random, it.pos) || any }
|
||||
return any
|
||||
}
|
||||
|
||||
fun configure(config: ConfiguredFeature<*, *>) = configure(Holder.direct(config))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val registrar = MDeferredRegister(MRegistries.FEATURE)
|
||||
|
||||
val DIRECT_CODEC: Codec<Configured<*, *>> by lazy {
|
||||
MBuiltInRegistries.FEATURE.byNameCodec().dispatch({ it.feature }, { it.codec })
|
||||
}
|
||||
|
||||
val CODEC: Codec<Holder<Configured<*, *>>> by lazy {
|
||||
RegistryFileCodec.create(MRegistries.CONFIGURED_FEATURE, DIRECT_CODEC)
|
||||
}
|
||||
|
||||
init {
|
||||
registrar.register("wrapper") { Wrapper }
|
||||
}
|
||||
|
||||
private fun registerRegistry(event: DataPackRegistryEvent.NewRegistry) {
|
||||
event.dataPackRegistry(MRegistries.CONFIGURED_FEATURE, DIRECT_CODEC)
|
||||
}
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
registrar.register(bus)
|
||||
bus.addListener(::registerRegistry)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.placement
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementContext
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
|
||||
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* Daisy-chaining placements. Required only when using placement modifiers which operate on children list
|
||||
*/
|
||||
class ChainPlacement(
|
||||
val children: List<PlacementModifier>
|
||||
) : PlacementModifier() {
|
||||
constructor(vararg children: PlacementModifier) : this(ImmutableList.copyOf(children))
|
||||
|
||||
override fun getPositions(context: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> {
|
||||
var stream = Stream.of(pos)
|
||||
children.forEach { c -> stream = stream.flatMap { c.getPositions(context, random, it) } }
|
||||
return stream
|
||||
}
|
||||
|
||||
override fun type(): PlacementModifierType<*> {
|
||||
return MPlacementModifiers.CHAIN
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<ChainPlacement> = Codec.list(PlacementModifier.CODEC).xmap(::ChainPlacement, ChainPlacement::children).fieldOf("children")
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.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.ConstantFloat
|
||||
import net.minecraft.util.valueproviders.FloatProvider
|
||||
import net.minecraft.util.valueproviders.IntProvider
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementContext
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
|
||||
import ru.dbotthepony.mc.otm.util.collect.Vec3iHashStrategy
|
||||
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
|
||||
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
|
||||
import ru.dbotthepony.mc.otm.util.math.VECTOR_POSITIVE_X
|
||||
import ru.dbotthepony.mc.otm.util.math.VECTOR_POSITIVE_Z
|
||||
import ru.dbotthepony.mc.otm.util.math.VECTOR_UP
|
||||
import ru.dbotthepony.mc.otm.util.math.Vector
|
||||
import ru.dbotthepony.mc.otm.util.math.rotate
|
||||
import ru.dbotthepony.mc.otm.util.math.rotateAroundThis
|
||||
import ru.dbotthepony.mc.otm.util.nextDouble
|
||||
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
|
||||
import ru.dbotthepony.mc.otm.server.world.PlacementPos
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
import kotlin.math.PI
|
||||
|
||||
/**
|
||||
* Ellipsoid ("cloud") placement
|
||||
*/
|
||||
data class EllipsoidPlacement(
|
||||
/**
|
||||
* 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,
|
||||
|
||||
val rotateXZ: BooleanProvider = BooleanProvider.AlwaysFalse,
|
||||
val rotateXY: FloatProvider = ConstantFloat.ZERO,
|
||||
val rotateZY: FloatProvider = ConstantFloat.ZERO,
|
||||
) : PlacementModifier(), EnhancedPlacement {
|
||||
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" }
|
||||
}
|
||||
|
||||
private fun evaluate(random: RandomSource, position: BlockPos): Set<BlockPos> {
|
||||
val count = count.sample(random)
|
||||
val results = TreeSet<BlockPos>(Vec3iHashStrategy)
|
||||
|
||||
if (count <= 0)
|
||||
return results
|
||||
|
||||
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
|
||||
|
||||
var iterations = 0
|
||||
val maxIterations = count * 10
|
||||
|
||||
val rotateXZ = rotateXZ.instance().sample(random)
|
||||
val rotateXY = rotateXY.sample(random)
|
||||
val rotateZY = rotateZY.sample(random)
|
||||
|
||||
val rotationXZ: Double
|
||||
|
||||
if (rotateXZ) {
|
||||
rotationXZ = random.nextDouble(-PI, PI)
|
||||
} else {
|
||||
rotationXZ = 0.0
|
||||
}
|
||||
|
||||
val qXZ = VECTOR_UP.rotateAroundThis(rotationXZ)
|
||||
val qXY = VECTOR_POSITIVE_Z.rotateAroundThis(rotateXY)
|
||||
val qZY = VECTOR_POSITIVE_X.rotateAroundThis(rotateZY)
|
||||
|
||||
while (results.size < count && ++iterations < maxIterations) {
|
||||
val x = this.x.sample(random) * xLength
|
||||
val y = this.y.sample(random) * yLength
|
||||
val z = this.z.sample(random) * zLength
|
||||
|
||||
val isValidPoint = (x * x) / xPow +
|
||||
(y * y) / yPow +
|
||||
(z * z) / zPow <= 1.0f
|
||||
|
||||
if (isValidPoint) {
|
||||
var point = Vector(x.toDouble(), y.toDouble(), z.toDouble())
|
||||
|
||||
point = point.rotate(qXZ)
|
||||
point = point.rotate(qXY)
|
||||
point = point.rotate(qZY)
|
||||
|
||||
results.add(BlockPos(point.x.toInt() + position.x, point.y.toInt() + position.y, point.z.toInt() + position.z))
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
override fun getPositions(
|
||||
context: PlacementContext,
|
||||
random: RandomSource,
|
||||
position: BlockPos
|
||||
): Stream<BlockPos> {
|
||||
return evaluate(random, position).stream()
|
||||
}
|
||||
|
||||
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
|
||||
val result = ArrayList<PlacementPos>()
|
||||
val results = positions.map { (it, c) -> evaluate(context.random, it).map { PlacementPos(it, c) } }
|
||||
result.ensureCapacity(result.size + results.sumOf { it.size } + 1)
|
||||
results.forEach { result.addAll(it) }
|
||||
return result
|
||||
}
|
||||
|
||||
override val type: EnhancedPlacement.Type<*>
|
||||
get() = Companion
|
||||
|
||||
override fun type(): PlacementModifierType<*> {
|
||||
return MPlacementModifiers.ELLIPSOID_PLACEMENT
|
||||
}
|
||||
|
||||
companion object : EnhancedPlacement.Type<EllipsoidPlacement> {
|
||||
val CODEC: MapCodec<EllipsoidPlacement> by lazy {
|
||||
RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
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),
|
||||
FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("y_size").forGetter(EllipsoidPlacement::yLength),
|
||||
BooleanProvider.CODEC.optionalFieldOf("rotateXZ", BooleanProvider.AlwaysFalse).forGetter(EllipsoidPlacement::rotateXZ),
|
||||
FloatProvider.CODEC.optionalFieldOf("rotateXY", ConstantFloat.ZERO).forGetter(EllipsoidPlacement::rotateXY),
|
||||
FloatProvider.CODEC.optionalFieldOf("rotateZY", ConstantFloat.ZERO).forGetter(EllipsoidPlacement::rotateZY),
|
||||
).apply(it, ::EllipsoidPlacement)
|
||||
}
|
||||
}
|
||||
|
||||
override val codec: MapCodec<EllipsoidPlacement>
|
||||
get() = CODEC
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.placement
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
|
||||
import ru.dbotthepony.mc.otm.server.world.PlacementPos
|
||||
|
||||
/**
|
||||
* Chains placements, feeding results from one placement into next
|
||||
*
|
||||
* Each placement gets its own, new [EnhancedPlacementContext], so variables can be pushed safely into it
|
||||
*/
|
||||
class EnhancedChainPlacement(
|
||||
val children: List<EnhancedPlacement>
|
||||
) : EnhancedPlacement {
|
||||
constructor(vararg children: EnhancedPlacement) : this(ImmutableList.copyOf(children))
|
||||
|
||||
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
|
||||
var currentContext = context
|
||||
var current = positions
|
||||
|
||||
children.forEach {
|
||||
current = it.evaluate(context, current)
|
||||
currentContext = currentContext.push()
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
override val type: EnhancedPlacement.Type<*>
|
||||
get() = Companion
|
||||
|
||||
companion object : EnhancedPlacement.Type<EnhancedChainPlacement> {
|
||||
override val codec: MapCodec<EnhancedChainPlacement> by lazy {
|
||||
Codec.list(EnhancedPlacement.CODEC).xmap(::EnhancedChainPlacement, EnhancedChainPlacement::children).fieldOf("children")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.placement
|
||||
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.util.valueproviders.IntProvider
|
||||
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
|
||||
import ru.dbotthepony.mc.otm.server.world.PlacementPos
|
||||
|
||||
class EnhancedCountPlacement(val provider: IntProvider) : EnhancedPlacement {
|
||||
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
|
||||
val count = provider.sample(context.random)
|
||||
|
||||
if (count <= 0) {
|
||||
return listOf()
|
||||
} else if (count == 1) {
|
||||
return positions
|
||||
} else {
|
||||
val result = ArrayList<PlacementPos>()
|
||||
result.ensureCapacity(positions.size * count)
|
||||
for (i in 0 until count) result.addAll(positions)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
override val type: EnhancedPlacement.Type<*>
|
||||
get() = Companion
|
||||
|
||||
companion object : EnhancedPlacement.Type<EnhancedCountPlacement> {
|
||||
override val codec: MapCodec<EnhancedCountPlacement> = IntProvider.CODEC.xmap(::EnhancedCountPlacement, EnhancedCountPlacement::provider).fieldOf("count")
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.placement
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import net.neoforged.bus.api.IEventBus
|
||||
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
|
||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistries
|
||||
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
|
||||
import ru.dbotthepony.mc.otm.server.world.PlacementPos
|
||||
import java.util.stream.Collectors
|
||||
|
||||
interface EnhancedPlacement {
|
||||
interface Type<T : EnhancedPlacement> {
|
||||
val codec: MapCodec<T>
|
||||
}
|
||||
|
||||
fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos>
|
||||
val type: Type<*>
|
||||
|
||||
class Wrapper(val parent: PlacementModifier) : EnhancedPlacement {
|
||||
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
|
||||
return positions.stream()
|
||||
.flatMap { (pos, vars) -> parent.getPositions(context.vanillaContext, context.random, pos).map { PlacementPos(it, vars) } }
|
||||
.collect(Collectors.toCollection(::ArrayList))
|
||||
}
|
||||
|
||||
override val type: Type<*>
|
||||
get() = Companion
|
||||
|
||||
companion object : Type<Wrapper> {
|
||||
override val codec: MapCodec<Wrapper> = PlacementModifier.CODEC.xmap(::Wrapper, Wrapper::parent).fieldOf("parent")
|
||||
}
|
||||
}
|
||||
|
||||
object Passthrough : EnhancedPlacement, Type<Passthrough> {
|
||||
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
|
||||
return positions
|
||||
}
|
||||
|
||||
override val type: Type<*>
|
||||
get() = this
|
||||
|
||||
override val codec: MapCodec<Passthrough> = MapCodec.unit(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val registrar = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
|
||||
|
||||
init {
|
||||
registrar.register("wrapper") { Wrapper.Companion }
|
||||
registrar.register("passthrough") { Passthrough }
|
||||
}
|
||||
|
||||
val CODEC: Codec<EnhancedPlacement> by lazy {
|
||||
MBuiltInRegistries.PLACEMENT_MODIFIER.byNameCodec().dispatch({ it.type }, { it.codec })
|
||||
}
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
registrar.register(bus)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.placement
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
|
||||
import ru.dbotthepony.mc.otm.server.world.PlacementPos
|
||||
|
||||
/**
|
||||
* Or "shard" placement, if you will.
|
||||
*
|
||||
* Basically, allows multiple [PlacementModifier]s to split/branch off from provided point.
|
||||
*/
|
||||
class EnhancedSplitPlacement(
|
||||
val mode: Mode,
|
||||
val children: List<EnhancedPlacement>,
|
||||
) : EnhancedPlacement {
|
||||
constructor(mode: Mode, vararg children: EnhancedPlacement) : this(mode, ImmutableList.copyOf(children))
|
||||
|
||||
enum class Mode : StringRepresentable {
|
||||
COMBINE,
|
||||
FORK;
|
||||
|
||||
private val lname = name.lowercase()
|
||||
|
||||
override fun getSerializedName(): String {
|
||||
return lname
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<Mode> = StringRepresentable.fromEnum(::values)
|
||||
}
|
||||
}
|
||||
|
||||
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
|
||||
if (mode == Mode.COMBINE) {
|
||||
val result = ArrayList<PlacementPos>()
|
||||
children.forEach { result.addAll(it.evaluate(context.push(), positions)) }
|
||||
return result
|
||||
} else {
|
||||
children.forEach { it.evaluate(context.push(), positions) }
|
||||
return positions
|
||||
}
|
||||
}
|
||||
|
||||
override val type: EnhancedPlacement.Type<*>
|
||||
get() = Companion
|
||||
|
||||
companion object : EnhancedPlacement.Type<EnhancedSplitPlacement> {
|
||||
override val codec: MapCodec<EnhancedSplitPlacement> by lazy {
|
||||
RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
Mode.CODEC.fieldOf("mode").forGetter(EnhancedSplitPlacement::mode),
|
||||
Codec.list(EnhancedPlacement.CODEC).fieldOf("children").forGetter(EnhancedSplitPlacement::children),
|
||||
).apply(it, ::EnhancedSplitPlacement)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.placement
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementContext
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
|
||||
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* Or "shard" placement, if you will.
|
||||
*
|
||||
* Basically, allows multiple [PlacementModifier]s to split/branch off from provided point.
|
||||
*/
|
||||
class SplitPlacement(
|
||||
val children: List<PlacementModifier>
|
||||
) : PlacementModifier() {
|
||||
constructor(vararg children: PlacementModifier) : this(ImmutableList.copyOf(children))
|
||||
|
||||
override fun getPositions(context: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> {
|
||||
return children.stream().flatMap { it.getPositions(context, random, pos) }
|
||||
}
|
||||
|
||||
override fun type(): PlacementModifierType<*> {
|
||||
return MPlacementModifiers.SPLIT
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<SplitPlacement> = Codec.list(PlacementModifier.CODEC).xmap(::SplitPlacement, SplitPlacement::children).fieldOf("children")
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ru.dbotthepony.mc.otm.data.world
|
||||
package ru.dbotthepony.mc.otm.server.world.placement
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
@ -0,0 +1,246 @@
|
||||
package ru.dbotthepony.mc.otm.server.world.placement
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
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.PlacementContext
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
|
||||
import ru.dbotthepony.mc.otm.data.codec.minRange
|
||||
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
|
||||
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
|
||||
import ru.dbotthepony.mc.otm.util.math.Vector
|
||||
import ru.dbotthepony.mc.otm.util.math.angleDifference
|
||||
import ru.dbotthepony.mc.otm.util.math.normalizeAngle
|
||||
import ru.dbotthepony.mc.otm.util.math.plus
|
||||
import ru.dbotthepony.mc.otm.util.math.toBlockPos
|
||||
import ru.dbotthepony.mc.otm.util.nextDouble
|
||||
import ru.dbotthepony.mc.otm.server.world.EnhancedPlacementContext
|
||||
import ru.dbotthepony.mc.otm.server.world.PlacementPos
|
||||
import java.util.stream.Stream
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* Creates one block thick "worm" positions which twists and turns
|
||||
*/
|
||||
class WormPlacement(
|
||||
val length: IntProvider,
|
||||
val turnChanceXZ: BooleanProvider,
|
||||
val turnChanceXY: BooleanProvider,
|
||||
val turnSpeedXZ: FloatProvider,
|
||||
val turnSpeedXY: FloatProvider,
|
||||
val turnRateXZ: FloatProvider,
|
||||
val turnRateXY: FloatProvider,
|
||||
val initialAngleXY: FloatProvider = DEFAULT_INITIAL_ANGLE_XY,
|
||||
val maxTravelDown: Int = Int.MAX_VALUE,
|
||||
val maxTravelUp: Int = Int.MAX_VALUE,
|
||||
) : PlacementModifier(), EnhancedPlacement {
|
||||
private inner class Worm(private val random: RandomSource, private var position: Vector) {
|
||||
private var remainingDistance = length.sample(random)
|
||||
private var xzRotation = random.nextDouble(-PI, PI)
|
||||
private var xyRotation = normalizeAngle(initialAngleXY.sample(random).toDouble())
|
||||
private var xzTargetRotation = random.nextDouble(-PI, PI)
|
||||
private var xyTargetRotation = normalizeAngle(initialAngleXY.sample(random).toDouble())
|
||||
|
||||
private var xzSin = sin(xzRotation)
|
||||
private var xzCos = cos(xzRotation)
|
||||
private var xySin = sin(xyRotation)
|
||||
|
||||
private val turnChanceXZ = this@WormPlacement.turnChanceXZ.instance()
|
||||
private val turnChanceXY = this@WormPlacement.turnChanceXY.instance()
|
||||
|
||||
val hasFinished: Boolean
|
||||
get() = remainingDistance <= 0
|
||||
|
||||
private var prevPos = BlockPos.ZERO
|
||||
|
||||
fun follow(): BlockPos? {
|
||||
if (hasFinished) return null
|
||||
remainingDistance--
|
||||
|
||||
// wormy turn
|
||||
if (turnChanceXZ.sample(random)) {
|
||||
// wormy angle
|
||||
xzTargetRotation += turnRateXZ.sample(random)
|
||||
xzTargetRotation = normalizeAngle(xzTargetRotation)
|
||||
}
|
||||
|
||||
// wormy-o!
|
||||
if (turnChanceXY.sample(random)) {
|
||||
// worm?
|
||||
xyTargetRotation += turnRateXY.sample(random)
|
||||
xyTargetRotation = normalizeAngle(xyTargetRotation)
|
||||
}
|
||||
|
||||
if (xzTargetRotation != xzRotation) {
|
||||
val diff = angleDifference(xzTargetRotation, xzRotation)
|
||||
val abs = diff.absoluteValue
|
||||
val speed = turnSpeedXZ.sample(random).toDouble()
|
||||
|
||||
if (abs <= speed)
|
||||
xzRotation = xzTargetRotation
|
||||
else
|
||||
xzRotation += speed * diff.sign
|
||||
|
||||
xzSin = sin(xzRotation)
|
||||
xzCos = cos(xzRotation)
|
||||
}
|
||||
|
||||
if (xyTargetRotation != xyRotation) {
|
||||
val diff = angleDifference(xyTargetRotation, xyRotation)
|
||||
val abs = diff.absoluteValue
|
||||
val speed = turnSpeedXZ.sample(random).toDouble()
|
||||
|
||||
if (abs <= speed)
|
||||
xyRotation = xyTargetRotation
|
||||
else
|
||||
xyRotation += speed * diff.sign
|
||||
|
||||
xySin = sin(xyRotation)
|
||||
}
|
||||
|
||||
// advance worm
|
||||
position += Vector(xzCos, xySin, xzSin)
|
||||
|
||||
if (position.y > maxTravelUp) {
|
||||
xyTargetRotation = 0.0
|
||||
xySin = 0.0
|
||||
position = Vector(position.x, maxTravelUp.toDouble(), position.z)
|
||||
} else if (position.y < -maxTravelDown) {
|
||||
xyTargetRotation = 0.0
|
||||
xySin = 0.0
|
||||
position = Vector(position.x, -maxTravelDown.toDouble(), position.z)
|
||||
}
|
||||
|
||||
val calc = position.toBlockPos()
|
||||
|
||||
if (calc != prevPos) {
|
||||
// if worm made a wurm, add new position to set
|
||||
prevPos = calc
|
||||
return calc
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun evaluate(random: RandomSource, position: BlockPos): List<BlockPos> {
|
||||
val worms = ArrayList<Worm>()
|
||||
worms.add(Worm(random, Vector.ZERO))
|
||||
val results = ArrayList<BlockPos>()
|
||||
results.add(position)
|
||||
|
||||
while (worms.isNotEmpty()) {
|
||||
worms.removeIf {
|
||||
val pos = it.follow()
|
||||
if (pos != null) results.add(pos + position)
|
||||
it.hasFinished
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
|
||||
if (positions.isEmpty())
|
||||
return positions
|
||||
else {
|
||||
val result = ArrayList<PlacementPos>()
|
||||
val results = positions.map { (it, c) -> evaluate(context.random, it).map { PlacementPos(it, c) } }
|
||||
result.ensureCapacity(result.size + results.sumOf { it.size } + 1)
|
||||
results.forEach { result.addAll(it) }
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
override val type: EnhancedPlacement.Type<*>
|
||||
get() = Companion
|
||||
|
||||
override fun getPositions(context: PlacementContext, random: RandomSource, center: BlockPos): Stream<BlockPos> {
|
||||
return evaluate(random, center).stream()
|
||||
}
|
||||
|
||||
override fun type(): PlacementModifierType<*> {
|
||||
return MPlacementModifiers.WORM_PLACEMENT
|
||||
}
|
||||
|
||||
companion object : EnhancedPlacement.Type<WormPlacement> {
|
||||
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 *= 2f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val DEFAULT_INITIAL_ANGLE_XY: UniformFloat = UniformFloat.of(-PI.toFloat() / 16f, increment(PI.toFloat() / 16f))
|
||||
|
||||
fun constantTurnRate(degrees: Float): FloatProvider {
|
||||
return constantTurnRateRadians(degrees * DEGREES_TO_RADIANS)
|
||||
}
|
||||
|
||||
fun constantTurnRateRadians(rad: Float): FloatProvider {
|
||||
return ConstantFloat.of(rad)
|
||||
}
|
||||
|
||||
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, mean: Float = 0f): FloatProvider {
|
||||
return normalDistributedTurnRateRadians(deviation * DEGREES_TO_RADIANS, mean * DEGREES_TO_RADIANS)
|
||||
}
|
||||
|
||||
fun normalDistributedTurnRateRadians(deviation: Float, mean: Float = 0f): FloatProvider {
|
||||
require(deviation >= 0f) { "Provided value must be non-negative, $deviation given" }
|
||||
if (deviation == 0f) return ConstantFloat.ZERO
|
||||
return ClampedNormalFloat.of(mean, deviation, -PI.toFloat() * 2f, PI.toFloat() * 2f)
|
||||
}
|
||||
|
||||
private const val DEGREES_TO_RADIANS = PI.toFloat() / 180f
|
||||
|
||||
val CODEC: MapCodec<WormPlacement> by lazy {
|
||||
RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length),
|
||||
BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ),
|
||||
BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY),
|
||||
FloatProvider.CODEC.fieldOf("turn_speed_xz").forGetter(WormPlacement::turnSpeedXZ),
|
||||
FloatProvider.CODEC.fieldOf("turn_speed_xy").forGetter(WormPlacement::turnSpeedXY),
|
||||
FloatProvider.CODEC.fieldOf("turn_rate_xz").forGetter(WormPlacement::turnRateXZ),
|
||||
FloatProvider.CODEC.fieldOf("turn_rate_xy").forGetter(WormPlacement::turnRateXY),
|
||||
FloatProvider.CODEC.optionalFieldOf("initial_angle_xy", DEFAULT_INITIAL_ANGLE_XY).forGetter(WormPlacement::initialAngleXY),
|
||||
Codec.INT.minRange(1).optionalFieldOf("max_travel_down", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelDown),
|
||||
Codec.INT.minRange(1).optionalFieldOf("max_travel_up", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelUp),
|
||||
).apply(it, ::WormPlacement)
|
||||
}
|
||||
}
|
||||
|
||||
override val codec: MapCodec<WormPlacement>
|
||||
get() = CODEC
|
||||
}
|
||||
}
|
@ -1,58 +1,37 @@
|
||||
package ru.dbotthepony.mc.otm.util
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntRBTreeSet
|
||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||
|
||||
class IDAllocator {
|
||||
private var nextID = -1
|
||||
private val gaps = IntRBTreeSet()
|
||||
private val sparseAllocations = IntRBTreeSet()
|
||||
|
||||
private fun increaseHighestID(): Int {
|
||||
while (sparseAllocations.remove(++nextID)) {}
|
||||
return nextID
|
||||
}
|
||||
private var highestID = 0
|
||||
private val gaps = IntAVLTreeSet()
|
||||
|
||||
fun allocate(): Int {
|
||||
if (gaps.isEmpty()) {
|
||||
return increaseHighestID()
|
||||
return highestID++
|
||||
} else {
|
||||
return gaps.removeFirst()
|
||||
}
|
||||
}
|
||||
|
||||
fun allocate(id: Int): Boolean {
|
||||
if (id < 0) {
|
||||
return true // not handling negative IDs
|
||||
} else if (id > nextID) {
|
||||
sparseAllocations.add(id)
|
||||
return true
|
||||
} else if (id == nextID) {
|
||||
increaseHighestID()
|
||||
return true
|
||||
} else {
|
||||
return gaps.remove(id)
|
||||
}
|
||||
}
|
||||
|
||||
fun release(id: Int) {
|
||||
if (id < 0) {
|
||||
return // not handling negative IDs
|
||||
} else if (sparseAllocations.remove(id)) {
|
||||
return // was allocated above highest ID
|
||||
} else if (id > nextID) {
|
||||
throw IllegalArgumentException("Not tracking ID: $id (highest known ID is ${nextID})")
|
||||
if (id >= 0) {
|
||||
throw IllegalArgumentException("Invalid ID: $id")
|
||||
} else if (id >= highestID) {
|
||||
throw IllegalArgumentException("Not tracking ID: $id (highest known ID is ${highestID - 1})")
|
||||
} else if (id in gaps) {
|
||||
throw IllegalArgumentException("ID is already free: $id")
|
||||
} else if (id == nextID) {
|
||||
nextID--
|
||||
}
|
||||
|
||||
if (id + 1 == highestID) {
|
||||
highestID--
|
||||
} else {
|
||||
check(gaps.add(id))
|
||||
gaps.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
nextID = -1
|
||||
highestID = 0
|
||||
gaps.clear()
|
||||
sparseAllocations.clear()
|
||||
}
|
||||
}
|
||||
|
@ -78,17 +78,6 @@ fun IntArray.shuffle(random: RandomSource): IntArray {
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T> Array<T>.shuffle(random: RandomSource): Array<T> {
|
||||
for (i in lastIndex downTo 1) {
|
||||
val j = random.nextInt(i + 1)
|
||||
val copy = this[i]
|
||||
this[i] = this[j]
|
||||
this[j] = copy
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun <L : IntList> L.shuffle(random: RandomSource): L {
|
||||
for (i in lastIndex downTo 1) {
|
||||
val j = random.nextInt(i + 1)
|
||||
|
@ -0,0 +1,31 @@
|
||||
package ru.dbotthepony.mc.otm.util.collect
|
||||
|
||||
import it.unimi.dsi.fastutil.Hash
|
||||
import it.unimi.dsi.fastutil.HashCommon
|
||||
import net.minecraft.core.Vec3i
|
||||
|
||||
object Vec3iHashStrategy : Hash.Strategy<Vec3i>, Comparator<Vec3i> {
|
||||
override fun equals(a: Vec3i?, b: Vec3i?): Boolean {
|
||||
return a == b
|
||||
}
|
||||
|
||||
// while this avoids collisions, it is rather slow when compared to just using a tree set here
|
||||
override fun hashCode(o: Vec3i?): Int {
|
||||
o ?: return 0
|
||||
return HashCommon.murmurHash3(o.x.toLong().and(1 shl 26 - 1) or o.z.toLong().and(1 shl 26 - 1).shl(26) or o.y.toLong().and(1 shl 12 - 1).shl(52)).toInt()
|
||||
}
|
||||
|
||||
override fun compare(o1: Vec3i?, o2: Vec3i?): Int {
|
||||
if (o1 == null && o2 == null)
|
||||
return 0
|
||||
else if (o1 == null)
|
||||
return -1 // nulls come first
|
||||
else if (o2 == null)
|
||||
return 1
|
||||
|
||||
var cmp = o1.x.compareTo(o2.x)
|
||||
if (cmp == 0) cmp = o1.z.compareTo(o2.z)
|
||||
if (cmp == 0) cmp = o1.y.compareTo(o2.y)
|
||||
return cmp
|
||||
}
|
||||
}
|
@ -20,21 +20,7 @@ operator fun BlockPos.plus(other: BlockRotation): BlockPos {
|
||||
}
|
||||
|
||||
enum class RelativeSide(val default: Direction) {
|
||||
FRONT(Direction.NORTH), BACK(Direction.SOUTH), LEFT(Direction.WEST), RIGHT(Direction.EAST), TOP(Direction.UP), BOTTOM(Direction.DOWN);
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun defaultOf(direction: Direction): RelativeSide {
|
||||
return when (direction) {
|
||||
Direction.DOWN -> BOTTOM
|
||||
Direction.UP -> TOP
|
||||
Direction.NORTH -> FRONT
|
||||
Direction.SOUTH -> BACK
|
||||
Direction.WEST -> LEFT
|
||||
Direction.EAST -> RIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
FRONT(Direction.NORTH), BACK(Direction.SOUTH), LEFT(Direction.WEST), RIGHT(Direction.EAST), TOP(Direction.UP), BOTTOM(Direction.DOWN)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,9 +8,11 @@ import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.AxisAngle4d
|
||||
import org.joml.AxisAngle4f
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Matrix4f
|
||||
import org.joml.Quaterniond
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import ru.dbotthepony.kommons.collect.filter
|
||||
@ -25,10 +27,14 @@ typealias Vector = Vec3
|
||||
|
||||
val VECTOR_UP = Vector(0.0, 1.0, 0.0)
|
||||
val VECTOR_FORWARD = Vector(1.0, 0.0, 0.0)
|
||||
val VECTOR_POSITIVE_X = VECTOR_FORWARD
|
||||
val VECTOR_BACKWARD = Vector(-1.0, 0.0, 0.0)
|
||||
val VECTOR_NEGATIVE_X = VECTOR_BACKWARD
|
||||
val VECTOR_DOWN = Vector(0.0, -1.0, 0.0)
|
||||
val VECTOR_RIGHT = Vector(0.0, 0.0, 1.0)
|
||||
val VECTOR_POSITIVE_Z = VECTOR_RIGHT
|
||||
val VECTOR_LEFT = Vector(0.0, 0.0, -1.0)
|
||||
val VECTOR_NEGATIVE_Z = VECTOR_LEFT
|
||||
|
||||
private const val DEGREES_TO_RADIANS = 0.017453292f
|
||||
|
||||
@ -36,6 +42,10 @@ fun toRadians(angle: Float): Float {
|
||||
return angle * DEGREES_TO_RADIANS
|
||||
}
|
||||
|
||||
fun toRadians(angle: Double): Double {
|
||||
return Math.toRadians(angle)
|
||||
}
|
||||
|
||||
fun Vector.asAngle(): Angle {
|
||||
val norm = normalize()
|
||||
return Angle(asin(norm.y), atan2(norm.x, norm.z))
|
||||
@ -173,16 +183,36 @@ fun Vector.rotate(q: Quaternionf): Vector {
|
||||
return Vector(quaternion.x.toDouble(), quaternion.y.toDouble(), quaternion.z.toDouble())
|
||||
}
|
||||
|
||||
fun Vector.rotate(q: Quaterniond): Vector {
|
||||
// TODO: 1.19.3
|
||||
val quaternion = Quaterniond(q)
|
||||
quaternion.mul(rotateAroundThisDouble())
|
||||
val quaternion1 = Quaterniond(q)
|
||||
quaternion1.conjugate()
|
||||
quaternion.mul(quaternion1)
|
||||
return Vector(quaternion.x, quaternion.y, quaternion.z)
|
||||
}
|
||||
|
||||
fun Vector.rotateAroundThis(rotation: Float, isDegrees: Boolean = false): Quaternionf {
|
||||
// TODO: 1.19.3
|
||||
return Quaternionf(AxisAngle4f(if (isDegrees) toRadians(rotation) else rotation, x.toFloat(), y.toFloat(), z.toFloat()))
|
||||
}
|
||||
|
||||
fun Vector.rotateAroundThis(rotation: Double, isDegrees: Boolean = false): Quaterniond {
|
||||
// TODO: 1.19.3
|
||||
return Quaterniond(AxisAngle4d(if (isDegrees) toRadians(rotation) else rotation, x, y, z))
|
||||
}
|
||||
|
||||
fun Vector.rotateAroundThis(): Quaternionf {
|
||||
// TODO: 1.19.3
|
||||
return Quaternionf(AxisAngle4f(0f, x.toFloat(), y.toFloat(), z.toFloat()))
|
||||
}
|
||||
|
||||
fun Vector.rotateAroundThisDouble(): Quaterniond {
|
||||
// TODO: 1.19.3
|
||||
return Quaterniond(AxisAngle4d(0.0, x, y, z))
|
||||
}
|
||||
|
||||
fun Vector.asMatrix(): Matrix3f {
|
||||
return Matrix3f().also {
|
||||
it[0, 0] = x.toFloat()
|
||||
@ -444,7 +474,9 @@ operator fun Vec3i.plus(direction: Vector): Vector = Vector(x + direction.x, y +
|
||||
operator fun Vec3i.minus(direction: Vector): Vector = Vector(x - direction.x, y - direction.y, z - direction.z)
|
||||
|
||||
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)
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"code_injector": "coremods/code_injector.js",
|
||||
"chest_menus": "coremods/chest_menus.js",
|
||||
"limb_brush_overclock": "coremods/limb_brush_overclock.js",
|
||||
"iron_chest_menus": "coremods/iron_chest_menus.js"
|
||||
"limb_brush_overclock": "coremods/limb_brush_overclock.js"
|
||||
}
|
Before Width: | Height: | Size: 368 B |
Before Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 377 B |
Before Width: | Height: | Size: 374 B |
Before Width: | Height: | Size: 374 B |
Before Width: | Height: | Size: 374 B |
Before Width: | Height: | Size: 374 B |
Before Width: | Height: | Size: 377 B |
Before Width: | Height: | Size: 373 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 262 B |
Before Width: | Height: | Size: 366 B |
Before Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 388 B |
Before Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 388 B |
Before Width: | Height: | Size: 380 B |
Before Width: | Height: | Size: 389 B |
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 484 B |
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 505 B |
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 505 B |
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 505 B |