Merge branch '1.20.1' into 1.20.2

# Conflicts:
#	gradle.properties
This commit is contained in:
Sollace 2024-02-11 14:52:56 +00:00
commit 0de1847392
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
37 changed files with 827 additions and 87 deletions

View file

@ -43,6 +43,8 @@ repositories {
maven { name 'minelp-snapshot'; url 'https://repo.minelittlepony-mod.com/maven/snapshot' } maven { name 'minelp-snapshot'; url 'https://repo.minelittlepony-mod.com/maven/snapshot' }
maven { name 'minelp-releases'; url 'https://repo.minelittlepony-mod.com/maven/release' } maven { name 'minelp-releases'; url 'https://repo.minelittlepony-mod.com/maven/release' }
maven { name 'TerraformersMC'; url 'https://maven.terraformersmc.com/' } maven { name 'TerraformersMC'; url 'https://maven.terraformersmc.com/' }
maven { name 'Modrinth'; url 'https://api.modrinth.com/maven' }
maven { name 'JitPack'; url 'https://jitpack.io'; content { includeGroup "com.github.Virtuoel" } }
} }
dependencies { dependencies {
@ -73,17 +75,22 @@ dependencies {
modImplementation "com.terraformersmc.terraform-api:terraform-wood-api-v1:${project.terraformer_api_version}" modImplementation "com.terraformersmc.terraform-api:terraform-wood-api-v1:${project.terraformer_api_version}"
include "com.terraformersmc.terraform-api:terraform-wood-api-v1:${project.terraformer_api_version}" include "com.terraformersmc.terraform-api:terraform-wood-api-v1:${project.terraformer_api_version}"
modCompileOnly "maven.modrinth:farmers-delight-fabric:${project.farmers_delight_version}", { exclude group: "net.fabricmc.fabric-api" }
if (project.use_pehkui == '1') {
modCompileOnly "maven.modrinth:pehkui:${project.pehkui_version}", { exclude group: "net.fabricmc.fabric-api" }
modCompileOnly "com.github.Virtuoel:KanosConfig:0.4.1", { exclude group: "net.fabricmc.fabric-api" }
}
if (project.use_sodium == '1') {
modCompileOnly "maven.modrinth:sodium:${project.sodium_version}", { exclude group: "net.fabricmc.fabric-api" }
modCompileOnly "maven.modrinth:iris:${project.iris_version}", { exclude group: "net.fabricmc.fabric-api" }
}
if (project.tmi_type == 'emi') { if (project.tmi_type == 'emi') {
modCompileOnly "dev.emi:emi-fabric:${project.emi_version}" modCompileOnly "dev.emi:emi-fabric:${project.emi_version}"
} else { } else {
modCompileOnly "dev.emi:emi-fabric-dummy:${project.emi_version}" modCompileOnly "dev.emi:emi-fabric-dummy:${project.emi_version}"
} }
if (project.tmi_type == 'rei') {
// TODO:
} else {
// TODO:
}
} }
processResources { processResources {

View file

@ -15,7 +15,7 @@ org.gradle.daemon=false
description=Magical Abilities for Mine Little Pony! description=Magical Abilities for Mine Little Pony!
# Publishing # Publishing
minecraft_version_range=>=1.20.2 minecraft_version_range=1.20.2
modrinth_loader_type=fabric modrinth_loader_type=fabric
modrinth_project_id=9K7RJlvM modrinth_project_id=9K7RJlvM
@ -24,10 +24,19 @@ org.gradle.daemon=false
modmenu_version=8.0.0-beta.1 modmenu_version=8.0.0-beta.1
minelp_version=4.11.1+1.20.2 minelp_version=4.11.1+1.20.2
kirin_version=1.16.0+1.20.2 kirin_version=1.16.0+1.20.2
reach_attributes_version=2.3.3 reach_attributes_version=2.3.4
trinkets_version=3.8.0 trinkets_version=3.8.0
terraformer_api_version=8.0.0-beta.1 terraformer_api_version=8.0.0-beta.1
# Testing
use_pehkui=0
use_sodium=1
farmers_delight_version=1.4.3
pehkui_version=3.7.8+1.14.4-1.20.1
iris_version=1.6.8+1.20.1
sodium_version=mc1.20.1-0.5.2
# TMI Testing # TMI Testing
tmi_type=emi tmi_type=emi
emi_version=1.0.21+1.20.2 emi_version=1.0.21+1.20.2

Binary file not shown.

View file

@ -48,6 +48,9 @@ public class Config extends com.minelittlepony.common.util.settings.Config {
public final Setting<Boolean> simplifiedPortals = value("compatibility", "simplifiedPortals", false) public final Setting<Boolean> simplifiedPortals = value("compatibility", "simplifiedPortals", false)
.addComment("Disables dynamic portal rendering"); .addComment("Disables dynamic portal rendering");
public final Setting<Boolean> disableShaders = value("compatibility", "disableShaders", false)
.addComment("Disables post-effect shaders used by the corruption mechanic");
public final Setting<Long> fancyPortalRefreshRate = value("client", "fancyPortalRefreshRate", -1L) public final Setting<Long> fancyPortalRefreshRate = value("client", "fancyPortalRefreshRate", -1L)
.addComment("Sets the refresh rate of portals when using fancy portal rendering") .addComment("Sets the refresh rate of portals when using fancy portal rendering")
.addComment("Set to -1 (default) for unlimited"); .addComment("Set to -1 (default) for unlimited");

View file

@ -38,6 +38,9 @@ public class UnicopiaMixinPlugin implements IMixinConfigPlugin {
if (mixinClassName.indexOf("minelp") != -1) { if (mixinClassName.indexOf("minelp") != -1) {
return FabricLoader.getInstance().isModLoaded("minelp"); return FabricLoader.getInstance().isModLoaded("minelp");
} }
if (mixinClassName.indexOf("forgified") != -1) {
return FabricLoader.getInstance().isModLoaded("connectormod");
}
} }
return true; return true;
} }

View file

@ -66,7 +66,7 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka
return true; return true;
} }
protected BlockState getPlacedFruitState(Random random) { public BlockState getPlacedFruitState(Random random) {
return fruit.get().getDefaultState(); return fruit.get().getDefaultState();
} }
@ -141,7 +141,7 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka
return TintedBlock.blend(foliageColor, overlay); return TintedBlock.blend(foliageColor, overlay);
} }
private boolean isPositionValidForFruit(BlockState state, BlockPos pos) { public boolean isPositionValidForFruit(BlockState state, BlockPos pos) {
return state.getRenderingSeed(pos) % 3 == 1; return state.getRenderingSeed(pos) % 3 == 1;
} }

View file

@ -20,7 +20,7 @@ public class GoldenOakLeavesBlock extends FruitBearingBlock {
} }
@Override @Override
protected BlockState getPlacedFruitState(Random random) { public BlockState getPlacedFruitState(Random random) {
return super.getPlacedFruitState(random).with(EnchantedFruitBlock.ENCHANTED, random.nextInt(1000) == 0); return super.getPlacedFruitState(random).with(EnchantedFruitBlock.ENCHANTED, random.nextInt(1000) == 0);
} }
} }

