diff --git a/build.gradle b/build.gradle index a071ebf1f..63c41a645 100644 --- a/build.gradle +++ b/build.gradle @@ -18,8 +18,9 @@ subprojects { minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" mappings loom.officialMojangMappings() //jabel, for modern Java syntax - annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:1.0.0' - compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:1.0.0' + //https://central.sonatype.com/artifact/com.pkware.jabel/jabel-javac-plugin + annotationProcessor 'com.pkware.jabel:jabel-javac-plugin:1.0.1-1' + compileOnly 'com.pkware.jabel:jabel-javac-plugin:1.0.1-1' } } @@ -38,7 +39,7 @@ allprojects { tasks.withType(JavaCompile) { options.encoding = "UTF-8" - sourceCompatibility = 17 + sourceCompatibility = 21 // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used. diff --git a/common/src/main/java/dev/latvian/kubejs/BuiltinKubeJSPlugin.java b/common/src/main/java/dev/latvian/kubejs/BuiltinKubeJSPlugin.java index 5c2135b95..2a499d091 100644 --- a/common/src/main/java/dev/latvian/kubejs/BuiltinKubeJSPlugin.java +++ b/common/src/main/java/dev/latvian/kubejs/BuiltinKubeJSPlugin.java @@ -141,28 +141,32 @@ public void init() { //block RegistryInfos.BLOCK.addType("basic", BlockBuilder.class, BlockBuilder::new); RegistryInfos.BLOCK.addType("detector", DetectorBlock.Builder.class, DetectorBlock.Builder::new); -// RegistryInfo.BLOCK.addType("slab", SlabBlockBuilder.class, SlabBlockBuilder::new); -// RegistryInfo.BLOCK.addType("stairs", StairBlockBuilder.class, StairBlockBuilder::new); -// RegistryInfo.BLOCK.addType("fence", FenceBlockBuilder.class, FenceBlockBuilder::new); -// RegistryInfo.BLOCK.addType("wall", WallBlockBuilder.class, WallBlockBuilder::new); -// RegistryInfo.BLOCK.addType("fence_gate", FenceGateBlockBuilder.class, FenceGateBlockBuilder::new); -// RegistryInfo.BLOCK.addType("pressure_plate", PressurePlateBlockBuilder.class, PressurePlateBlockBuilder::new); -// RegistryInfo.BLOCK.addType("button", ButtonBlockBuilder.class, ButtonBlockBuilder::new); + RegistryInfos.BLOCK.addType("slab", ShapedBlockBuilderProxy.Slab.class, ShapedBlockBuilderProxy.Slab::new); + RegistryInfos.BLOCK.addType("stairs", ShapedBlockBuilderProxy.Stairs.class, ShapedBlockBuilderProxy.Stairs::new); + RegistryInfos.BLOCK.addType("fence", ShapedBlockBuilderProxy.Fence.class, ShapedBlockBuilderProxy.Fence::new); + RegistryInfos.BLOCK.addType("wall", ShapedBlockBuilderProxy.Wall.class, ShapedBlockBuilderProxy.Wall::new); + RegistryInfos.BLOCK.addType("fence_gate", ShapedBlockBuilderProxy.FenceGate.class, ShapedBlockBuilderProxy.FenceGate::new); + RegistryInfos.BLOCK.addType("stone_pressure_plate", ShapedBlockBuilderProxy.StoneButton.class, ShapedBlockBuilderProxy.StoneButton::new); + RegistryInfos.BLOCK.addType("stone_button", ShapedBlockBuilderProxy.StonePressurePlate.class, ShapedBlockBuilderProxy.StonePressurePlate::new); + RegistryInfos.BLOCK.addType("wooden_pressure_plate", ShapedBlockBuilderProxy.WoodenButton.class, ShapedBlockBuilderProxy.WoodenButton::new); + RegistryInfos.BLOCK.addType("wooden_button", ShapedBlockBuilderProxy.WoodenPressurePlate.class, ShapedBlockBuilderProxy.WoodenPressurePlate::new); +// RegistryInfos.BLOCK.addType("pressure_plate", ShapedBlockBuilderProxy.PressurePlate.class, ShapedBlockBuilderProxy.PressurePlate::new); +// RegistryInfos.BLOCK.addType("button", ShapedBlockBuilderProxy.Button.class, ShapedBlockBuilderProxy.Button::new); // RegistryInfo.BLOCK.addType("falling", FallingBlockBuilder.class, FallingBlockBuilder::new); -// RegistryInfo.BLOCK.addType("crop", CropBlockBuilder.class, CropBlockBuilder::new); -// RegistryInfo.BLOCK.addType("cardinal", HorizontalDirectionalBlockBuilder.class, HorizontalDirectionalBlockBuilder::new); + RegistryInfos.BLOCK.addType("crop", CropBlockBuilder.class, CropBlockBuilder::new); + RegistryInfos.BLOCK.addType("cardinal", HorizontalDirectionalBlockBuilder.class, HorizontalDirectionalBlockBuilder::new); //item RegistryInfos.ITEM.addType("basic", ItemBuilder.class, ItemBuilder::new); -// RegistryInfo.ITEM.addType("sword", SwordItemBuilder.class, SwordItemBuilder::new); -// RegistryInfo.ITEM.addType("pickaxe", PickaxeItemBuilder.class, PickaxeItemBuilder::new); -// RegistryInfo.ITEM.addType("axe", AxeItemBuilder.class, AxeItemBuilder::new); -// RegistryInfo.ITEM.addType("shovel", ShovelItemBuilder.class, ShovelItemBuilder::new); -// RegistryInfo.ITEM.addType("shears", ShearsItemBuilder.class, ShearsItemBuilder::new); -// RegistryInfo.ITEM.addType("hoe", HoeItemBuilder.class, HoeItemBuilder::new); -// RegistryInfo.ITEM.addType("helmet", ArmorItemBuilder.Helmet.class, ArmorItemBuilder.Helmet::new); -// RegistryInfo.ITEM.addType("chestplate", ArmorItemBuilder.Chestplate.class, ArmorItemBuilder.Chestplate::new); -// RegistryInfo.ITEM.addType("leggings", ArmorItemBuilder.Leggings.class, ArmorItemBuilder.Leggings::new); -// RegistryInfo.ITEM.addType("boots", ArmorItemBuilder.Boots.class, ArmorItemBuilder.Boots::new); + RegistryInfos.ITEM.addType("sword", CustomItemBuilderProxy.Sword.class, CustomItemBuilderProxy.Sword::new); + RegistryInfos.ITEM.addType("pickaxe", CustomItemBuilderProxy.Pickaxe.class, CustomItemBuilderProxy.Pickaxe::new); + RegistryInfos.ITEM.addType("axe", CustomItemBuilderProxy.Axe.class, CustomItemBuilderProxy.Axe::new); + RegistryInfos.ITEM.addType("shovel", CustomItemBuilderProxy.Shovel.class, CustomItemBuilderProxy.Shovel::new); + RegistryInfos.ITEM.addType("shears", ShearsItemBuilder.class, ShearsItemBuilder::new); + RegistryInfos.ITEM.addType("hoe", CustomItemBuilderProxy.Hoe.class, CustomItemBuilderProxy.Hoe::new); + RegistryInfos.ITEM.addType("helmet", CustomItemBuilderProxy.Helmet.class, CustomItemBuilderProxy.Helmet::new); + RegistryInfos.ITEM.addType("chestplate", CustomItemBuilderProxy.Chestplate.class, CustomItemBuilderProxy.Chestplate::new); + RegistryInfos.ITEM.addType("leggings", CustomItemBuilderProxy.Leggings.class, CustomItemBuilderProxy.Leggings::new); + RegistryInfos.ITEM.addType("boots", CustomItemBuilderProxy.Boots.class, CustomItemBuilderProxy.Boots::new); RegistryInfos.ITEM.addType("music_disc", RecordItemJS.Builder.class, RecordItemJS.Builder::new); //misc RegistryInfos.FLUID.addType("basic", FluidBuilder.class, FluidBuilder::new); @@ -557,7 +561,7 @@ public void generateLang(Map lang) { for (var builder : RegistryInfos.ALL_BUILDERS) { if (builder.overrideLangJson && builder.display != null) { - lang.put(builder.translationKey, builder.display.getString()); + lang.put(builder.getTranslationKey(), builder.display.getString()); } } } diff --git a/common/src/main/java/dev/latvian/kubejs/block/BlockBuilder.java b/common/src/main/java/dev/latvian/kubejs/block/BlockBuilder.java index f97a84096..8f1808ccf 100644 --- a/common/src/main/java/dev/latvian/kubejs/block/BlockBuilder.java +++ b/common/src/main/java/dev/latvian/kubejs/block/BlockBuilder.java @@ -2,12 +2,11 @@ import com.google.gson.JsonObject; import dev.latvian.kubejs.KubeJSRegistries; -import dev.latvian.kubejs.block.custom.BasicBlockJS; import dev.latvian.kubejs.block.custom.BasicBlockType; import dev.latvian.kubejs.block.custom.BlockType; import dev.latvian.kubejs.client.ModelGenerator; import dev.latvian.kubejs.client.VariantBlockStateGenerator; -import dev.latvian.kubejs.core.ItemKJS; +import dev.latvian.kubejs.core.BlockKJS; import dev.latvian.kubejs.generator.AssetJsonGenerator; import dev.latvian.kubejs.generator.DataJsonGenerator; import dev.latvian.kubejs.loot.LootBuilder; @@ -16,6 +15,7 @@ import dev.latvian.kubejs.script.ScriptType; import dev.latvian.kubejs.registry.BuilderBase; import dev.latvian.mods.rhino.annotations.typing.JSInfo; +import dev.latvian.mods.rhino.util.HideFromJS; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import me.shedaniel.architectury.registry.BlockProperties; import me.shedaniel.architectury.registry.ToolType; @@ -30,92 +30,63 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; /** * @author LatvianModder */ public class BlockBuilder extends BuilderBase { - public transient BlockType type; - public transient MaterialJS material; - public transient float hardness; - public transient float resistance; - public transient float lightLevel; - public transient ToolType harvestTool; - public transient int harvestLevel; - public transient boolean opaque; - public transient boolean fullBlock; - public transient boolean requiresTool; - public transient String renderType; - public transient Int2IntOpenHashMap color; + public transient BlockType type = BasicBlockType.INSTANCE; + public transient MaterialJS material = MaterialListJS.INSTANCE.map.get("wood"); + public transient float hardness = 0.5F; + public transient float resistance = -1F; + public transient float lightLevel = 0F; + public transient ToolType harvestTool = null; + public transient int harvestLevel = -1; + public transient boolean opaque = true; + public transient boolean fullBlock = false; + public transient boolean requiresTool = false; + public transient String renderType = "solid"; + public transient Int2IntOpenHashMap color = new Int2IntOpenHashMap(); public transient BlockTintFunction tint; - public transient final JsonObject textures; - public transient String model; + public transient final JsonObject textures = new JsonObject(); + public transient String model = ""; public transient BlockItemBuilder itemBuilder; - public transient List customShape; - public transient boolean noCollission; - public transient boolean notSolid; - public transient boolean waterlogged; + public transient List customShape = new ArrayList<>(); + public transient boolean noCollission = false; + public transient boolean notSolid = false; + public transient boolean waterlogged = false; public transient float slipperiness = 0.6F; public transient float speedFactor = 1.0F; public transient float jumpFactor = 1.0F; - public Consumer randomTickCallback; + public Consumer randomTickCallback = null; public Consumer lootTable; - public JsonObject blockstateJson; - public JsonObject modelJson; - public transient boolean noValidSpawns; - public transient boolean suffocating; - public transient boolean viewBlocking; - public transient boolean redstoneConductor; - public transient boolean transparent; - public transient Set defaultTags; + public JsonObject blockstateJson = null; + public JsonObject modelJson = null; + public transient boolean noValidSpawns = false; + public transient boolean suffocating = true; + public transient boolean viewBlocking = true; + public transient boolean redstoneConductor = true; + public transient boolean transparent = false; - public transient Block block; + public transient Block block; public BlockBuilder(ResourceLocation id) { super(id); - type = BasicBlockType.INSTANCE; - material = MaterialListJS.INSTANCE.map.get("wood"); - hardness = 0.5F; - resistance = -1F; - lightLevel = 0F; - harvestTool = null; - harvestLevel = -1; - opaque = true; - fullBlock = false; - requiresTool = false; - renderType = "solid"; - color = new Int2IntOpenHashMap(); - color.defaultReturnValue(0xFFFFFFFF); - textures = new JsonObject(); + color.defaultReturnValue(0xFFFFFFFF); textureAll(id.getNamespace() + ":block/" + id.getPath()); - model = ""; - itemBuilder = new BlockItemBuilder(id); + itemBuilder = new BlockItemBuilder(id); itemBuilder.blockBuilder = this; - customShape = new ArrayList<>(); - noCollission = false; - notSolid = false; - waterlogged = false; - randomTickCallback = null; - lootTable = loot -> loot.addPool(pool -> { + lootTable = loot -> loot.addPool(pool -> { pool.survivesExplosion(); pool.addItem(new ItemStack(block)); }); - - blockstateJson = null; - modelJson = null; - noValidSpawns = false; - suffocating = true; - viewBlocking = true; - redstoneConductor = true; - transparent = false; - defaultTags = new HashSet<>(); - } + } @Override public RegistryInfo getRegistryType() { @@ -124,16 +95,19 @@ public RegistryInfo getRegistryType() { @Override public Block createObject() { - return new BasicBlockJS(this); + return this.type.createBlock(this); } + @Override + public Block transformObject(Block obj) { + ((BlockKJS) obj).setBlockBuilderKJS(this); + return obj; + } + @Override public void createAdditionalObjects() { if (this.itemBuilder != null) { - KubeJSRegistries.items().register(itemBuilder.id, () -> itemBuilder.createObject()); - if (itemBuilder.blockItem instanceof ItemKJS kjsItem) { - kjsItem.setItemBuilderKJS(itemBuilder); - } + KubeJSRegistries.items().register(itemBuilder.id, () -> itemBuilder.get()); } } @@ -142,7 +116,12 @@ public String getBuilderType() { return "block"; } - public BlockBuilder type(BlockType t) { + @Deprecated + public Set getDefaultTags() { + return tags.stream().map(ResourceLocation::toString).collect(Collectors.toSet()); + } + + public BlockBuilder type(BlockType t) { type = t; type.applyDefaults(this); return this; @@ -392,18 +371,22 @@ public BlockBuilder box(double x0, double y0, double z0, double x1, double y1, d return box(x0, y0, z0, x1, y1, z1, true); } - public VoxelShape createShape() { - if (customShape.isEmpty()) { - return Shapes.block(); - } + public static VoxelShape createShape(List boxes) { + if (boxes.isEmpty()) { + return Shapes.block(); + } - VoxelShape shape = Shapes.create(customShape.get(0)); + var shape = Shapes.create(boxes.get(0)); - for (int i = 1; i < customShape.size(); i++) { - shape = Shapes.or(shape, Shapes.create(customShape.get(i))); - } + for (var i = 1; i < boxes.size(); i++) { + shape = Shapes.or(shape, Shapes.create(boxes.get(i))); + } + + return shape; + } - return shape; + public VoxelShape createShape() { + return createShape(this.customShape); } public BlockBuilder noCollission() { @@ -477,25 +460,44 @@ public BlockBuilder transparent(boolean b) { } public BlockBuilder defaultCutout() { - return renderType("cutout").notSolid().noValidSpawns(true).suffocating(false).viewBlocking(false).redstoneConductor(false).transparent(true); - } + return renderType("cutout").notSolid() + .noValidSpawns(true) + .suffocating(false) + .viewBlocking(false) + .redstoneConductor(false) + .transparent(true); + } public BlockBuilder defaultTranslucent() { return defaultCutout().renderType("translucent"); } - public BlockBuilder tag(String tag) { - defaultTags.add(tag); - return this; - } + @JSInfo("Tags the block with the given tag.") + public BlockBuilder tag(ResourceLocation tag) { + super.tag(tag); + return this; + } - public BlockBuilder tagBlockAndItem(String tag) { - defaultTags.add(tag); - itemBuilder.tags.add(ResourceLocation.tryParse(tag)); - return this; - } + @JSInfo("Tags the item with the given tag.") + public BlockBuilder tagItem(ResourceLocation tag) { + itemBuilder.tag(tag); + return this; + } + + @JSInfo("Tags both the block and the item with the given tag.") + public BlockBuilder tagBlockAndItem(ResourceLocation tag) { + tag(tag); + tagItem(tag); + return this; + } + + @Deprecated + @HideFromJS + public BlockBuilder tagBlockAndItem(String tag) { + return tagBlockAndItem(new ResourceLocation(tag)); + } - public Block.Properties createProperties() { + public Block.Properties createProperties() { BlockProperties properties = BlockProperties.of(material.getMinecraftMaterial()); properties.sound(material.getSound()); diff --git a/common/src/main/java/dev/latvian/kubejs/block/MaterialListJS.java b/common/src/main/java/dev/latvian/kubejs/block/MaterialListJS.java index ff57e4f86..8394ad0d6 100644 --- a/common/src/main/java/dev/latvian/kubejs/block/MaterialListJS.java +++ b/common/src/main/java/dev/latvian/kubejs/block/MaterialListJS.java @@ -47,6 +47,9 @@ private MaterialListJS() { add("berry_bush", Material.PLANT, SoundType.SWEET_BERRY_BUSH); add("lantern", Material.METAL, SoundType.LANTERN); + //kessjs + add("crop", Material.PLANT, SoundType.CROP); + // Legacy add("rock", Material.STONE, SoundType.STONE); add("iron", Material.METAL, SoundType.METAL); diff --git a/common/src/main/java/dev/latvian/kubejs/block/custom/BasicCropBlockJS.java b/common/src/main/java/dev/latvian/kubejs/block/custom/BasicCropBlockJS.java new file mode 100644 index 000000000..33f0ef2bc --- /dev/null +++ b/common/src/main/java/dev/latvian/kubejs/block/custom/BasicCropBlockJS.java @@ -0,0 +1,99 @@ +package dev.latvian.kubejs.block.custom; + +import dev.latvian.kubejs.block.RandomTickCallbackJS; +import dev.latvian.kubejs.item.ItemBuilder; +import dev.latvian.kubejs.world.BlockContainerJS; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; + +import net.minecraft.world.item.Items; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.CropBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import java.util.List; +import java.util.Random; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; + +public class BasicCropBlockJS extends CropBlock { + private final int age; + private final ItemBuilder seedItem; + private final List shapeByAge; + private final boolean dropSeed; + private final ToDoubleFunction growSpeedCallback; + private final ToIntFunction fertilizerCallback; + private final CropBlockBuilder.SurviveCallback surviveCallback; + + public BasicCropBlockJS(CropBlockBuilder builder) { + super(builder.createProperties().sound(SoundType.CROP).randomTicks()); + age = builder.age; + seedItem = builder.itemBuilder; + shapeByAge = builder.shapeByAge; + dropSeed = builder.dropSeed; + growSpeedCallback = builder.growSpeedCallback; + fertilizerCallback = builder.fertilizerCallback; + surviveCallback = builder.surviveCallback; + } + + @Override + public int getMaxAge() { + return age; + } + + @Override + protected ItemLike getBaseSeedId() { + return dropSeed ? seedItem.get() : Items.AIR; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(getAgeProperty()); + } + + @Override + public VoxelShape getShape(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext) { + return shapeByAge.get(blockState.getValue(this.getAgeProperty())); + } + + @Override + public void randomTick(BlockState blockState, ServerLevel serverLevel, BlockPos blockPos, Random random) { + double f = growSpeedCallback == null ? -1 : growSpeedCallback.applyAsDouble(new RandomTickCallbackJS(new BlockContainerJS(serverLevel, blockPos), random)); + int age = this.getAge(blockState); + if (age < this.getMaxAge()) { + if (f < 0) { + f = getGrowthSpeed(this, serverLevel, blockPos); + } + if (f > 0 && random.nextInt((int) (25.0F / f) + 1) == 0) { + serverLevel.setBlock(blockPos, this.getStateForAge(age + 1), 2); + } + } + } + + @Override + public void growCrops(Level level, BlockPos blockPos, BlockState blockState) { + if (fertilizerCallback == null) { + super.growCrops(level, blockPos, blockState); + } else { + int effect = fertilizerCallback.applyAsInt(new RandomTickCallbackJS(new BlockContainerJS(level, blockPos), level.random)); + if (effect > 0) { + level.setBlock(blockPos, this.getStateForAge(Integer.min(getAge(blockState) + effect, getMaxAge())), 2); + } + } + } + + @Override + public boolean canSurvive(BlockState blockState, LevelReader levelReader, BlockPos blockPos) { + return surviveCallback != null ? + surviveCallback.survive(blockState, levelReader, blockPos) : + super.canSurvive(blockState, levelReader, blockPos); + } +} diff --git a/common/src/main/java/dev/latvian/kubejs/block/custom/CropBlockBuilder.java b/common/src/main/java/dev/latvian/kubejs/block/custom/CropBlockBuilder.java new file mode 100644 index 000000000..0be7c12a0 --- /dev/null +++ b/common/src/main/java/dev/latvian/kubejs/block/custom/CropBlockBuilder.java @@ -0,0 +1,287 @@ +package dev.latvian.kubejs.block.custom; + +import com.google.gson.JsonObject; +import com.mojang.datafixers.util.Pair; +import dev.latvian.kubejs.KubeJS; +import dev.latvian.kubejs.block.BlockBuilder; +import dev.latvian.kubejs.block.MaterialListJS; +import dev.latvian.kubejs.block.RandomTickCallbackJS; +import dev.latvian.kubejs.client.VariantBlockStateGenerator; +import dev.latvian.kubejs.generator.AssetJsonGenerator; +import dev.latvian.kubejs.item.ItemStackJS; +import dev.latvian.kubejs.item.custom.SeedItemBuilder; +import dev.latvian.kubejs.world.BlockContainerJS; +import dev.latvian.mods.rhino.annotations.typing.JSInfo; +import me.shedaniel.architectury.platform.Platform; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.storage.loot.ConstantIntValue; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.function.Consumer; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; + +public class CropBlockBuilder extends BlockBuilder { + @FunctionalInterface + public interface SurviveCallback { + boolean survive(BlockState state, LevelReader reader, BlockPos pos); + } + + public static class ShapeBuilder { + private final List shapes; + + public ShapeBuilder(int age) { + this.shapes = new ArrayList<>(Collections.nCopies(age + 1, Block.box(0.0d, 0.0d, 0.0d, 16.0d, 16.0d, 16.0d))); + } + + @JSInfo(""" + Describe the shape of the crop at a specific age. + + min/max coordinates are double values between 0 and 16. + """) + public ShapeBuilder shape(int age, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + shapes.set(age, Block.box(minX, minY, minZ, maxX, maxY, maxZ)); + return this; + } + + public List getShapes() { + return new ArrayList<>(shapes); + } + } + + public transient int age; + protected transient List shapeByAge; + public transient boolean dropSeed; + public transient ToDoubleFunction growSpeedCallback; + public transient ToIntFunction fertilizerCallback; + public transient SurviveCallback surviveCallback; + + public transient List> outputs; + + public CropBlockBuilder(ResourceLocation i) { + super(i); + age = 7; + shapeByAge = Collections.nCopies(8, Block.box(0.0d, 0.0d, 0.0d, 16.0d, 16.0d, 16.0d)); + growSpeedCallback = null; + fertilizerCallback = null; + surviveCallback = null; + renderType = "cutout"; + noCollission = true; + itemBuilder = new SeedItemBuilder(newID("", "_seed")); + itemBuilder.blockBuilder = this; + hardness = 0.0f; + resistance = 0.0f; + dropSeed = true; + outputs = new ArrayList<>(); + notSolid = true; + + this.material(MaterialListJS.INSTANCE.get("crop")); + + //This should work as a minimum crop-like table + lootTable = loot -> { + var condition = new JsonObject(); + condition.addProperty("condition", "minecraft:block_state_property"); + condition.addProperty("block", this.newID("", "").toString()); + var properties = new JsonObject(); + properties.addProperty("age", String.valueOf(this.age)); + condition.add("properties", properties); + + var function = new JsonObject(); + function.addProperty("function", "minecraft:apply_bonus"); + function.addProperty("enchantment", "minecraft:fortune"); + function.addProperty("formula", "minecraft:binomial_with_bonus_count"); + var parameters = new JsonObject(); + parameters.addProperty("extra", 3); + parameters.addProperty("probability", 0.5714286); //Same as vanilla + function.add("parameters", parameters); + + if (dropSeed) { + loot.addPool(bonuses -> { + bonuses.rolls = ConstantIntValue.exactly(1); + bonuses.bonusRolls = ConstantIntValue.exactly(0); + bonuses.addItem(new ItemStack(itemBuilder.get())) + .addCondition(condition) + .addFunction(function); + bonuses.addItem(new ItemStack(itemBuilder.get())); + }); + } + + for (Pair output : outputs) { + loot.addPool(crops -> { + crops.rolls = ConstantIntValue.exactly(1); + crops.bonusRolls = ConstantIntValue.exactly(0); + crops.addItem(ItemStackJS.of(output.getFirst()).getItemStack()) + .addCondition(condition) + .randomChance(output.getSecond()) + ; + }); + } + }; + + for (int a = 0; a <= age; a++) { + texture(String.valueOf(a), id.getNamespace() + ":block/" + id.getPath() + a); + } + + tag(BlockTags.CROPS.getName()); + if (Platform.isForge()) { + tagItem(new ResourceLocation("forge", "seeds")); + } + } + + @JSInfo("Add a crop output with a 100% chance.") + public CropBlockBuilder crop(Object output) { + crop(output, 1.0); + return this; + } + + @JSInfo("Add a crop output with a specific chance.") + public CropBlockBuilder crop(Object output, double chance) { + outputs.add(new Pair<>(output, chance)); + return this; + } + + @JSInfo("Set the age of the crop. Note that the box will be the same for all ages (A full block size).") + public CropBlockBuilder age(int age) { + age(age, (builder) -> { + }); + return this; + } + + @JSInfo("Set if the crop should drop seeds when harvested.") + public CropBlockBuilder dropSeed(boolean dropSeed) { + this.dropSeed = dropSeed; + return this; + } + + @JSInfo("Set the age of the crop and the shape of the crop at that age.") + public CropBlockBuilder age(int age, Consumer builder) { + this.age = age; + ShapeBuilder shapes = new ShapeBuilder(age); + builder.accept(shapes); + this.shapeByAge = shapes.getShapes(); + for (int i = 0; i <= age; i++) { + texture(String.valueOf(i), id.getNamespace() + ":block/" + id.getPath() + i); + } + return this; + } + + @Override + public BlockBuilder texture(String id, String tex) { + try { + int intId = (int) Double.parseDouble(id); + return super.texture(String.valueOf(intId), tex); + } catch (NumberFormatException e) { + return super.texture(id, tex); + } + } + + public CropBlockBuilder bonemeal(ToIntFunction bonemealCallback) { + this.fertilizerCallback = bonemealCallback; + return this; + } + + public CropBlockBuilder survive(SurviveCallback surviveCallback) { + this.surviveCallback = surviveCallback; + return this; + } + + public CropBlockBuilder growTick(ToDoubleFunction growSpeedCallback) { + this.growSpeedCallback = growSpeedCallback; + return this; + } + + @Override + public BlockBuilder randomTick(@Nullable Consumer randomTickCallback) { + KubeJS.LOGGER.warn("randomTick is overridden by growTick to return grow speed, use it instead."); + return this; + } + + @Override + protected void generateBlockStateJson(VariantBlockStateGenerator bs) { + for (int i = 0; i <= age; i++) { + bs.variant( + String.format("age=%s", i), + model.isEmpty() ? (id.getNamespace() + ":block/" + id.getPath() + i) : model + ); + } + } + + @Override + protected void generateBlockModelJsons(AssetJsonGenerator generator) { + for (int i = 0; i <= age; i++) { + final int fi = i; + generator.blockModel(newID("", String.valueOf(i)), m -> { + m.parent("minecraft:block/crop"); + m.texture("crop", textures.get(String.valueOf(fi)).getAsString()); + }); + } + + } + + @Override + public Block createObject() { + IntegerProperty ageProperty = IntegerProperty.create("age", 0, age); + return new BasicCropBlockJS(this) { + @Override + public IntegerProperty getAgeProperty() { + /* + * Overriding getAgeProperty here because Minecraft calls getAgeProperty + * when CropBlock.class initializes. This happens when nothing is registered + * or assigned yet. + */ + return ageProperty; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(ageProperty); + } + + @Override + public void randomTick(BlockState blockState, ServerLevel serverLevel, BlockPos blockPos, Random random) { + double f = growSpeedCallback == null ? -1 : growSpeedCallback.applyAsDouble(new RandomTickCallbackJS(new BlockContainerJS(serverLevel, blockPos), random)); + int age = this.getAge(blockState); + if (age < this.getMaxAge()) { + if (f < 0) { + f = getGrowthSpeed(this, serverLevel, blockPos); + } + if (f > 0 && random.nextInt((int) (25.0F / f) + 1) == 0) { + serverLevel.setBlock(blockPos, this.getStateForAge(age + 1), 2); + } + } + } + + @Override + public void growCrops(Level level, BlockPos blockPos, BlockState blockState) { + if (fertilizerCallback == null) { + super.growCrops(level, blockPos, blockState); + } else { + int effect = fertilizerCallback.applyAsInt(new RandomTickCallbackJS(new BlockContainerJS(level, blockPos), level.random)); + if (effect > 0) { + level.setBlock(blockPos, this.getStateForAge(Integer.min(getAge(blockState) + effect, getMaxAge())), 2); + } + } + } + + @Override + public boolean canSurvive(BlockState blockState, LevelReader levelReader, BlockPos blockPos) { + return surviveCallback != null ? surviveCallback.survive(blockState, levelReader, blockPos) : super.canSurvive(blockState, levelReader, blockPos); + } + }; + } +} diff --git a/common/src/main/java/dev/latvian/kubejs/block/custom/HorizontalDirectionalBlockBuilder.java b/common/src/main/java/dev/latvian/kubejs/block/custom/HorizontalDirectionalBlockBuilder.java new file mode 100644 index 000000000..8e199c1cc --- /dev/null +++ b/common/src/main/java/dev/latvian/kubejs/block/custom/HorizontalDirectionalBlockBuilder.java @@ -0,0 +1,157 @@ +package dev.latvian.kubejs.block.custom; + +import dev.latvian.kubejs.block.BlockBuilder; +import dev.latvian.kubejs.client.ModelGenerator; +import dev.latvian.kubejs.client.VariantBlockStateGenerator; +import dev.latvian.kubejs.generator.AssetJsonGenerator; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HorizontalDirectionalBlockBuilder extends BlockBuilder { + + // Cardinal blocks that can face any horizontal direction (NSEW). + + public HorizontalDirectionalBlockBuilder(ResourceLocation i) { + super(i); + } + + @Override + protected void generateBlockStateJson(VariantBlockStateGenerator bs) { + var modelLocation = model.isEmpty() ? newID("block/", "").toString() : model; + bs.variant("facing=north", v -> v.model(modelLocation)); + bs.variant("facing=east", v -> v.model(modelLocation).y(90)); + bs.variant("facing=south", v -> v.model(modelLocation).y(180)); + bs.variant("facing=west", v -> v.model(modelLocation).y(270)); + } + + @Override + protected void generateBlockModelJsons(AssetJsonGenerator gen) { + gen.blockModel(id, mg -> { + var side = getTextureOrDefault("side", newID("block/", "").toString()); + + mg.texture("side", side); + mg.texture("front", getTextureOrDefault("front", newID("block/", "_front").toString())); + mg.texture("particle", getTextureOrDefault("particle", side)); + mg.texture("top", getTextureOrDefault("top", side)); + + if (textures.has("bottom")) { + mg.parent("block/orientable_with_bottom"); + mg.texture("bottom", textures.get("bottom").getAsString()); + } else { + mg.parent("minecraft:block/orientable"); + } + }); + } + + @Override + protected void generateItemModelJson(ModelGenerator m) { + m.parent(model.isEmpty() ? newID("block/", "").toString() : model); + } + + @Override + public HorizontalDirectionalBlockBuilder textureAll(String tex) { + super.textureAll(tex); + texture("side", tex); + return this; + } + + private String getTextureOrDefault(String name, String defaultTexture) { + return textures.has(name) ? textures.get(name).getAsString() : defaultTexture; + } + + @Override + public Block createObject() { + return new HorizontalDirectionalBlockJS(this); + } + + public static class HorizontalDirectionalBlockJS extends BasicBlockJS { + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + public final Map shapes = new HashMap<>(); + + public HorizontalDirectionalBlockJS(BlockBuilder p) { + super(p); + if (hasCustomShape()) { + Direction.Plane.HORIZONTAL.forEach(direction -> shapes.put(direction, rotateShape(shape, direction))); + } + } + + private static VoxelShape rotateShape(VoxelShape shape, Direction direction) { + List newShapes = new ArrayList<>(); + + switch (direction) { + case NORTH -> { + return shape; + } + case SOUTH -> shape.forAllBoxes((x1, y1, z1, x2, y2, z2) -> newShapes.add(new AABB(1D - x2, + y1, + 1D - z2, + 1D - x1, + y2, + 1D - z1 + ))); + case WEST -> shape.forAllBoxes((x1, y1, z1, x2, y2, z2) -> newShapes.add(new AABB(z1, + y1, + 1D - x2, + z2, + y2, + 1D - x1 + ))); + case EAST -> shape.forAllBoxes((x1, y1, z1, x2, y2, z2) -> newShapes.add(new AABB(1D - z2, + y1, + x1, + 1D - z1, + y2, + x2 + ))); + default -> throw new IllegalArgumentException("Cannot rotate around direction " + direction.getName()); + } + return createShape(newShapes); + } + + @Override + protected void createBlockStateDefinition(@NotNull StateDefinition.Builder builder) { + builder.add(FACING); + super.createBlockStateDefinition(builder); + } + + @Override + public BlockState getStateForPlacement(@NotNull BlockPlaceContext context) { + var state = defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + + if (properties.waterlogged) { + state = state.setValue(BlockStateProperties.WATERLOGGED, context.getLevel().getFluidState(context.getClickedPos()).getType() == Fluids.WATER); + } + + return state; + } + + private boolean hasCustomShape() { + return shape != Shapes.block(); + } + + @Override + @Deprecated + public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return hasCustomShape() ? shapes.get(state.getValue(FACING)) : shape; + } + } +} diff --git a/common/src/main/java/dev/latvian/kubejs/block/custom/ShapedBlockBuilderProxy.java b/common/src/main/java/dev/latvian/kubejs/block/custom/ShapedBlockBuilderProxy.java new file mode 100644 index 000000000..84a4853c8 --- /dev/null +++ b/common/src/main/java/dev/latvian/kubejs/block/custom/ShapedBlockBuilderProxy.java @@ -0,0 +1,64 @@ +package dev.latvian.kubejs.block.custom; + +import dev.latvian.kubejs.block.BlockBuilder; +import net.minecraft.resources.ResourceLocation; + +/** + * @author ZZZank + */ +public class ShapedBlockBuilderProxy { + public static class Fence extends BlockBuilder { + public Fence(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.FENCE); + } + } + public static class FenceGate extends BlockBuilder { + public FenceGate(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.FENCE_GATE); + } + } + public static class Slab extends BlockBuilder { + public Slab(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.SLAB); + } + } + public static class Stairs extends BlockBuilder { + public Stairs(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.STAIRS); + } + } + public static class StoneButton extends BlockBuilder { + public StoneButton(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.STONE_BUTTON); + } + } + public static class StonePressurePlate extends BlockBuilder { + public StonePressurePlate(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.STONE_PRESSURE_PLATE); + } + } + public static class Wall extends BlockBuilder { + public Wall(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.WALL); + } + } + public static class WoodenButton extends BlockBuilder { + public WoodenButton(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.WOODEN_BUTTON); + } + } + public static class WoodenPressurePlate extends BlockBuilder { + public WoodenPressurePlate(ResourceLocation id) { + super(id); + this.type(ShapedBlockType.WOODEN_PRESSURE_PLATE); + } + } +} diff --git a/common/src/main/java/dev/latvian/kubejs/block/custom/ShapedBlockType.java b/common/src/main/java/dev/latvian/kubejs/block/custom/ShapedBlockType.java index 14a1d4658..701f298fd 100644 --- a/common/src/main/java/dev/latvian/kubejs/block/custom/ShapedBlockType.java +++ b/common/src/main/java/dev/latvian/kubejs/block/custom/ShapedBlockType.java @@ -83,8 +83,8 @@ public void applyDefaults(BlockBuilder builder) { @Override public void generateAssets(BlockBuilder builder, AssetJsonGenerator generator) { - if (builder.block instanceof CustomBlockJS) { - ((CustomBlockJS) builder.block).generateAssets(builder, generator); + if (builder.block instanceof CustomBlockJS custom) { + custom.generateAssets(builder, generator); } else { super.generateAssets(builder, generator); } diff --git a/common/src/main/java/dev/latvian/kubejs/core/ItemKJS.java b/common/src/main/java/dev/latvian/kubejs/core/ItemKJS.java index 0278046b4..98ae05495 100644 --- a/common/src/main/java/dev/latvian/kubejs/core/ItemKJS.java +++ b/common/src/main/java/dev/latvian/kubejs/core/ItemKJS.java @@ -9,7 +9,7 @@ /** * @author LatvianModder */ -public interface ItemKJS { +public interface ItemKJS extends KjsSelf { @Nullable ItemBuilder getItemBuilderKJS(); diff --git a/common/src/main/java/dev/latvian/kubejs/core/KjsSelf.java b/common/src/main/java/dev/latvian/kubejs/core/KjsSelf.java new file mode 100644 index 000000000..160880e1c --- /dev/null +++ b/common/src/main/java/dev/latvian/kubejs/core/KjsSelf.java @@ -0,0 +1,12 @@ +package dev.latvian.kubejs.core; + +/** + * + * @author ZZZank + */ +public interface KjsSelf { + + default T kjs$self() { + return (T) this; + } +} diff --git a/common/src/main/java/dev/latvian/kubejs/event/PlatformEventHandler.java b/common/src/main/java/dev/latvian/kubejs/event/PlatformEventHandler.java index 6b2998bb0..761fe8cfa 100644 --- a/common/src/main/java/dev/latvian/kubejs/event/PlatformEventHandler.java +++ b/common/src/main/java/dev/latvian/kubejs/event/PlatformEventHandler.java @@ -3,10 +3,9 @@ import dev.architectury.injectables.annotations.ExpectPlatform; import dev.latvian.kubejs.KubeJS; import dev.latvian.kubejs.script.ScriptType; -import dev.latvian.mods.rhino.util.DynamicFunction; import org.jetbrains.annotations.Contract; -public abstract class PlatformEventHandler implements DynamicFunction.Callback { +public abstract class PlatformEventHandler { @ExpectPlatform @Contract(value = " -> _") diff --git a/common/src/main/java/dev/latvian/kubejs/item/ItemBuilder.java b/common/src/main/java/dev/latvian/kubejs/item/ItemBuilder.java index ba6ce563e..77801e117 100644 --- a/common/src/main/java/dev/latvian/kubejs/item/ItemBuilder.java +++ b/common/src/main/java/dev/latvian/kubejs/item/ItemBuilder.java @@ -9,10 +9,7 @@ import dev.latvian.kubejs.bindings.RarityWrapper; import dev.latvian.kubejs.core.ItemKJS; import dev.latvian.kubejs.generator.AssetJsonGenerator; -import dev.latvian.kubejs.item.custom.ArmorItemType; -import dev.latvian.kubejs.item.custom.BasicItemJS; -import dev.latvian.kubejs.item.custom.BasicItemType; -import dev.latvian.kubejs.item.custom.ItemType; +import dev.latvian.kubejs.item.custom.*; import dev.latvian.kubejs.registry.RegistryInfo; import dev.latvian.kubejs.registry.BuilderBase; import dev.latvian.kubejs.registry.RegistryInfos; @@ -65,7 +62,7 @@ public class ItemBuilder extends BuilderBase { } public transient final List tooltip; - public transient ItemType type; + public transient ItemType type = BasicItemType.INSTANCE; public transient int maxStackSize; public transient int maxDamage; public transient int burnTime; @@ -116,10 +113,6 @@ public class ItemBuilder extends BuilderBase { public JsonObject modelJson; - public ItemBuilder(String i) { - this(UtilsJS.getMCID(KubeJS.appendModId(i))); - } - public ItemBuilder(ResourceLocation i) { super(i); maxStackSize = 64; @@ -180,15 +173,13 @@ public static Tier toToolTier(Object o) { } @Override - public final RegistryInfo getRegistryType() { + public final RegistryInfo getRegistryType() { return RegistryInfos.ITEM; } @Override public Item createObject() { - object = new BasicItemJS(this); - item = object; - return object; + return type.createItem(this); } @Override diff --git a/common/src/main/java/dev/latvian/kubejs/item/custom/BasicItemJS.java b/common/src/main/java/dev/latvian/kubejs/item/custom/BasicItemJS.java index d72a18361..0dd7dba03 100644 --- a/common/src/main/java/dev/latvian/kubejs/item/custom/BasicItemJS.java +++ b/common/src/main/java/dev/latvian/kubejs/item/custom/BasicItemJS.java @@ -22,7 +22,6 @@ */ public class BasicItemJS extends Item { private final ImmutableMultimap attributes; - private ItemStack containerItem; private Function> subtypes; public BasicItemJS(ItemBuilder p) { diff --git a/common/src/main/java/dev/latvian/kubejs/item/custom/CustomItemBuilderProxy.java b/common/src/main/java/dev/latvian/kubejs/item/custom/CustomItemBuilderProxy.java new file mode 100644 index 000000000..530da7b61 --- /dev/null +++ b/common/src/main/java/dev/latvian/kubejs/item/custom/CustomItemBuilderProxy.java @@ -0,0 +1,70 @@ +package dev.latvian.kubejs.item.custom; + +import dev.latvian.kubejs.item.ItemBuilder; +import net.minecraft.resources.ResourceLocation; + +/** + * @author ZZZank + */ +public class CustomItemBuilderProxy { + public static class Sword extends ItemBuilder { + public Sword(ResourceLocation i) { + super(i); + type(ToolItemType.SWORD); + } + } + public static class Pickaxe extends ItemBuilder { + public Pickaxe(ResourceLocation i) { + super(i); + type(ToolItemType.PICKAXE); + } + } + public static class Axe extends ItemBuilder { + public Axe(ResourceLocation i) { + super(i); + type(ToolItemType.AXE); + } + } + public static class Shovel extends ItemBuilder { + public Shovel(ResourceLocation i) { + super(i); + type(ToolItemType.SHOVEL); + } + } +// public static class Shears extends ItemBuilder { +// public Shears(ResourceLocation i) { +// super(i); +// type(ToolItemType.); +// } +// } + public static class Hoe extends ItemBuilder { + public Hoe(ResourceLocation i) { + super(i); + type(ToolItemType.HOE); + } + } + public static class Helmet extends ItemBuilder { + public Helmet(ResourceLocation i) { + super(i); + type(ArmorItemType.HELMET); + } + } + public static class Chestplate extends ItemBuilder { + public Chestplate(ResourceLocation i) { + super(i); + type(ArmorItemType.CHESTPLATE); + } + } + public static class Leggings extends ItemBuilder { + public Leggings(ResourceLocation i) { + super(i); + type(ArmorItemType.LEGGINGS); + } + } + public static class Boots extends ItemBuilder { + public Boots(ResourceLocation i) { + super(i); + type(ArmorItemType.BOOTS); + } + } +} diff --git a/common/src/main/java/dev/latvian/kubejs/item/custom/SeedItemBuilder.java b/common/src/main/java/dev/latvian/kubejs/item/custom/SeedItemBuilder.java new file mode 100644 index 000000000..ad359d15c --- /dev/null +++ b/common/src/main/java/dev/latvian/kubejs/item/custom/SeedItemBuilder.java @@ -0,0 +1,44 @@ +package dev.latvian.kubejs.item.custom; + +import dev.latvian.kubejs.block.BlockItemBuilder; +import dev.latvian.kubejs.generator.AssetJsonGenerator; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemNameBlockItem; + +public class SeedItemBuilder extends BlockItemBuilder { + public SeedItemBuilder(ResourceLocation i) { + super(i); + } + + @Override + public String getTranslationKeyGroup() { + return "item"; + } + + @Override + public BlockItem createObject() { + return new ItemNameBlockItem(blockBuilder.get(), createItemProperties()); + } + + @Override + public void generateAssetJsons(AssetJsonGenerator generator) { + if (modelJson != null) { + generator.json(AssetJsonGenerator.asItemModelLocation(id), modelJson); + return; + } + + generator.itemModel(id, m -> { + if (!parentModel.isEmpty()) { + m.parent(parentModel); + } else { + m.parent("minecraft:item/generated"); + } + + if (textureJson.size() == 0) { + texture(newID("item/", "").toString()); + } + m.textures(textureJson); + }); + } +} diff --git a/common/src/main/java/dev/latvian/kubejs/item/custom/ShearsItemBuilder.java b/common/src/main/java/dev/latvian/kubejs/item/custom/ShearsItemBuilder.java new file mode 100644 index 000000000..181b88287 --- /dev/null +++ b/common/src/main/java/dev/latvian/kubejs/item/custom/ShearsItemBuilder.java @@ -0,0 +1,67 @@ +package dev.latvian.kubejs.item.custom; + +import dev.latvian.kubejs.item.ItemBuilder; +import me.shedaniel.architectury.platform.Platform; +import net.minecraft.core.dispenser.ShearsDispenseItemBehavior; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ShearsItem; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.DispenserBlock; +import net.minecraft.world.level.block.state.BlockState; + +public class ShearsItemBuilder extends ItemBuilder { + public static boolean isCustomShears(ItemStack stack) { + return stack.getItem() instanceof ShearsItemKJS; + } + + public static final ResourceLocation TAG = new ResourceLocation(Platform.isForge() ? "forge:shears" : "c:shears"); + + public transient float speedBaseline; + + public ShearsItemBuilder(ResourceLocation i) { + super(i); + speedBaseline(5f); + parentModel("minecraft:item/handheld"); + unstackable(); + tag(TAG); + } + + public ShearsItemBuilder speedBaseline(float f) { + speedBaseline = f; + return this; + } + + @Override + public Item createObject() { + var item = new ShearsItemKJS(this); + DispenserBlock.registerBehavior(item, new ShearsDispenseItemBehavior()); + return item; + } + + public static class ShearsItemKJS extends ShearsItem { + public final ShearsItemBuilder builder; + + public ShearsItemKJS(ShearsItemBuilder builder) { + super(builder.createItemProperties()); + this.builder = builder; + } + + @Override + public float getDestroySpeed(ItemStack itemStack, BlockState blockState) { + if (blockState.is(BlockTags.LEAVES)) { + return 15F; + } else if (blockState.is(Blocks.COBWEB)) { + return builder.speedBaseline * 3F; + } else if (blockState.is(Blocks.VINE)) { + return builder.speedBaseline / 2.5F; + } else if (blockState.is(BlockTags.WOOL)) { + return builder.speedBaseline; + } else { + return super.getDestroySpeed(itemStack, blockState); + } + } + } +} diff --git a/common/src/main/java/dev/latvian/kubejs/mixin/common/BlockBehaviourMixin.java b/common/src/main/java/dev/latvian/kubejs/mixin/common/BlockBehaviourMixin.java index 1b2424568..a4ca11f0e 100644 --- a/common/src/main/java/dev/latvian/kubejs/mixin/common/BlockBehaviourMixin.java +++ b/common/src/main/java/dev/latvian/kubejs/mixin/common/BlockBehaviourMixin.java @@ -16,17 +16,17 @@ @Mixin(BlockBehaviour.class) public abstract class BlockBehaviourMixin implements BlockKJS { @Unique - private BlockBuilder blockBuilderKJS; + private BlockBuilder kjs$blockBuilder; @Override @Nullable public BlockBuilder getBlockBuilderKJS() { - return blockBuilderKJS; + return kjs$blockBuilder; } @Override public void setBlockBuilderKJS(BlockBuilder b) { - blockBuilderKJS = b; + kjs$blockBuilder = b; } @Override diff --git a/common/src/main/java/dev/latvian/kubejs/mixin/common/ItemMixin.java b/common/src/main/java/dev/latvian/kubejs/mixin/common/ItemMixin.java index 6caf4189e..41c2b3b06 100644 --- a/common/src/main/java/dev/latvian/kubejs/mixin/common/ItemMixin.java +++ b/common/src/main/java/dev/latvian/kubejs/mixin/common/ItemMixin.java @@ -72,12 +72,12 @@ public void setItemBuilderKJS(ItemBuilder b) { @Override @RemapForJS("setBurnTime") public void setBurnTimeKJS(int i) { - FuelRegistry.register(i, (Item) (Object) this); + FuelRegistry.register(i, kjs$self()); } @RemapForJS("getId") public String getIdKJS() { - return KubeJSRegistries.items().getId((Item) (Object) this).toString(); + return KubeJSRegistries.items().getId(kjs$self()).toString(); } @Override diff --git a/common/src/main/java/dev/latvian/kubejs/registry/BuilderBase.java b/common/src/main/java/dev/latvian/kubejs/registry/BuilderBase.java index e3d35f182..f1a7d11e2 100644 --- a/common/src/main/java/dev/latvian/kubejs/registry/BuilderBase.java +++ b/common/src/main/java/dev/latvian/kubejs/registry/BuilderBase.java @@ -1,6 +1,5 @@ package dev.latvian.kubejs.registry; -import dev.latvian.kubejs.KubeJS; import dev.latvian.kubejs.client.asset.LangEventJS; import dev.latvian.kubejs.generator.AssetJsonGenerator; import dev.latvian.kubejs.generator.DataJsonGenerator; @@ -17,7 +16,7 @@ public abstract class BuilderBase implements Supplier { public final ResourceLocation id; - public String translationKey; + protected String translationKey; public Component display; protected T object; public boolean overrideLangJson; @@ -34,7 +33,7 @@ public BuilderBase(ResourceLocation i) { tags = new HashSet<>(); } - public abstract RegistryInfo getRegistryType(); + public abstract RegistryInfo getRegistryType(); public abstract T createObject(); @@ -49,6 +48,9 @@ public String getBuilderType() { @Override public final T get() { + if (object == null) { + object = transformObject(createObject()); + } return object; } @@ -125,7 +127,7 @@ public void generateDataJsons(DataJsonGenerator generator) { public void generateAssetJsons(AssetJsonGenerator generator) { } - public String getBuilderTranslationKey() { + public String getTranslationKey() { if (translationKey.isEmpty()) { return Util.makeDescriptionId(getTranslationKeyGroup(), id); } @@ -136,12 +138,12 @@ public void generateLang(LangEventJS event) { var name = display == null ? UtilsJS.convertSnakeCaseToCamelCase(id.getPath()) : display.getString(); - event.get(id.getNamespace(), "en_us").add(getBuilderTranslationKey(), name); + event.get(id.getNamespace(), "en_us").add(getTranslationKey(), name); } @JSInfo("dev only, do not use") public T createTransformedObject() { - object = transformObject(createObject()); + object = transformObject(get()); return object; } diff --git a/common/src/main/java/dev/latvian/kubejs/registry/types/particle/ParticleTypeBuilder.java b/common/src/main/java/dev/latvian/kubejs/registry/types/particle/ParticleTypeBuilder.java index 7a7ba89bc..26e5d0707 100644 --- a/common/src/main/java/dev/latvian/kubejs/registry/types/particle/ParticleTypeBuilder.java +++ b/common/src/main/java/dev/latvian/kubejs/registry/types/particle/ParticleTypeBuilder.java @@ -7,7 +7,7 @@ import net.minecraft.core.particles.ParticleType; import net.minecraft.resources.ResourceLocation; -public class ParticleTypeBuilder extends BuilderBase> { +public class ParticleTypeBuilder extends BuilderBase { public transient boolean overrideLimiter; public transient ParticleOptions.Deserializer deserializer; diff --git a/common/src/main/java/dev/latvian/kubejs/server/TagEventJS.java b/common/src/main/java/dev/latvian/kubejs/server/TagEventJS.java index 5f7a5bd11..9bf2fb2bb 100644 --- a/common/src/main/java/dev/latvian/kubejs/server/TagEventJS.java +++ b/common/src/main/java/dev/latvian/kubejs/server/TagEventJS.java @@ -391,8 +391,8 @@ public void post(String event) { } else if (type.equals("blocks")) { for (BuilderBase builderBase : RegistryInfos.BLOCK.objects.values()) { if (builderBase instanceof BlockBuilder builder) { - for (String s : builder.defaultTags) { - add(new ResourceLocation(s), builder.id); + for (var s : builder.tags) { + add(s, builder.id); } } } diff --git a/common/src/main/resources/kubejs.accesswidener b/common/src/main/resources/kubejs.accesswidener index f4ff4c3c7..abccf4af5 100644 --- a/common/src/main/resources/kubejs.accesswidener +++ b/common/src/main/resources/kubejs.accesswidener @@ -9,3 +9,5 @@ accessible field net/minecraft/commands/Commands$CommandSelection includeDedicat accessible method net/minecraft/world/entity/npc/VillagerType (Ljava/lang/String;)V accessible method net/minecraft/world/entity/npc/VillagerProfession (Ljava/lang/String;Lnet/minecraft/world/entity/ai/village/poi/PoiType;Lcom/google/common/collect/ImmutableSet;Lcom/google/common/collect/ImmutableSet;Lnet/minecraft/sounds/SoundEvent;)V accessible method net/minecraft/world/entity/ai/village/poi/PoiType (Ljava/lang/String;Ljava/util/Set;II)V + +accessible field net/minecraft/world/item/ArmorItem defaultModifiers Lcom/google/common/collect/Multimap; \ No newline at end of file diff --git a/forge/src/main/java/dev/latvian/kubejs/event/forge/PlatformEventHandlerImpl.java b/forge/src/main/java/dev/latvian/kubejs/event/forge/PlatformEventHandlerImpl.java index aab0a4b44..4867c8993 100644 --- a/forge/src/main/java/dev/latvian/kubejs/event/forge/PlatformEventHandlerImpl.java +++ b/forge/src/main/java/dev/latvian/kubejs/event/forge/PlatformEventHandlerImpl.java @@ -2,24 +2,33 @@ import dev.latvian.kubejs.event.PlatformEventHandler; import dev.latvian.kubejs.forge.KubeJSForgeEventHandlerWrapper; +import dev.latvian.kubejs.util.UtilsJS; +import dev.latvian.mods.rhino.Kit; +import dev.latvian.mods.rhino.NativeJavaClass; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.GenericEvent; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; public class PlatformEventHandlerImpl extends PlatformEventHandler { private static final PlatformEventHandlerImpl INSTANCE = new PlatformEventHandlerImpl(); private final List listeners = new ArrayList<>(); + private final List>> genericListeners = new ArrayList<>(); @Override public void unregister() { - for (var listener : this.listeners) { - MinecraftForge.EVENT_BUS.unregister(listener); - } - this.listeners.clear(); + for (var listener : this.listeners) { + MinecraftForge.EVENT_BUS.unregister(listener); + } + this.listeners.clear(); + for (var listener : this.genericListeners) { + MinecraftForge.EVENT_BUS.unregister(listener); + } + this.genericListeners.clear(); } /** @@ -29,27 +38,64 @@ public static PlatformEventHandler instance() { return INSTANCE; } - @Override - public @Nullable Object call(Object[] params) { + private static Class readClass(Object o) { + if (o instanceof String s) { + return Kit.classOrNull(s); + } else if (o instanceof Class c) { + return c; + } else if (o instanceof NativeJavaClass njc) { + return njc.getClassObject(); + } + return null; + } + + public static @Nullable Object onEvent(Object[] params) { if (params.length < 2) { throw new RuntimeException("Invalid syntax! onForgeEvent(string | Class, function) required event class and handler"); } - try { - var clazz = params[0] instanceof Class c ? c : Class.forName(params[0].toString()); - if (!Event.class.isAssignableFrom(clazz)) { - throw new RuntimeException("The first parameter of onForgeEvent() must represent a class that is a subclass of `net.minecraftforge.eventbus.api.Event`"); + try { + Class clazz = readClass(params[0]); + if (clazz == null) { + throw new RuntimeException("The first parameter must represent a proper class, instead got:" + params[0]); } - var handler = secured((KubeJSForgeEventHandlerWrapper) params[1],clazz); - MinecraftForge.EVENT_BUS.addListener(EventPriority.NORMAL, false, clazz, handler); - listeners.add(handler); - } catch (Exception ex) { - throw new RuntimeException(ex); - } + var handler = secured((KubeJSForgeEventHandlerWrapper) params[1], clazz); + MinecraftForge.EVENT_BUS.addListener(EventPriority.NORMAL, false, UtilsJS.cast(clazz), handler); + INSTANCE.listeners.add(handler); + } catch (Exception e) { + throw new RuntimeException("Error when creating event handler", e); + } - return null; + return null; } + public static @Nullable Object onGenericEvent(Object[] params) { + if (params.length < 2) { + throw new RuntimeException("Invalid syntax! onForgeEvent(string | Class, function) required event class and handler"); + } + + Class clazz = readClass(params[0]); + if (clazz == null) { + throw new RuntimeException("The first parameter must represent a proper class, instead got:" + params[0]); + } + + try { + Consumer> handler = (event) -> { + try { + ((Consumer>) params[1]).accept(event); + } catch (Exception e) { + logException(e, "Error in native generic event listener for " + clazz); + } + }; + MinecraftForge.EVENT_BUS.addGenericListener(UtilsJS.cast(clazz), handler); + INSTANCE.genericListeners.add(handler); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return null; + } + /** * wrap provided event handler with a try-catch that will catch Exception and log them using {@link PlatformEventHandler#logException(Exception, String)} * @param handler event handler @@ -61,7 +107,7 @@ static KubeJSForgeEventHandlerWrapper secured(KubeJSForgeEventHandlerWrapper han try { handler.accept(event); } catch (Exception ex) { - logException(ex, "Error when calling 'onForgeEvent' for " + eventTarget); + logException(ex, "Error in native event listener for " + eventTarget); } }; } diff --git a/forge/src/main/java/dev/latvian/kubejs/fluid/forge/KubeJSFluidEventHandlerImpl.java b/forge/src/main/java/dev/latvian/kubejs/fluid/forge/KubeJSFluidEventHandlerImpl.java index 22b2b647d..b92e3000b 100644 --- a/forge/src/main/java/dev/latvian/kubejs/fluid/forge/KubeJSFluidEventHandlerImpl.java +++ b/forge/src/main/java/dev/latvian/kubejs/fluid/forge/KubeJSFluidEventHandlerImpl.java @@ -2,6 +2,7 @@ import dev.latvian.kubejs.KubeJS; import dev.latvian.kubejs.fluid.FluidBuilder; +import dev.latvian.kubejs.fluid.KubeJSFluidEventHandler; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.BucketItem; @@ -15,7 +16,7 @@ import net.minecraftforge.fluids.capability.wrappers.FluidBucketWrapper; import org.jetbrains.annotations.Nullable; -public class KubeJSFluidEventHandlerImpl { +public class KubeJSFluidEventHandlerImpl extends KubeJSFluidEventHandler { public static FlowingFluid buildFluid(boolean source, FluidBuilder builder) { if (source) { return new ForgeFlowingFluid.Source(createProperties(builder)); @@ -28,23 +29,27 @@ public static ForgeFlowingFluid.Properties createProperties(FluidBuilder fluidBu if (fluidBuilder.extraPlatformInfo != null) { return (ForgeFlowingFluid.Properties) fluidBuilder.extraPlatformInfo; } - FluidAttributes.Builder builder = FluidAttributes.builder( - new ResourceLocation(fluidBuilder.stillTexture), - new ResourceLocation(fluidBuilder.flowingTexture)) - .translationKey("fluid." + fluidBuilder.id.getNamespace() + "." + fluidBuilder.id.getPath()) - .color(fluidBuilder.color) - .rarity(fluidBuilder.rarity.rarity) - .density(fluidBuilder.density) - .viscosity(fluidBuilder.viscosity) - .luminosity(fluidBuilder.luminosity) - .temperature(fluidBuilder.temperature); + FluidAttributes.Builder builder = FluidAttributes.builder( + new ResourceLocation(fluidBuilder.stillTexture), + new ResourceLocation(fluidBuilder.flowingTexture) + ) + .translationKey(fluidBuilder.getTranslationKey()) + .color(fluidBuilder.color) + .rarity(fluidBuilder.rarity.rarity) + .density(fluidBuilder.density) + .viscosity(fluidBuilder.viscosity) + .luminosity(fluidBuilder.luminosity) + .temperature(fluidBuilder.temperature); - if (fluidBuilder.isGaseous) { + if (fluidBuilder.isGaseous) { builder.gaseous(); } - ForgeFlowingFluid.Properties properties = new ForgeFlowingFluid.Properties(() -> fluidBuilder.stillFluid, () -> fluidBuilder.flowingFluid, builder).bucket(() -> fluidBuilder.bucketItem).block(() -> fluidBuilder.block); - fluidBuilder.extraPlatformInfo = properties; + ForgeFlowingFluid.Properties properties = new ForgeFlowingFluid + .Properties(() -> fluidBuilder.stillFluid, () -> fluidBuilder.flowingFluid, builder) + .bucket(() -> fluidBuilder.bucketItem) + .block(() -> fluidBuilder.block); + fluidBuilder.extraPlatformInfo = properties; return properties; } diff --git a/forge/src/main/java/dev/latvian/kubejs/forge/BuiltinKubeJSForgePlugin.java b/forge/src/main/java/dev/latvian/kubejs/forge/BuiltinKubeJSForgePlugin.java index 1bb1b9f89..c7eb876a5 100644 --- a/forge/src/main/java/dev/latvian/kubejs/forge/BuiltinKubeJSForgePlugin.java +++ b/forge/src/main/java/dev/latvian/kubejs/forge/BuiltinKubeJSForgePlugin.java @@ -1,7 +1,7 @@ package dev.latvian.kubejs.forge; import dev.latvian.kubejs.BuiltinKubeJSPlugin; -import dev.latvian.kubejs.event.PlatformEventHandler; +import dev.latvian.kubejs.event.forge.PlatformEventHandlerImpl; import dev.latvian.kubejs.script.BindingsEvent; import dev.latvian.kubejs.script.ScriptType; import dev.latvian.kubejs.util.ClassFilter; @@ -28,8 +28,13 @@ public void addBindings(BindingsEvent event) { super.addBindings(event); if (event.type == ScriptType.STARTUP) { - event.addFunction("onForgeEvent", PlatformEventHandler.instance(), null, KubeJSForgeEventHandlerWrapper.class); - } + event.addFunction( + "onForgeEvent", + PlatformEventHandlerImpl::onEvent, + null, + KubeJSForgeEventHandlerWrapper.class + ); + } event.add("BiomeDictionary", BiomeDictionaryWrapper.class); } @@ -45,6 +50,6 @@ public void addTypeWrappers(ScriptType type, TypeWrappers typeWrappers) { */ @Deprecated public static Object onPlatformEvent(BindingsEvent event, Object[] args) { - return PlatformEventHandler.instance().call(args); + return PlatformEventHandlerImpl.onEvent(args); } }