mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-08-29 16:47:56 +00:00
Make upgrade recipe requirements a little more lax
- Move some common upgrade code to IUpgradeBase. 99% sure this this preserves binary compatibility (on the JVM at least). - Instead of requiring the share tag to match, allow upgrades to specify their own predicate. IMO this is a little ugly, but required to fix #614 as other mods chuck their own NBT on items.
This commit is contained in:
81
src/main/java/dan200/computercraft/api/IUpgradeBase.java
Normal file
81
src/main/java/dan200/computercraft/api/IUpgradeBase.java
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package dan200.computercraft.api;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common functionality between {@link ITurtleUpgrade} and {@link IPocketUpgrade}.
|
||||||
|
*/
|
||||||
|
public interface IUpgradeBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem"
|
||||||
|
* or "my_mod:my_upgrade".
|
||||||
|
*
|
||||||
|
* You should use a unique resource domain to ensure this upgrade is uniquely identified.
|
||||||
|
* The upgrade will fail registration if an already used ID is specified.
|
||||||
|
*
|
||||||
|
* @return The unique ID for this upgrade.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Identifier getUpgradeID();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an unlocalised string to describe this type of computer in item names.
|
||||||
|
*
|
||||||
|
* Examples of built-in adjectives are "Wireless", "Mining" and "Crafty".
|
||||||
|
*
|
||||||
|
* @return The localisation key for this upgrade's adjective.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
String getUnlocalisedAdjective();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an item stack representing the type of item that a computer must be crafted
|
||||||
|
* with to create a version which holds this upgrade. This item stack is also used
|
||||||
|
* to determine the upgrade given by {@code turtle.equipLeft()} or {@code pocket.equipBack()}
|
||||||
|
*
|
||||||
|
* This should be constant over a session (or at least a datapack reload). It is recommended
|
||||||
|
* that you cache the stack too, in order to prevent constructing it every time the method
|
||||||
|
* is called.
|
||||||
|
*
|
||||||
|
* @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
ItemStack getCraftingItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an item is suitable for being used for this upgrade.
|
||||||
|
*
|
||||||
|
* When un-equipping an upgrade, we return {@link #getCraftingItem()} rather than
|
||||||
|
* the original stack. In order to prevent people losing items with enchantments (or
|
||||||
|
* repairing items with non-0 damage), we impose additional checks on the item.
|
||||||
|
*
|
||||||
|
* The default check requires that any non-capability NBT is exactly the same as the
|
||||||
|
* crafting item, but this may be relaxed for your upgrade.
|
||||||
|
*
|
||||||
|
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
|
||||||
|
* {@link #getCraftingItem()}.
|
||||||
|
* @return If this stack may be used to equip this upgrade.
|
||||||
|
* @see net.minecraftforge.common.crafting.NBTIngredient#test(ItemStack) For the implementation of the default
|
||||||
|
* check.
|
||||||
|
*/
|
||||||
|
default boolean isItemSuitable( @Nonnull ItemStack stack )
|
||||||
|
{
|
||||||
|
ItemStack crafting = getCraftingItem();
|
||||||
|
|
||||||
|
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
||||||
|
// null one.
|
||||||
|
CompoundTag shareTag = stack.getTag();
|
||||||
|
CompoundTag craftingShareTag = crafting.getTag();
|
||||||
|
if( shareTag == craftingShareTag ) return true;
|
||||||
|
if( shareTag == null ) return craftingShareTag.isEmpty();
|
||||||
|
if( craftingShareTag == null ) return shareTag.isEmpty();
|
||||||
|
return shareTag.equals( craftingShareTag );
|
||||||
|
}
|
||||||
|
}
|
@@ -10,56 +10,18 @@ import javax.annotation.Nonnull;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.IUpgradeBase;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional peripherals for pocket computers.
|
* Additional peripherals for pocket computers.
|
||||||
*
|
*
|
||||||
* This is similar to {@link ITurtleUpgrade}.
|
* @see ComputerCraftAPI#registerPocketUpgrade(IPocketUpgrade)
|
||||||
*/
|
*/
|
||||||
public interface IPocketUpgrade {
|
public interface IPocketUpgrade extends IUpgradeBase
|
||||||
|
{
|
||||||
/**
|
|
||||||
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade".
|
|
||||||
*
|
|
||||||
* You should use a unique resource domain to ensure this upgrade is uniquely identified. The upgrade will fail registration if an already used ID is
|
|
||||||
* specified.
|
|
||||||
*
|
|
||||||
* @return The upgrade's id.
|
|
||||||
* @see IPocketUpgrade#getUpgradeID()
|
|
||||||
* @see ComputerCraftAPI#registerPocketUpgrade(IPocketUpgrade)
|
|
||||||
*/
|
|
||||||
@Nonnull
|
|
||||||
Identifier getUpgradeID();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an unlocalised string to describe the type of pocket computer this upgrade provides.
|
|
||||||
*
|
|
||||||
* An example of a built-in adjectives is "Wireless" - this is converted to "Wireless Pocket Computer".
|
|
||||||
*
|
|
||||||
* @return The unlocalised adjective.
|
|
||||||
* @see ITurtleUpgrade#getUnlocalisedAdjective()
|
|
||||||
*/
|
|
||||||
@Nonnull
|
|
||||||
String getUnlocalisedAdjective();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an item stack representing the type of item that a pocket computer must be crafted with to create a pocket computer which holds this upgrade.
|
|
||||||
* This item stack is also used to determine the upgrade given by {@code pocket.equip()}/{@code pocket.unequip()}.
|
|
||||||
*
|
|
||||||
* Ideally this should be constant over a session. It is recommended that you cache the item too, in order to prevent constructing it every time the
|
|
||||||
* method is called.
|
|
||||||
*
|
|
||||||
* @return The item stack used for crafting. This can be {@link ItemStack#EMPTY} if crafting is disabled.
|
|
||||||
*/
|
|
||||||
@Nonnull
|
|
||||||
ItemStack getCraftingItem();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a peripheral for the pocket computer.
|
* Creates a peripheral for the pocket computer.
|
||||||
*
|
*
|
||||||
|
@@ -10,11 +10,10 @@ import javax.annotation.Nonnull;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.IUpgradeBase;
|
||||||
import dan200.computercraft.api.client.TransformedModel;
|
import dan200.computercraft.api.client.TransformedModel;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
|
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
@@ -25,27 +24,8 @@ import net.fabricmc.api.Environment;
|
|||||||
*
|
*
|
||||||
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
|
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
|
||||||
*/
|
*/
|
||||||
public interface ITurtleUpgrade {
|
public interface ITurtleUpgrade extends IUpgradeBase
|
||||||
/**
|
{
|
||||||
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade". You should use a unique
|
|
||||||
* resource domain to ensure this upgrade is uniquely identified. The turtle will fail registration if an already used ID is specified.
|
|
||||||
*
|
|
||||||
* @return The unique ID for this upgrade.
|
|
||||||
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
|
|
||||||
*/
|
|
||||||
@Nonnull
|
|
||||||
Identifier getUpgradeID();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an unlocalised string to describe this type of turtle in turtle item names.
|
|
||||||
*
|
|
||||||
* Examples of built-in adjectives are "Wireless", "Mining" and "Crafty".
|
|
||||||
*
|
|
||||||
* @return The localisation key for this upgrade's adjective.
|
|
||||||
*/
|
|
||||||
@Nonnull
|
|
||||||
String getUnlocalisedAdjective();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether this turtle adds a tool or a peripheral to the turtle.
|
* Return whether this turtle adds a tool or a peripheral to the turtle.
|
||||||
*
|
*
|
||||||
@@ -55,18 +35,6 @@ public interface ITurtleUpgrade {
|
|||||||
@Nonnull
|
@Nonnull
|
||||||
TurtleUpgradeType getType();
|
TurtleUpgradeType getType();
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an item stack representing the type of item that a turtle must be crafted with to create a turtle which holds this upgrade. This item stack is
|
|
||||||
* also used to determine the upgrade given by {@code turtle.equip()}
|
|
||||||
*
|
|
||||||
* Ideally this should be constant over a session. It is recommended that you cache the item too, in order to prevent constructing it every time the
|
|
||||||
* method is called.
|
|
||||||
*
|
|
||||||
* @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted.
|
|
||||||
*/
|
|
||||||
@Nonnull
|
|
||||||
ItemStack getCraftingItem();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade.
|
* Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade.
|
||||||
*
|
*
|
||||||
|
@@ -59,7 +59,8 @@ public final class PocketUpgrades {
|
|||||||
|
|
||||||
for (IPocketUpgrade upgrade : upgrades.values()) {
|
for (IPocketUpgrade upgrade : upgrades.values()) {
|
||||||
ItemStack craftingStack = upgrade.getCraftingItem();
|
ItemStack craftingStack = upgrade.getCraftingItem();
|
||||||
if (!craftingStack.isEmpty() && InventoryUtil.areItemsSimilar(stack, craftingStack)) {
|
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.isItemSuitable( stack ) )
|
||||||
|
{
|
||||||
return upgrade;
|
return upgrade;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@ import javax.annotation.Nullable;
|
|||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.util.InventoryUtil;
|
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
@@ -96,7 +95,8 @@ public final class TurtleUpgrades {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ItemStack craftingStack = wrapper.upgrade.getCraftingItem();
|
ItemStack craftingStack = wrapper.upgrade.getCraftingItem();
|
||||||
if (!craftingStack.isEmpty() && InventoryUtil.areItemsSimilar(stack, craftingStack)) {
|
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && wrapper.upgrade.isItemSuitable( stack ) )
|
||||||
|
{
|
||||||
return wrapper.upgrade;
|
return wrapper.upgrade;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,8 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.client.TransformedModel;
|
import dan200.computercraft.api.client.TransformedModel;
|
||||||
import dan200.computercraft.api.turtle.AbstractTurtleUpgrade;
|
import dan200.computercraft.api.turtle.AbstractTurtleUpgrade;
|
||||||
@@ -29,8 +31,6 @@ import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
|||||||
import dan200.computercraft.shared.util.DropConsumer;
|
import dan200.computercraft.shared.util.DropConsumer;
|
||||||
import dan200.computercraft.shared.util.InventoryUtil;
|
import dan200.computercraft.shared.util.InventoryUtil;
|
||||||
import dan200.computercraft.shared.util.WorldUtil;
|
import dan200.computercraft.shared.util.WorldUtil;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
@@ -41,9 +41,9 @@ import net.minecraft.entity.Entity;
|
|||||||
import net.minecraft.entity.attribute.EntityAttributes;
|
import net.minecraft.entity.attribute.EntityAttributes;
|
||||||
import net.minecraft.entity.damage.DamageSource;
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
import net.minecraft.entity.decoration.ArmorStandEntity;
|
import net.minecraft.entity.decoration.ArmorStandEntity;
|
||||||
import net.minecraft.fluid.FluidState;
|
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.util.ActionResult;
|
import net.minecraft.util.ActionResult;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
@@ -59,6 +59,9 @@ import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
|||||||
public class TurtleTool extends AbstractTurtleUpgrade {
|
public class TurtleTool extends AbstractTurtleUpgrade {
|
||||||
protected final ItemStack item;
|
protected final ItemStack item;
|
||||||
|
|
||||||
|
private static final int TAG_LIST = 9;
|
||||||
|
private static final int TAG_COMPOUND = 10;
|
||||||
|
|
||||||
public TurtleTool(Identifier id, String adjective, Item item) {
|
public TurtleTool(Identifier id, String adjective, Item item) {
|
||||||
super(id, TurtleUpgradeType.TOOL, adjective, item);
|
super(id, TurtleUpgradeType.TOOL, adjective, item);
|
||||||
this.item = new ItemStack(item);
|
this.item = new ItemStack(item);
|
||||||
@@ -74,6 +77,24 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
|||||||
this.item = toolItem;
|
this.item = toolItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isItemSuitable( @Nonnull ItemStack stack )
|
||||||
|
{
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if( tag == null || tag.isEmpty() ) return true;
|
||||||
|
|
||||||
|
// Check we've not got anything vaguely interesting on the item. We allow other mods to add their
|
||||||
|
// own NBT, with the understanding such details will be lost to the mist of time.
|
||||||
|
if( stack.isDamaged() || stack.hasEnchantments() || stack.hasCustomName() ) return false;
|
||||||
|
if( tag.contains( "AttributeModifiers", TAG_LIST ) &&
|
||||||
|
!tag.getList( "AttributeModifiers", TAG_COMPOUND ).isEmpty() )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public TurtleCommandResult useTool(@Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction) {
|
public TurtleCommandResult useTool(@Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction) {
|
||||||
@@ -183,7 +204,6 @@ public class TurtleTool extends AbstractTurtleUpgrade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BlockState state = world.getBlockState(blockPosition);
|
BlockState state = world.getBlockState(blockPosition);
|
||||||
FluidState fluidState = world.getFluidState(blockPosition);
|
|
||||||
|
|
||||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer(turtle, turtlePosition, direction);
|
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer(turtle, turtlePosition, direction);
|
||||||
turtlePlayer.loadInventory(this.item.copy());
|
turtlePlayer.loadInventory(this.item.copy());
|
||||||
|
@@ -14,7 +14,6 @@ import net.minecraft.block.entity.BlockEntity;
|
|||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.inventory.Inventory;
|
import net.minecraft.inventory.Inventory;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
@@ -28,53 +27,6 @@ public final class InventoryUtil {
|
|||||||
return a == b || (a.getItem() == b.getItem() && ItemStack.areTagsEqual(a, b));
|
return a == b || (a.getItem() == b.getItem() && ItemStack.areTagsEqual(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if two items are "mostly" equivalent. Namely, they have the same item and damage, and identical share stacks.
|
|
||||||
*
|
|
||||||
* sufficient to ensure basic information (such as enchantments) are the same, while not having to worry about capabilities.
|
|
||||||
*
|
|
||||||
* @param a The first stack to check
|
|
||||||
* @param b The second stack to check
|
|
||||||
* @return If these items are largely the same.
|
|
||||||
*/
|
|
||||||
public static boolean areItemsSimilar(@Nonnull ItemStack a, @Nonnull ItemStack b) {
|
|
||||||
if (a == b) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (a.isEmpty()) {
|
|
||||||
return !b.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.getItem() != b.getItem()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
|
||||||
// null one.
|
|
||||||
CompoundTag shareTagA = a.getTag();
|
|
||||||
CompoundTag shareTagB = b.getTag();
|
|
||||||
if (shareTagA == shareTagB) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (shareTagA == null) {
|
|
||||||
return shareTagB.isEmpty();
|
|
||||||
}
|
|
||||||
if (shareTagB == null) {
|
|
||||||
return shareTagA.isEmpty();
|
|
||||||
}
|
|
||||||
return shareTagA.equals(shareTagB);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public static ItemStack copyItem(@Nonnull ItemStack a) {
|
|
||||||
return a.copy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ItemStorage getStorage(World world, BlockPos pos, Direction side) {
|
|
||||||
Inventory inventory = getInventory(world, pos, side);
|
|
||||||
return inventory == null ? null : ItemStorage.wrap(inventory, side);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods for finding inventories:
|
// Methods for finding inventories:
|
||||||
|
|
||||||
public static Inventory getInventory(World world, BlockPos pos, Direction side) {
|
public static Inventory getInventory(World world, BlockPos pos, Direction side) {
|
||||||
|
Reference in New Issue
Block a user