View file

@ -42,14 +42,24 @@ public class ViewportShader implements SynchronousResourceReloader, Identifiable
} }
public void loadShader(@Nullable Identifier shaderId) { public void loadShader(@Nullable Identifier shaderId) {
if (shader != null) { if (shader != null) {
shader.close(); try {
shader.close();
} catch (Throwable ignored) {
} finally {
shader = null;
}
} }
if (shaderId == null) { if (shaderId == null) {
return; return;
} }
if (Unicopia.getConfig().disableShaders.get()) {
return;
}
try { try {
shader = new LoadedShader(client, shaderId); shader = new LoadedShader(client, shaderId);
} catch (IOException e) { } catch (IOException e) {
@ -66,6 +76,10 @@ public class ViewportShader implements SynchronousResourceReloader, Identifiable
} }
public void render(float tickDelta) { public void render(float tickDelta) {
if (Unicopia.getConfig().disableShaders.get()) {
return;
}
if (shader != null && client.player != null) { if (shader != null && client.player != null) {
RenderSystem.disableBlend(); RenderSystem.disableBlend();
RenderSystem.disableDepthTest(); RenderSystem.disableDepthTest();

View file

@ -17,7 +17,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box; import net.minecraft.util.math.Box;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes; import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.WorldAccess; import net.minecraft.world.EntityView;
public class EntityCollisions { public class EntityCollisions {
@ -40,7 +40,7 @@ public class EntityCollisions {
} }
} }
public static List<VoxelShape> getColissonShapes(@Nullable Entity entity, WorldAccess world, Box box) { public static List<VoxelShape> getColissonShapes(@Nullable Entity entity, EntityView world, Box box) {
ShapeContext ctx = entity == null ? ShapeContext.absent() : ShapeContext.of(entity); ShapeContext ctx = entity == null ? ShapeContext.absent() : ShapeContext.of(entity);
return collectCollisionBoxes(box, collector -> { return collectCollisionBoxes(box, collector -> {
world.getOtherEntities(entity, box.expand(50), e -> { world.getOtherEntities(entity, box.expand(50), e -> {

View file

@ -31,7 +31,12 @@ abstract class MixinBoatEntity extends Entity implements LavaAffine {
"fall", "fall",
"canAddPassenger" "canAddPassenger"
}, },
at = @At(value = "FIELD", target = "net/minecraft/registry/tag/FluidTags.WATER:Lnet/minecraft/registry/tag/TagKey;", opcode = Opcodes.GETSTATIC) at = @At(
value = "FIELD",
target = "net/minecraft/registry/tag/FluidTags.WATER:Lnet/minecraft/registry/tag/TagKey;",
opcode = Opcodes.GETSTATIC
),
require = 0 // Forge
) )
private TagKey<Fluid> redirectFluidTag() { private TagKey<Fluid> redirectFluidTag() {
return isLavaAffine() ? FluidTags.LAVA : FluidTags.WATER; return isLavaAffine() ? FluidTags.LAVA : FluidTags.WATER;

View file

@ -0,0 +1,32 @@
package com.minelittlepony.unicopia.mixin;
import java.util.List;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.entity.collision.EntityCollisions;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.Box;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.EntityView;
@Mixin(EntityView.class)
interface MixinEntityView {
@Inject(method = "getEntityCollisions(Lnet/minecraft/entity/Entity;Lnet/minecraft/util/math/Box;)Ljava/util/List;", at = @At("RETURN"), cancellable = true)
private void onGetEntityCollisions(@Nullable Entity entity, Box box, CallbackInfoReturnable<List<VoxelShape>> info) {
if (box.getAverageSideLength() < 1.0E-7D) {
return;
}
List<VoxelShape> shapes = EntityCollisions.getColissonShapes(entity, (EntityView)this, box);
if (!shapes.isEmpty()) {
info.setReturnValue(Stream.concat(shapes.stream(), info.getReturnValue().stream()).toList());
}
}
}

View file

@ -0,0 +1,23 @@
package com.minelittlepony.unicopia.mixin;
import java.util.function.Consumer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import com.minelittlepony.unicopia.server.world.gen.BiomeSelectionInjector;
import com.mojang.datafixers.util.Pair;
import net.minecraft.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
import net.minecraft.world.biome.source.util.VanillaBiomeParameters;
@Mixin(VanillaBiomeParameters.class)
abstract class MixinVanillaBiomeParameters {
@ModifyVariable(method = "writeOverworldBiomeParameters", at = @At("HEAD"))
private Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> onWriteOverworldBiomeParameters(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parametersCollector) {
return new BiomeSelectionInjector(parametersCollector);
}
}

View file

@ -1,26 +1,17 @@
package com.minelittlepony.unicopia.mixin; package com.minelittlepony.unicopia.mixin;
import java.util.List;
import java.util.Stack; import java.util.Stack;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.entity.collision.EntityCollisions;
import com.minelittlepony.unicopia.entity.duck.RotatedView; import com.minelittlepony.unicopia.entity.duck.RotatedView;
import com.minelittlepony.unicopia.server.world.BlockDestructionManager; import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldAccess; import net.minecraft.world.WorldAccess;
@ -47,18 +38,6 @@ abstract class MixinWorld implements WorldAccess, BlockDestructionManager.Source
return destructions.get(); return destructions.get();
} }
@Override
public List<VoxelShape> getEntityCollisions(@Nullable Entity entity, Box box) {
if (box.getAverageSideLength() >= 1.0E-7D) {
List<VoxelShape> shapes = EntityCollisions.getColissonShapes(entity, this, box);
if (!shapes.isEmpty()) {
return Stream.concat(shapes.stream(), WorldAccess.super.getEntityCollisions(entity, box).stream()).toList();
}
}
return WorldAccess.super.getEntityCollisions(entity, box);
}
@ModifyVariable(method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;II)Z", at = @At("HEAD")) @ModifyVariable(method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;II)Z", at = @At("HEAD"))
private BlockPos modifyBlockPos(BlockPos pos) { private BlockPos modifyBlockPos(BlockPos pos) {
pos = applyRotation(pos); pos = applyRotation(pos);

View file

@ -0,0 +1,54 @@
package com.minelittlepony.unicopia.mixin.forgified;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.*;
import com.minelittlepony.unicopia.entity.duck.LavaAffine;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.registry.tag.FluidTags;
@Pseudo
@Mixin(targets = "net.minecraftforge.common.extensions.IForgeBoat")
interface MixinIForgeBoat {
@ModifyVariable(
method = "canBoatInFluid(Lnet/minecraft/fluid/FluidState;)Z",
at = @At("HEAD"),
ordinal = 0,
argsOnly = true,
require = 0
)
private FluidState modifyFluidState(FluidState incoming) {
if (this instanceof LavaAffine a && a.isLavaAffine()) {
if (incoming.isIn(FluidTags.WATER)) {
return Fluids.LAVA.getDefaultState();
}
if (incoming.isIn(FluidTags.LAVA)) {
return Fluids.WATER.getDefaultState();
}
}
return incoming;
}
@SuppressWarnings("deprecation")
@ModifyVariable(
method = "canBoatInFluid(Lnet/minecraft/fluid/Fluid;)Z",
at = @At("HEAD"),
ordinal = 0,
argsOnly = true,
require = 0
)
private Fluid modifyFluid(Fluid incoming) {
if (this instanceof LavaAffine a && a.isLavaAffine()) {
if (incoming.isIn(FluidTags.WATER)) {
return Fluids.LAVA;
}
if (incoming.isIn(FluidTags.LAVA)) {
return Fluids.WATER;
}
}
return incoming;
}
}

View file

@ -4,6 +4,7 @@ import java.util.*;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.block.UBlocks;
@ -17,10 +18,11 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import net.minecraft.registry.*; import net.minecraft.registry.*;
import net.minecraft.registry.tag.BiomeTags; import net.minecraft.registry.tag.BiomeTags;
import net.minecraft.world.gen.GenerationStep; import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.gen.feature.*; import net.minecraft.world.gen.feature.*;
import net.minecraft.world.gen.feature.size.TwoLayersFeatureSize; import net.minecraft.world.gen.feature.size.TwoLayersFeatureSize;
import net.minecraft.world.gen.foliage.FoliagePlacer; import net.minecraft.world.gen.foliage.FoliagePlacer;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.placementmodifier.PlacementModifier; import net.minecraft.world.gen.placementmodifier.PlacementModifier;
import net.minecraft.world.gen.stateprovider.BlockStateProvider; import net.minecraft.world.gen.stateprovider.BlockStateProvider;
import net.minecraft.world.gen.trunk.TrunkPlacer; import net.minecraft.world.gen.trunk.TrunkPlacer;
@ -29,9 +31,8 @@ public record Tree (
Identifier id, Identifier id,
TreeFeatureConfig.Builder config, TreeFeatureConfig.Builder config,
RegistryKey<ConfiguredFeature<?, ?>> configuredFeatureId, RegistryKey<ConfiguredFeature<?, ?>> configuredFeatureId,
Optional<RegistryKey<PlacedFeature>> placedFeatureId, Set<Placement> placements,
Optional<Block> sapling, Optional<Block> sapling
Optional<PlacementModifier> placement
) { ) {
public static final List<Tree> REGISTRY = new ArrayList<>(); public static final List<Tree> REGISTRY = new ArrayList<>();
@ -44,20 +45,22 @@ public record Tree (
}); });
registries.getOptional(RegistryKeys.PLACED_FEATURE).ifPresent(registry -> { registries.getOptional(RegistryKeys.PLACED_FEATURE).ifPresent(registry -> {
var reg = registries.asDynamicRegistryManager().createRegistryLookup().getOrThrow(RegistryKeys.CONFIGURED_FEATURE); var reg = registries.asDynamicRegistryManager().createRegistryLookup().getOrThrow(RegistryKeys.CONFIGURED_FEATURE);
REGISTRY.stream().filter(tree -> tree.placedFeatureId().isPresent()).forEach(tree -> { REGISTRY.stream().forEach(tree -> {
var placedFeature = new PlacedFeature(reg.getOrThrow(tree.configuredFeatureId()), tree.placements().forEach(placement -> {
VegetationPlacedFeatures.treeModifiersWithWouldSurvive(tree.placement().orElseThrow(), tree.sapling().orElse(Blocks.OAK_SAPLING)) Registry.register(registry, placement.id(), new PlacedFeature(reg.getOrThrow(tree.configuredFeatureId()),
); VegetationPlacedFeatures.treeModifiersWithWouldSurvive(placement.count(), tree.sapling().orElse(Blocks.OAK_SAPLING))
));
Registry.register(registry, tree.id, placedFeature); });
}); });
}); });
}); });
} }
public static class Builder { public static class Builder {
public static final Predicate<BiomeSelectionContext> IS_FOREST = BiomeSelectors.foundInOverworld().and(BiomeSelectors.tag(BiomeTags.IS_FOREST)); public static final Predicate<BiomeSelectionContext> IS_FOREST = BiomeSelectors.foundInOverworld().and(BiomeSelectors.tag(BiomeTags.IS_FOREST));
public static final Predicate<BiomeSelectionContext> IS_OAK_FOREST = IS_FOREST
.and(BiomeSelectors.excludeByKey(BiomeKeys.BIRCH_FOREST, BiomeKeys.OLD_GROWTH_BIRCH_FOREST, BiomeKeys.DARK_FOREST))
.and(BiomeSelectors.tag(BiomeTags.IS_TAIGA).negate());
public static Builder create(Identifier id, TrunkPlacer trunkPlacer, FoliagePlacer foliagePlacer) { public static Builder create(Identifier id, TrunkPlacer trunkPlacer, FoliagePlacer foliagePlacer) {
return new Builder(id, trunkPlacer, foliagePlacer); return new Builder(id, trunkPlacer, foliagePlacer);
@ -73,8 +76,7 @@ public record Tree (
private final Identifier id; private final Identifier id;
private Optional<Predicate<BiomeSelectionContext>> selector = Optional.empty(); private Map<Identifier, Placement> placements = new HashMap<>();
private Optional<PlacementModifier> countModifier = Optional.empty();
private Function<TreeFeatureConfig.Builder, TreeFeatureConfig.Builder> configParameters = Function.identity(); private Function<TreeFeatureConfig.Builder, TreeFeatureConfig.Builder> configParameters = Function.identity();
private Optional<TwoLayersFeatureSize> size = Optional.empty(); private Optional<TwoLayersFeatureSize> size = Optional.empty();
@ -104,13 +106,18 @@ public record Tree (
return this; return this;
} }
public Builder count(int count, float extraChance, int extraCount) { public Builder placement(int count, float extraChance, int extraCount, Predicate<BiomeSelectionContext> selector) {
countModifier = Optional.of(PlacedFeatures.createCountExtraModifier(count, extraChance, extraCount)); return placement("", count, extraChance, extraCount, selector);
return this;
} }
public Builder biomes(Predicate<BiomeSelectionContext> selector) { public Builder placement(String suffex, int count, float extraChance, int extraCount, Predicate<BiomeSelectionContext> selector) {
this.selector = Optional.of(selector); Identifier id = this.id.withSuffixedPath("/placed" + (suffex.isEmpty() ? "" : "/") + suffex);
placements.put(id, new Placement(
id,
PlacedFeatures.createCountExtraModifier(count, extraChance, extraCount),
RegistryKey.of(RegistryKeys.PLACED_FEATURE, id),
selector
));
return this; return this;
} }
@ -119,7 +126,7 @@ public record Tree (
return this; return this;
} }
public Builder farmingCondition(int yLevel, int sizeBelowY, int sizeAboveY) { public Builder dimensions(int yLevel, int sizeBelowY, int sizeAboveY) {
this.size = Optional.of(new TwoLayersFeatureSize(yLevel, Math.max(0, sizeBelowY), Math.max(0, sizeAboveY))); this.size = Optional.of(new TwoLayersFeatureSize(yLevel, Math.max(0, sizeBelowY), Math.max(0, sizeAboveY)));
return this; return this;
} }
@ -132,23 +139,28 @@ public record Tree (
BlockStateProvider.of(leavesType), BlockStateProvider.of(leavesType),
foliagePlacer, foliagePlacer,
size.get() size.get()
)), configuredFeatureId, selector.map(selector -> { )), configuredFeatureId, placements.values().stream()
RegistryKey<PlacedFeature> i = RegistryKey.of(RegistryKeys.PLACED_FEATURE, id); .collect(Collectors.toUnmodifiableSet()),
BiomeModifications.addFeature(selector, GenerationStep.Feature.VEGETAL_DECORATION, i); saplingId.map(id -> UBlocks.register(id, saplingConstructor.apply(new SaplingGenerator() {
return i;
}), saplingId.map(id -> UBlocks.register(id, saplingConstructor.apply(new SaplingGenerator() {
@Override @Override
protected RegistryKey<ConfiguredFeature<?, ?>> getTreeFeature(Random rng, boolean flowersNearby) { protected RegistryKey<ConfiguredFeature<?, ?>> getTreeFeature(Random rng, boolean flowersNearby) {
return configuredFeatureId; return configuredFeatureId;
} }
}, FabricBlockSettings.copy(Blocks.OAK_SAPLING)), ItemGroups.NATURAL)), countModifier); }, FabricBlockSettings.copy(Blocks.OAK_SAPLING)), ItemGroups.NATURAL)));
if (REGISTRY.isEmpty()) { if (REGISTRY.isEmpty()) {
bootstrap(); bootstrap();
} }
REGISTRY.add(tree); REGISTRY.add(tree);
tree.placements().forEach(placement -> {
BiomeModifications.addFeature(placement.selector(), GenerationStep.Feature.VEGETAL_DECORATION, placement.feature());
});
return tree; return tree;
} }
} }
public record Placement(Identifier id, PlacementModifier count, RegistryKey<PlacedFeature> feature, Predicate<BiomeSelectionContext> selector) {
}
} }

