diff --git a/common/src/main/java/dev/latvian/mods/kubejs/item/custom/MultitoolItemJS.java b/common/src/main/java/dev/latvian/mods/kubejs/item/custom/MultitoolItemJS.java new file mode 100644 index 000000000..8a825fd32 --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/item/custom/MultitoolItemJS.java @@ -0,0 +1,186 @@ +package dev.latvian.mods.kubejs.item.custom; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import dev.latvian.mods.kubejs.item.MutableToolTier; +import dev.latvian.mods.kubejs.registry.KubeJSRegistries; +import dev.latvian.mods.kubejs.typings.Info; +import dev.latvian.mods.kubejs.typings.Param; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.item.DiggerItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public class MultitoolItemJS extends DiggerItem { + public static class Builder extends HandheldItemBuilder { + protected final List speedValues = new ArrayList<>(5); + protected final List attackValues = new ArrayList<>(5); + protected final EnumSet toolTypes = EnumSet.noneOf(ToolTypes.class); + + public Builder(ResourceLocation i) { + super(i, 0, 0); + } + + @Info(value = """ + Adds a tool to the multi-tool. + + Valid tool types include: 'axe', 'hoe', 'pickaxe', and 'shovel'. + """, params = + @Param(name = "tool", value = "The name of the tool to add to the multi-tool. Will error if it is an unknown tool type.")) + public Builder tool(ToolTypes tool) { + toolTypes.add(tool); + attackValues.add(tool.attack); + speedValues.add(tool.speed); + return this; + } + + @Override + public Builder attackDamageBaseline(float f) { + attackValues.clear(); + attackValues.add(f); + return this; + } + + @Override + public Builder speedBaseline(float f) { + speedValues.clear(); + speedValues.add(f); + return this; + } + + protected void setValues() { + // this finds the average of all the floats in attack/speedValues, + // then does average += average * 0.08 or 0.05, and rounds it up to the + // tenths place. the resulting value is the attackDamage/speedBaseline. + for (final var f : attackValues) { + attackDamageBaseline += f; + } + attackDamageBaseline = (float)(Math.ceil(attackDamageBaseline * 11.2 / attackValues.size()) / 10); + + for (final var f : speedValues) { + speedBaseline += f; + } + speedBaseline = (float)(Math.ceil(speedBaseline * 11.2 / speedValues.size()) / 10); + } + + @Override + public Item createObject() { + setValues(); + return new MultitoolItemJS(attackDamageBaseline, speedBaseline, toolTier, createItemProperties(), this); + } + } + + public enum ToolTypes { + // maybe swords and shears? (i would need some help with that, as neither extends diggeritem) + AXE(6, -3.1F, BlockTags.MINEABLE_WITH_AXE), + HOE(-2, -1, BlockTags.MINEABLE_WITH_HOE), + PICKAXE(1, -2.8F, BlockTags.MINEABLE_WITH_PICKAXE), + SHOVEL(1.5F, -3, BlockTags.MINEABLE_WITH_SHOVEL); + + public final float attack, speed; + @Nullable + public final TagKey tag; + + ToolTypes(float attack, float speed, @Nullable TagKey tag) { + this.attack = attack; + this.speed = speed; + this.tag = tag; + } + } + + { + defaultModifiers = ArrayListMultimap.create(defaultModifiers); + } + + public final Builder builder; + public final Set> mineableTags; + private boolean modified = false; + + public MultitoolItemJS(float attack, float speed, MutableToolTier tier, Properties properties, Builder builder) { + super(attack, speed, tier, null, properties); + this.builder = builder; + + var mineables = new ImmutableSet.Builder>(); + for (var toolType : builder.toolTypes) + if (toolType.tag != null) + mineables.add(toolType.tag); + mineableTags = mineables.build(); + } + + public boolean isAxe() { + return builder.toolTypes.contains(ToolTypes.AXE); + } + + public boolean isHoe() { + return builder.toolTypes.contains(ToolTypes.HOE); + } + + public boolean isPickaxe() { + return builder.toolTypes.contains(ToolTypes.PICKAXE); + } + + public boolean isShovel() { + return builder.toolTypes.contains(ToolTypes.SHOVEL); + } + + @Override + public float getDestroySpeed(ItemStack itemStack, BlockState blockState) { + return isInMineables(blockState) ? speed : 1f; + } + + @Override + public Multimap getDefaultAttributeModifiers(EquipmentSlot equipmentSlot) { + if (!modified) { + modified = true; + builder.attributes.forEach((r, m) -> defaultModifiers.put(KubeJSRegistries.attributes().get(r), m)); + } + return super.getDefaultAttributeModifiers(equipmentSlot); + } + + @Override + // right-clicking on dirt will till, unless sneaking, then it will turn it into a path + public InteractionResult useOn(UseOnContext ctx) { + return (isAxe() && Items.IRON_AXE.useOn(ctx) != InteractionResult.PASS) || + (isShovel() && (!isHoe() || Optional.ofNullable(ctx.getPlayer()) + .map(Entity::isCrouching).orElse(false)) && // player sneaking check + Items.IRON_SHOVEL.useOn(ctx) != InteractionResult.PASS) || + (isHoe() && Items.IRON_HOE.useOn(ctx) != InteractionResult.PASS) || + (isPickaxe() && Items.IRON_HOE.useOn(ctx) != InteractionResult.PASS) ? + InteractionResult.sidedSuccess(ctx.getLevel().isClientSide) : InteractionResult.PASS; + } + + @Override + public boolean isCorrectToolForDrops(BlockState state) { + final int i = tier.getLevel(); + + return (i >= 3 || !state.is(BlockTags.NEEDS_DIAMOND_TOOL)) && + (i >= 2 || !state.is(BlockTags.NEEDS_IRON_TOOL)) && + (i >= 1 || !state.is(BlockTags.NEEDS_STONE_TOOL)) && + isInMineables(state); + } + + protected boolean isInMineables(BlockState state) { + for (final var tag : mineableTags) + if (state.is(tag)) return true; + return false; + } +} \ No newline at end of file diff --git a/fabric/src/main/java/dev/latvian/mods/kubejs/fabric/BuiltinKubeJSFabricPlugin.java b/fabric/src/main/java/dev/latvian/mods/kubejs/fabric/BuiltinKubeJSFabricPlugin.java index a470ae28f..01df6de9a 100644 --- a/fabric/src/main/java/dev/latvian/mods/kubejs/fabric/BuiltinKubeJSFabricPlugin.java +++ b/fabric/src/main/java/dev/latvian/mods/kubejs/fabric/BuiltinKubeJSFabricPlugin.java @@ -1,10 +1,18 @@ package dev.latvian.mods.kubejs.fabric; import dev.latvian.mods.kubejs.BuiltinKubeJSPlugin; +import dev.latvian.mods.kubejs.item.fabric.custom.MultitoolItemJSFabric; +import dev.latvian.mods.kubejs.registry.RegistryInfo; import dev.latvian.mods.kubejs.script.ScriptType; import dev.latvian.mods.kubejs.util.ClassFilter; public class BuiltinKubeJSFabricPlugin extends BuiltinKubeJSPlugin { + @Override + public void init() { + super.init(); + + RegistryInfo.ITEM.addType("multitool", MultitoolItemJSFabric.Builder.class, MultitoolItemJSFabric.Builder::new); + } @Override public void registerClasses(ScriptType type, ClassFilter filter) { super.registerClasses(type, filter); diff --git a/fabric/src/main/java/dev/latvian/mods/kubejs/item/fabric/custom/MultitoolItemJSFabric.java b/fabric/src/main/java/dev/latvian/mods/kubejs/item/fabric/custom/MultitoolItemJSFabric.java new file mode 100644 index 000000000..e29268699 --- /dev/null +++ b/fabric/src/main/java/dev/latvian/mods/kubejs/item/fabric/custom/MultitoolItemJSFabric.java @@ -0,0 +1,30 @@ +package dev.latvian.mods.kubejs.item.fabric.custom; + +import dev.latvian.mods.kubejs.item.MutableToolTier; +import dev.latvian.mods.kubejs.item.custom.MultitoolItemJS; +import net.fabricmc.fabric.api.mininglevel.v1.MiningLevelManager; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.state.BlockState; + +public class MultitoolItemJSFabric extends MultitoolItemJS { + public static class Builder extends MultitoolItemJS.Builder { + public Builder(ResourceLocation i) { + super(i); + } + + @Override + public Item createObject() { + setValues(); + return new MultitoolItemJSFabric(attackDamageBaseline, speedBaseline, toolTier, createItemProperties(), this); + } + } + public MultitoolItemJSFabric(float attack, float speed, MutableToolTier tier, Properties properties, Builder builder) { + super(attack, speed, tier, properties, builder); + } + + @Override + public boolean isCorrectToolForDrops(BlockState state) { + return tier.getLevel() >= MiningLevelManager.getRequiredMiningLevel(state) && isInMineables(state); + } +} diff --git a/forge/src/main/java/dev/latvian/mods/kubejs/forge/BuiltinKubeJSForgePlugin.java b/forge/src/main/java/dev/latvian/mods/kubejs/forge/BuiltinKubeJSForgePlugin.java index 55861a1a3..a47b9e552 100644 --- a/forge/src/main/java/dev/latvian/mods/kubejs/forge/BuiltinKubeJSForgePlugin.java +++ b/forge/src/main/java/dev/latvian/mods/kubejs/forge/BuiltinKubeJSForgePlugin.java @@ -3,6 +3,8 @@ import dev.latvian.mods.kubejs.BuiltinKubeJSPlugin; import dev.latvian.mods.kubejs.fluid.FluidStackJS; import dev.latvian.mods.kubejs.integration.forge.jei.JEIEvents; +import dev.latvian.mods.kubejs.item.forge.custom.MultitoolItemJSForge; +import dev.latvian.mods.kubejs.registry.RegistryInfo; import dev.latvian.mods.kubejs.script.BindingsEvent; import dev.latvian.mods.kubejs.script.ScriptType; import dev.latvian.mods.kubejs.util.ClassFilter; @@ -14,6 +16,12 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; public class BuiltinKubeJSForgePlugin extends BuiltinKubeJSPlugin { + @Override + public void init() { + super.init(); + + RegistryInfo.ITEM.addType("multitool", MultitoolItemJSForge.Builder.class, MultitoolItemJSForge.Builder::new); + } @Override public void registerEvents() { super.registerEvents(); diff --git a/forge/src/main/java/dev/latvian/mods/kubejs/item/forge/custom/MultitoolItemJSForge.java b/forge/src/main/java/dev/latvian/mods/kubejs/item/forge/custom/MultitoolItemJSForge.java new file mode 100644 index 000000000..cecb52e65 --- /dev/null +++ b/forge/src/main/java/dev/latvian/mods/kubejs/item/forge/custom/MultitoolItemJSForge.java @@ -0,0 +1,48 @@ +package dev.latvian.mods.kubejs.item.forge.custom; + +import dev.latvian.mods.kubejs.item.MutableToolTier; +import dev.latvian.mods.kubejs.item.custom.MultitoolItemJS; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.TierSortingRegistry; +import net.minecraftforge.common.ToolAction; +import net.minecraftforge.common.ToolActions; + +public class MultitoolItemJSForge extends MultitoolItemJS { + public static class Builder extends MultitoolItemJS.Builder { + public Builder(ResourceLocation i) { + super(i); + } + + @Override + public Item createObject() { + setValues(); + return new MultitoolItemJSForge(attackDamageBaseline, speedBaseline, toolTier, createItemProperties(), this); + } + } + + public MultitoolItemJSForge(float attack, float speed, MutableToolTier tier, Properties properties, Builder builder) { + super(attack, speed, tier, properties, builder); + } + + @Override + public boolean canPerformAction(ItemStack stack, ToolAction toolAction) { + return super.canPerformAction(stack, toolAction) || + ((isAxe() && ToolActions.DEFAULT_AXE_ACTIONS.contains(toolAction)) || + (isHoe() && ToolActions.DEFAULT_HOE_ACTIONS.contains(toolAction)) || + (isPickaxe() && ToolActions.DEFAULT_PICKAXE_ACTIONS.contains(toolAction)) || + (isShovel() && ToolActions.DEFAULT_SHOVEL_ACTIONS.contains(toolAction))); + } + + @Override + public boolean isCorrectToolForDrops(BlockState state) { + return isInMineables(state) && TierSortingRegistry.isCorrectTierForDrops(tier, state); + } + + @Override + public boolean isCorrectToolForDrops(ItemStack stack, BlockState state) { + return isCorrectToolForDrops(state); + } +}