View file

@ -3,7 +3,9 @@ package com.minelittlepony.unicopia.server.world;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.server.world.gen.FruitBlobFoliagePlacer;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
import net.minecraft.block.*; import net.minecraft.block.*;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.intprovider.ConstantIntProvider; import net.minecraft.util.math.intprovider.ConstantIntProvider;
@ -39,19 +41,20 @@ public interface UTreeGen {
.log(UBlocks.ZAP_LOG) .log(UBlocks.ZAP_LOG)
.leaves(UBlocks.ZAP_LEAVES) .leaves(UBlocks.ZAP_LEAVES)
.sapling(Unicopia.id("zapling")) .sapling(Unicopia.id("zapling"))
.biomes(Tree.Builder.IS_FOREST) .placement(0, 0.01F, 1, Tree.Builder.IS_FOREST)
.count(0, 0.01F, 1) .dimensions(6, 0, 8)
.farmingCondition(6, 0, 8)
.build(); .build();
Tree GREEN_APPLE_TREE = createAppleTree("green_apple", UBlocks.GREEN_APPLE_LEAVES, 2); Tree GREEN_APPLE_TREE = createAppleTree("green_apple", UBlocks.GREEN_APPLE_LEAVES, 2, 0.2F).build();
Tree SWEET_APPLE_TREE = createAppleTree("sweet_apple", UBlocks.SWEET_APPLE_LEAVES, 3); Tree SWEET_APPLE_TREE = createAppleTree("sweet_apple", UBlocks.SWEET_APPLE_LEAVES, 3, 0.1F)
Tree SOUR_APPLE_TREE = createAppleTree("sour_apple", UBlocks.SOUR_APPLE_LEAVES, 5); .placement("orchard", 6, 0.1F, 3, BiomeSelectors.includeByKey(UWorldGen.SWEET_APPLE_ORCHARD))
.build();
Tree SOUR_APPLE_TREE = createAppleTree("sour_apple", UBlocks.SOUR_APPLE_LEAVES, 5, 0.2F).build();
Tree GOLDEN_APPLE_TREE = Tree.Builder.create(Unicopia.id("golden_oak_tree"), Tree GOLDEN_APPLE_TREE = Tree.Builder.create(Unicopia.id("golden_oak_tree"),
new StraightTrunkPlacer(6, 1, 3), new StraightTrunkPlacer(6, 1, 3),
new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3)
) )
.configure(TreeFeatureConfig.Builder::forceDirt) .configure(TreeFeatureConfig.Builder::forceDirt)
.farmingCondition(1, 3, 5) .dimensions(1, 3, 5)
.log(UBlocks.GOLDEN_OAK_LOG) .log(UBlocks.GOLDEN_OAK_LOG)
.leaves(UBlocks.GOLDEN_OAK_LEAVES) .leaves(UBlocks.GOLDEN_OAK_LEAVES)
.sapling(Unicopia.id("golden_oak_sapling")) .sapling(Unicopia.id("golden_oak_sapling"))
@ -60,7 +63,7 @@ public interface UTreeGen {
new StraightTrunkPlacer(4, 5, 3), new StraightTrunkPlacer(4, 5, 3),
new FernFoliagePlacer(ConstantIntProvider.create(4), ConstantIntProvider.create(0)) new FernFoliagePlacer(ConstantIntProvider.create(4), ConstantIntProvider.create(0))
) )
.farmingCondition(6, 0, 8) .dimensions(6, 0, 8)
.log(UBlocks.PALM_LOG) .log(UBlocks.PALM_LOG)
.leaves(UBlocks.PALM_LEAVES) .leaves(UBlocks.PALM_LEAVES)
.sapling(Unicopia.id("palm_sapling")).sapling((generator, settings) -> { .sapling(Unicopia.id("palm_sapling")).sapling((generator, settings) -> {
@ -72,36 +75,33 @@ public interface UTreeGen {
}; };
}) })
.configure(builder -> builder.dirtProvider(BlockStateProvider.of(Blocks.SAND))) .configure(builder -> builder.dirtProvider(BlockStateProvider.of(Blocks.SAND)))
.biomes(selector -> selector.hasTag(BiomeTags.IS_BEACH) || selector.hasTag(BiomeTags.IS_JUNGLE)) .placement(2, 0.01F, 1, selector -> selector.hasTag(BiomeTags.IS_BEACH) || selector.hasTag(BiomeTags.IS_JUNGLE))
.count(2, 0.01F, 1)
.build(); .build();
Tree MANGO_TREE = Tree.Builder.create(Unicopia.id("mango_tree"), Tree MANGO_TREE = Tree.Builder.create(Unicopia.id("mango_tree"),
new StraightTrunkPlacer(4, 7, 3), new StraightTrunkPlacer(4, 7, 3),
new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3)
) )
.farmingCondition(9, 0, 4) .dimensions(9, 0, 4)
.log(Blocks.JUNGLE_LOG) .log(Blocks.JUNGLE_LOG)
.leaves(UBlocks.MANGO_LEAVES) .leaves(UBlocks.MANGO_LEAVES)
.biomes(selector -> selector.hasTag(BiomeTags.IS_JUNGLE) && selector.getBiomeKey() != BiomeKeys.SPARSE_JUNGLE)
.sapling(Unicopia.id("mango_sapling")) .sapling(Unicopia.id("mango_sapling"))
.count(1, 1, 2) .placement(1, 1, 2, selector -> selector.hasTag(BiomeTags.IS_JUNGLE) && selector.getBiomeKey() != BiomeKeys.SPARSE_JUNGLE)
.configure(builder -> builder.decorators(ImmutableList.of(TrunkVineTreeDecorator.INSTANCE, new LeavesVineTreeDecorator(0.25f)))) .configure(builder -> builder.decorators(ImmutableList.of(TrunkVineTreeDecorator.INSTANCE, new LeavesVineTreeDecorator(0.25f))))
.build(); .build();
static Tree createAppleTree(String name, Block leaves, int preferredDensity) { static Tree.Builder createAppleTree(String name, Block leaves, int preferredDensity, float spawnRate) {
return Tree.Builder.create(Unicopia.id(name + "_tree"), return Tree.Builder.create(Unicopia.id(name + "_tree"),
new StraightTrunkPlacer(4, 3, 2), new StraightTrunkPlacer(4, 3, 2),
new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) new FruitBlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3)
) )
.configure(TreeFeatureConfig.Builder::forceDirt) .configure(TreeFeatureConfig.Builder::forceDirt)
.biomes(selector -> selector.hasTag(BiomeTags.IS_FOREST)) .placement(0, spawnRate, 4, Tree.Builder.IS_OAK_FOREST)
.count(2, 0.01F, 1) .dimensions(1, preferredDensity - 2, preferredDensity)
.farmingCondition(1, preferredDensity - 2, preferredDensity)
.log(Blocks.OAK_LOG) .log(Blocks.OAK_LOG)
.leaves(leaves) .leaves(leaves)
.sapling(Unicopia.id(name + "_sapling")) .sapling(Unicopia.id(name + "_sapling"));
.build();
} }
static void bootstrap() { } static void bootstrap() {
}
} }

View file

@ -5,6 +5,7 @@ import java.util.List;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.block.ShellsBlock; import com.minelittlepony.unicopia.block.ShellsBlock;
import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.server.world.gen.OverworldBiomeSelectionCallback;
import net.fabricmc.fabric.api.biome.v1.BiomeModifications; import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors; import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
@ -13,11 +14,13 @@ import net.minecraft.block.Blocks;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.BiomeTags; import net.minecraft.registry.tag.BiomeTags;
import net.minecraft.util.collection.DataPool; import net.minecraft.util.collection.DataPool;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3i; import net.minecraft.util.math.Vec3i;
import net.minecraft.util.math.intprovider.UniformIntProvider; import net.minecraft.util.math.intprovider.UniformIntProvider;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys; import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.gen.GenerationStep; import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.blockpredicate.BlockPredicate; import net.minecraft.world.gen.blockpredicate.BlockPredicate;
@ -66,6 +69,8 @@ public interface UWorldGen {
BiomePlacementModifier.of() BiomePlacementModifier.of()
)); ));
RegistryKey<Biome> SWEET_APPLE_ORCHARD = RegistryKey.of(RegistryKeys.BIOME, Unicopia.id("sweet_apple_orchard"));
static void bootstrap() { static void bootstrap() {
BiomeModifications.addFeature(BiomeSelectors.tag(BiomeTags.IS_JUNGLE), GenerationStep.Feature.VEGETAL_DECORATION, PINEAPPLE_PLANT_PLACED_FEATURE); BiomeModifications.addFeature(BiomeSelectors.tag(BiomeTags.IS_JUNGLE), GenerationStep.Feature.VEGETAL_DECORATION, PINEAPPLE_PLANT_PLACED_FEATURE);
BiomeModifications.addFeature( BiomeModifications.addFeature(
@ -75,5 +80,11 @@ public interface UWorldGen {
.or(BiomeSelectors.includeByKey(BiomeKeys.STONY_SHORE)) .or(BiomeSelectors.includeByKey(BiomeKeys.STONY_SHORE))
), GenerationStep.Feature.VEGETAL_DECORATION, SHELLS_PLACED_FEATURE); ), GenerationStep.Feature.VEGETAL_DECORATION, SHELLS_PLACED_FEATURE);
UTreeGen.bootstrap(); UTreeGen.bootstrap();
OverworldBiomeSelectionCallback.EVENT.register(context -> {
if (context.biomeKey() == BiomeKeys.FOREST) {
context.addOverride(context.referenceFrame().temperature().splitAbove(0.9F), SWEET_APPLE_ORCHARD);
}
});
} }
} }

View file

@ -0,0 +1,31 @@
package com.minelittlepony.unicopia.server.world.gen;
import org.jetbrains.annotations.Nullable;
import net.minecraft.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
public interface BiomeSelectionContext {
RegistryKey<Biome> biomeKey();
SplittableBiomeCoordinate referenceFrame();
@Nullable
RegistryKey<Biome> addOverride(SplittableBiomeCoordinate coordinate, RegistryKey<Biome> biome);
public record SplittableBiomeCoordinate(
SplitableParameterRange temperature,
SplitableParameterRange humidity,
SplitableParameterRange continentalness,
SplitableParameterRange erosion,
SplitableParameterRange depth,
SplitableParameterRange weirdness,
long offset) {
}
public interface SplitableParameterRange {
SplittableBiomeCoordinate splitAbove(float midpoint);
SplittableBiomeCoordinate splitBelow(float midpoint);
}
}

View file

@ -0,0 +1,185 @@
package com.minelittlepony.unicopia.server.world.gen;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.include.com.google.common.base.Objects;
import com.minelittlepony.unicopia.server.world.gen.BiomeSelectionContext.SplittableBiomeCoordinate;
import com.mojang.datafixers.util.Pair;
import net.minecraft.registry.RegistryKey;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
import net.minecraft.world.biome.source.util.MultiNoiseUtil.NoiseHypercube;
import net.minecraft.world.biome.source.util.MultiNoiseUtil.ParameterRange;
public final class BiomeSelectionInjector implements Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> {
private final Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parameterConsumer;
public BiomeSelectionInjector(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parameterConsumer) {
this.parameterConsumer = parameterConsumer;
}
@Override
public void accept(Pair<NoiseHypercube, RegistryKey<Biome>> parameter) {
Context context = new Context(parameter);
OverworldBiomeSelectionCallback.EVENT.invoker().onSelectingBiome(context);
if (!context.overrides.isEmpty()) {
List<Pair<NoiseHypercube, RegistryKey<Biome>>> subSections = List.of(parameter);
// sort splits from largest to smallest
var divisions = context.overrides.entrySet()
.stream()
.sorted(Comparator.<Map.Entry<SplittableBiomeCoordinate, RegistryKey<Biome>>, Float>comparing(entry -> Context.getVolume(entry.getKey())).reversed())
.toList();
// recursively sub-divide into parts so every split gets a (kinda) fair share
for (Map.Entry<SplittableBiomeCoordinate, RegistryKey<Biome>> division : divisions) {
subSections = subSections.stream().flatMap(par -> Stream.of(
Pair.of(Context.write(division.getKey(), par.getFirst()), division.getValue()),
par
)).toList();
}
// output the result
subSections.forEach(parameterConsumer);
} else {
parameterConsumer.accept(parameter);
}
}
class Context implements BiomeSelectionContext {
private final Pair<NoiseHypercube, RegistryKey<Biome>> parameter;
private final Map<SplittableBiomeCoordinate, RegistryKey<Biome>> overrides = new LinkedHashMap<>();
public Context(Pair<NoiseHypercube, RegistryKey<Biome>> parameter) {
this.parameter = parameter;
}
@Override
public RegistryKey<Biome> biomeKey() {
return parameter.getSecond();
}
@Override
public SplittableBiomeCoordinate referenceFrame() {
return createStart(parameter.getFirst());
}
@Override
@Nullable
public RegistryKey<Biome> addOverride(SplittableBiomeCoordinate coordinate, RegistryKey<Biome> biome) {
return overrides.put(coordinate, biome);
}
static float getVolume(SplittableBiomeCoordinate coordinate) {
return ((SplitableParameterRangeImpl)coordinate.temperature()).length()
* ((SplitableParameterRangeImpl)coordinate.humidity()).length()
* ((SplitableParameterRangeImpl)coordinate.continentalness()).length()
* ((SplitableParameterRangeImpl)coordinate.erosion()).length()
* ((SplitableParameterRangeImpl)coordinate.depth()).length()
* ((SplitableParameterRangeImpl)coordinate.weirdness()).length();
}
static NoiseHypercube write(SplittableBiomeCoordinate coordinate, NoiseHypercube referenceFrame) {
return new NoiseHypercube(
((SplitableParameterRangeImpl)coordinate.temperature()).write(referenceFrame.temperature()),
((SplitableParameterRangeImpl)coordinate.humidity()).write(referenceFrame.humidity()),
((SplitableParameterRangeImpl)coordinate.continentalness()).write(referenceFrame.continentalness()),
((SplitableParameterRangeImpl)coordinate.erosion()).write(referenceFrame.erosion()),
((SplitableParameterRangeImpl)coordinate.depth()).write(referenceFrame.depth()),
((SplitableParameterRangeImpl)coordinate.weirdness()).write(referenceFrame.weirdness()),
coordinate.offset()
);
}
static SplittableBiomeCoordinate createStart(NoiseHypercube referenceFrame) {
final SplittableBiomeCoordinate[] self = new SplittableBiomeCoordinate[1];
return self[0] = new SplittableBiomeCoordinate(
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::temperature, 0, 1),
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::humidity, 0, 1),
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::continentalness, 0, 1),
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::erosion, 0, 1),
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::depth, 0, 1),
new SplitableParameterRangeImpl(self, SplittableBiomeCoordinate::weirdness, 0, 1),
referenceFrame.offset()
);
}
static SplittableBiomeCoordinate createCopy(SplittableBiomeCoordinate old) {
final SplittableBiomeCoordinate[] self = new SplittableBiomeCoordinate[1];
return self[0] = new SplittableBiomeCoordinate(
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.temperature()),
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.humidity()),
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.continentalness()),
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.erosion()),
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.depth()),
new SplitableParameterRangeImpl(self, (SplitableParameterRangeImpl)old.weirdness()),
old.offset()
);
}
private static final class SplitableParameterRangeImpl implements SplitableParameterRange {
private final SplittableBiomeCoordinate[] coordinate;
private final Function<SplittableBiomeCoordinate, SplitableParameterRange> dimension;
private float min;
private float max;
SplitableParameterRangeImpl(SplittableBiomeCoordinate[] coordinate, SplitableParameterRangeImpl original) {
this(coordinate, original.dimension, original.min, original.max);
}
SplitableParameterRangeImpl(SplittableBiomeCoordinate[] coordinate, Function<SplittableBiomeCoordinate, SplitableParameterRange> dimension, float min, float max) {
this.coordinate = coordinate;
this.dimension = dimension;
this.min = min;
this.max = max;
}
@Override
public SplittableBiomeCoordinate splitAbove(float midpoint) {
return copyWithDifference(o -> o.min = MathHelper.lerp(midpoint, o.min, o.max));
}
@Override
public SplittableBiomeCoordinate splitBelow(float midpoint) {
return copyWithDifference(o -> o.max = MathHelper.lerp(midpoint, o.min, o.max));
}
private SplittableBiomeCoordinate copyWithDifference(Consumer<SplitableParameterRangeImpl> mutator) {
SplittableBiomeCoordinate copy = createCopy(coordinate[0]);
mutator.accept((SplitableParameterRangeImpl)dimension.apply(copy));
return copy;
}
public ParameterRange write(ParameterRange range) {
return ParameterRange.of(
MultiNoiseUtil.toFloat(range.min()) * (1 + min),
MultiNoiseUtil.toFloat(range.max()) * max
);
}
public float length() {
return max - min;
}
@Override
public boolean equals(Object o) {
return o instanceof SplitableParameterRangeImpl i
&& MathHelper.approximatelyEquals(i.min, min)
&& MathHelper.approximatelyEquals(i.max, max);
}
@Override
public int hashCode() {
return Objects.hashCode(min, max);
}
}
}
}

View file

@ -0,0 +1,51 @@
package com.minelittlepony.unicopia.server.world.gen;
import java.util.HashMap;
import java.util.Map;
import org.joml.Vector2i;
import com.minelittlepony.unicopia.block.FruitBearingBlock;
import com.mojang.datafixers.util.Pair;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.intprovider.IntProvider;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.TestableWorld;
import net.minecraft.world.gen.feature.TreeFeatureConfig;
import net.minecraft.world.gen.foliage.BlobFoliagePlacer;
public class FruitBlobFoliagePlacer extends BlobFoliagePlacer {
public FruitBlobFoliagePlacer(IntProvider radius, IntProvider offset, int height) {
super(radius, offset, height);
}
@Override
public void generate(TestableWorld world, BlockPlacer placer, Random random, TreeFeatureConfig config, int trunkHeight, TreeNode treeNode, int foliageHeight, int radius) {
final Map<Vector2i, Pair<FruitBearingBlock, Integer>> leafPositions = new HashMap<>();
super.generate(world, new BlockPlacer() {
@Override
public void placeBlock(BlockPos pos, BlockState state) {
placer.placeBlock(pos, state);
if (state.getBlock() instanceof FruitBearingBlock block
&& block.isPositionValidForFruit(state, pos)) {
leafPositions.compute(new Vector2i(pos.getX(), pos.getZ()), (col, original) -> original == null || original.getSecond() > pos.getY() ? Pair.of(block, pos.getY()) : original);
}
}
@Override
public boolean hasPlacedBlock(BlockPos pos) {
return placer.hasPlacedBlock(pos);
}
}, random, config, trunkHeight, treeNode, foliageHeight, radius);
BlockPos.Mutable mutable = new BlockPos.Mutable();
leafPositions.entrySet().forEach(pos -> {
mutable.set(pos.getKey().x(), pos.getValue().getSecond() - 1, pos.getKey().y());
if (!placer.hasPlacedBlock(mutable) && random.nextInt(12) == 0) {
placer.placeBlock(mutable, pos.getValue().getFirst().getPlacedFruitState(random));
}
});
}
}

View file

@ -0,0 +1,23 @@
package com.minelittlepony.unicopia.server.world.gen;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
/**
* Provides a basic event for mods to inject their own biomes in place of vanilla overworld biomes.
* <p>
* Mods are able to insert entries for their biomes into the ranges for existing ones by
* sub-dividing the provided section into pieces and assigning a new biome to one of the create
* segments.
*/
public interface OverworldBiomeSelectionCallback {
Event<OverworldBiomeSelectionCallback> EVENT = EventFactory.createArrayBacked(OverworldBiomeSelectionCallback.class, delegates -> {
return context -> {
for (OverworldBiomeSelectionCallback delegate : delegates) {
delegate.onSelectingBiome(context);
}
};
});
void onSelectingBiome(BiomeSelectionContext context);
}

View file

@ -1,6 +1,15 @@
{ {
"variants": { "variants": {
"": { "stage=flowering": {
"model": "unicopia:block/green_apple_leaves_flowering"
},
"stage=idle": {
"model": "unicopia:block/green_apple_leaves"
},
"stage=fruiting": {
"model": "unicopia:block/green_apple_leaves"
},
"stage=withering": {
"model": "unicopia:block/green_apple_leaves" "model": "unicopia:block/green_apple_leaves"
} }
} }

View file

@ -1,6 +1,15 @@
{ {
"variants": { "variants": {
"": { "stage=flowering": {
"model": "unicopia:block/sour_apple_leaves_flowering"
},
"stage=idle": {
"model": "unicopia:block/sour_apple_leaves"
},
"stage=fruiting": {
"model": "unicopia:block/sour_apple_leaves"
},
"stage=withering": {
"model": "unicopia:block/sour_apple_leaves" "model": "unicopia:block/sour_apple_leaves"
} }
} }

View file

@ -1,6 +1,15 @@
{ {
"variants": { "variants": {
"": { "stage=flowering": {
"model": "unicopia:block/sweet_apple_leaves_flowering"
},
"stage=idle": {
"model": "unicopia:block/sweet_apple_leaves"
},
"stage=fruiting": {
"model": "unicopia:block/sweet_apple_leaves"
},
"stage=withering": {
"model": "unicopia:block/sweet_apple_leaves" "model": "unicopia:block/sweet_apple_leaves"
} }
} }

View file

@ -232,6 +232,20 @@
"block.unicopia.zap_wood": "Зап-яблоня", "block.unicopia.zap_wood": "Зап-яблоня",
"block.unicopia.stripped_zap_log": "Бревно обтёсанной зап-яблони", "block.unicopia.stripped_zap_log": "Бревно обтёсанной зап-яблони",
"block.unicopia.stripped_zap_wood": "Обтёсанная древесина зап-яблони", "block.unicopia.stripped_zap_wood": "Обтёсанная древесина зап-яблони",
"block.unicopia.zap_planks": "Зап-яблочные доски",
"block.unicopia.zap_stairs": "Зап-яблочные ступеньки",
"block.unicopia.zap_slab": "Зап-яблочная плита",
"block.unicopia.zap_fence": "Зап-яблочный забор",
"block.unicopia.zap_fence_gate": "Зап-яблочная калитка",
"block.unicopia.waxed_zap_log": "Вощёное бревно зап-яблони",
"block.unicopia.waxed_zap_wood": "Вощёная древесина зап-яблони",
"block.unicopia.waxed_stripped_zap_log": "Вощёное бревно обтёсанной зап-яблони",
"block.unicopia.waxed_stripped_zap_wood": "Вощёная древесина обтёсанной зап-яблони",
"block.unicopia.waxed_zap_planks": "Вощёные доски зап-яблони",
"block.unicopia.waxed_zap_stairs": "Вощёные ступеньки зап-яблони",
"block.unicopia.waxed_zap_slab": "Вощёная плита зап-яблони",
"block.unicopia.waxed_zap_fence": "Вощёный зап-яблочный забор",
"block.unicopia.waxed_zap_fence_gate": "Вощёная зап-яблочная калитка",
"block.unicopia.zap_leaves": "Листья зап-яблони", "block.unicopia.zap_leaves": "Листья зап-яблони",
"block.unicopia.flowering_zap_leaves": "Листья цветущей зап-яблони", "block.unicopia.flowering_zap_leaves": "Листья цветущей зап-яблони",
"block.unicopia.zap_apple": "Зап-яблоня", "block.unicopia.zap_apple": "Зап-яблоня",
@ -1447,6 +1461,8 @@
"death.attack.unicopia.horseshoe.self": "%1$s зарядил подковой в себя", "death.attack.unicopia.horseshoe.self": "%1$s зарядил подковой в себя",
"death.attack.unicopia.horseshoe.item": "%2$s зарядил подковой в %1$s используя %3$s", "death.attack.unicopia.horseshoe.item": "%2$s зарядил подковой в %1$s используя %3$s",
"death.attack.unicopia.horseshoe.player": "%2$s зарядил подковой в %1$s", "death.attack.unicopia.horseshoe.player": "%2$s зарядил подковой в %1$s",
"death.attack.unicopia.spikes": "%1$s был заколот до смерти",
"death.attack.unicopia.spikes.player": "%1$s упал на шипы, пытаясь спастись от %2$s",
"death.fell.accident.ladder.pegasus": "%1$s забыл, что умеет летать, и упал с лестницы", "death.fell.accident.ladder.pegasus": "%1$s забыл, что умеет летать, и упал с лестницы",
"death.fell.accident.vines.pegasus": "%1$s забыл, что умеет летать, и упал с лозы", "death.fell.accident.vines.pegasus": "%1$s забыл, что умеет летать, и упал с лозы",

View file

@ -0,0 +1,7 @@
{
"parent": "unicopia:block/sweet_apple_leaves_flowering",
"textures": {
"all": "unicopia:block/green_apple_leaves",
"overlay": "unicopia:block/green_apple_leaves_flowering"
}
}

View file

@ -0,0 +1,7 @@
{
"parent": "unicopia:block/sweet_apple_leaves_flowering",
"textures": {
"all": "unicopia:block/sour_apple_leaves",
"overlay": "unicopia:block/sour_apple_leaves_flowering"
}
}

View file

@ -0,0 +1,31 @@
{ "parent": "minecraft:block/block",
"textures": {
"all": "unicopia:block/sweet_apple_leaves",
"particle": "#all",
"overlay": "unicopia:block/sweet_apple_leaves_flowering"
},
"elements": [
{ "from": [ 0, 0, 0 ],
"to": [ 16, 16, 16 ],
"faces": {
"down": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "tintindex": 0, "cullface": "down" },
"up": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "tintindex": 0, "cullface": "up" },
"north": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "tintindex": 0, "cullface": "north" },
"south": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "tintindex": 0, "cullface": "south" },
"west": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "tintindex": 0, "cullface": "west" },
"east": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "tintindex": 0, "cullface": "east" }
}
},
{ "from": [ 0, 0, 0 ],
"to": [ 16, 16, 16 ],
"faces": {
"down": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "cullface": "down" },
"up": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "cullface": "up" },
"north": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "cullface": "north" },
"south": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "cullface": "south" },
"west": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "cullface": "west" },
"east": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "cullface": "east" }
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"unicopia:sweet_apple_orchard"
]
}

View file

@ -4,7 +4,9 @@
"traits": { "traits": {
"knowledge": 19, "life": 10, "chaos": 4 "knowledge": 19, "life": 10, "chaos": 4
}, },
"ingredients": [], "ingredients": [
{ "item": "unicopia:gemstone", "spell": "unicopia:transformation" }
],
"result": { "result": {
"item": "unicopia:gemstone", "item": "unicopia:gemstone",
"spell": "unicopia:mimic" "spell": "unicopia:mimic"

View file

@ -0,0 +1,199 @@
{
"carvers": {
"air": [
"minecraft:cave",
"minecraft:cave_extra_underground",
"minecraft:canyon"
]
},
"downfall": 0.8,
"effects": {
"fog_color": 12638463,
"mood_sound": {
"block_search_extent": 8,
"offset": 2.0,
"sound": "minecraft:ambient.cave",
"tick_delay": 6000
},
"music": {
"max_delay": 24000,
"min_delay": 12000,
"replace_current_music": false,
"sound": "minecraft:music.overworld.forest"
},
"sky_color": 7972607,
"water_color": 4159204,
"water_fog_color": 329011
},
"features": [
[],
[
"minecraft:lake_lava_underground",
"minecraft:lake_lava_surface"
],
[
"minecraft:amethyst_geode"
],
[
"minecraft:monster_room",
"minecraft:monster_room_deep"
],
[],
[],
[
"minecraft:ore_dirt",
"minecraft:ore_gravel",
"minecraft:ore_granite_upper",
"minecraft:ore_granite_lower",
"minecraft:ore_diorite_upper",
"minecraft:ore_diorite_lower",
"minecraft:ore_andesite_upper",
"minecraft:ore_andesite_lower",
"minecraft:ore_tuff",
"minecraft:ore_coal_upper",
"minecraft:ore_coal_lower",
"minecraft:ore_iron_upper",
"minecraft:ore_iron_middle",
"minecraft:ore_iron_small",
"minecraft:ore_gold",
"minecraft:ore_gold_lower",
"minecraft:ore_redstone",
"minecraft:ore_redstone_lower",
"minecraft:ore_diamond",
"minecraft:ore_diamond_large",
"minecraft:ore_diamond_buried",
"minecraft:ore_lapis",
"minecraft:ore_lapis_buried",
"minecraft:ore_copper",
"minecraft:underwater_magma",
"minecraft:disk_sand",
"minecraft:disk_clay",
"minecraft:disk_gravel"
],
[],
[
"minecraft:spring_water",
"minecraft:spring_lava"
],
[
"minecraft:glow_lichen",
"minecraft:forest_flowers",
"minecraft:flower_default",
"minecraft:patch_grass_forest",
"minecraft:brown_mushroom_normal",
"minecraft:red_mushroom_normal",
"minecraft:patch_sugar_cane",
"minecraft:patch_pumpkin"
],
[
"minecraft:freeze_top_layer"
]
],
"has_precipitation": true,
"spawn_costs": {},
"spawners": {
"ambient": [
{
"type": "minecraft:bat",
"maxCount": 8,
"minCount": 8,
"weight": 10
}
],
"axolotls": [],
"creature": [
{
"type": "minecraft:sheep",
"maxCount": 4,
"minCount": 4,
"weight": 12
},
{
"type": "minecraft:pig",
"maxCount": 4,
"minCount": 4,
"weight": 10
},
{
"type": "minecraft:chicken",
"maxCount": 4,
"minCount": 4,
"weight": 10
},
{
"type": "minecraft:cow",
"maxCount": 4,
"minCount": 4,
"weight": 8
},
{
"type": "minecraft:wolf",
"maxCount": 4,
"minCount": 4,
"weight": 5
}
],
"misc": [],
"monster": [
{
"type": "minecraft:spider",
"maxCount": 4,
"minCount": 4,
"weight": 100
},
{
"type": "minecraft:zombie",
"maxCount": 4,
"minCount": 4,
"weight": 95
},
{
"type": "minecraft:zombie_villager",
"maxCount": 1,
"minCount": 1,
"weight": 5
},
{
"type": "minecraft:skeleton",
"maxCount": 4,
"minCount": 4,
"weight": 100
},
{
"type": "minecraft:creeper",
"maxCount": 4,
"minCount": 4,
"weight": 100
},
{
"type": "minecraft:slime",
"maxCount": 4,
"minCount": 4,
"weight": 100
},
{
"type": "minecraft:enderman",
"maxCount": 4,
"minCount": 1,
"weight": 10
},
{
"type": "minecraft:witch",
"maxCount": 1,
"minCount": 1,
"weight": 5
}
],
"underground_water_creature": [
{
"type": "minecraft:glow_squid",
"maxCount": 6,
"minCount": 4,
"weight": 10
}
],
"water_ambient": [],
"water_creature": []
},
"temperature": 0.8
}

View file

@ -20,6 +20,7 @@
"MixinEnchantmentHelper", "MixinEnchantmentHelper",
"MixinFallLocation", "MixinFallLocation",
"MixinEntity", "MixinEntity",
"MixinEntityView",
"MixinEntityShapeContext", "MixinEntityShapeContext",
"MixinFallingBlock", "MixinFallingBlock",
"MixinFallingBlockEntity", "MixinFallingBlockEntity",
@ -48,6 +49,7 @@
"MixinStateManagerBuilder", "MixinStateManagerBuilder",
"MixinBlockState", "MixinBlockState",
"MixinTargetPredicate", "MixinTargetPredicate",
"MixinVanillaBiomeParameters",
"MixinWardenEntity", "MixinWardenEntity",
"MixinWorld", "MixinWorld",
"MixinWorldChunk", "MixinWorldChunk",
@ -56,7 +58,8 @@
"trinkets.MixinTrinketInventory", "trinkets.MixinTrinketInventory",
"trinkets.MixinScreenHandler", "trinkets.MixinScreenHandler",
"seasons.MixinFertilizableUtil", "seasons.MixinFertilizableUtil",
"ad_astra.MixinOxygenUtils" "ad_astra.MixinOxygenUtils",
"forgified.MixinIForgeBoat"
], ],
"client": [ "client": [
"client.MixinAnimalModel", "client.MixinAnimalModel",