Remove several usages of ComputerFamily

While ComputerFamily is still useful, there's definitely some places
where it adds an extra layer of indirection. This commit attempts to
clean up some places where we no longer need it.

 - Remove ComputerFamily from AbstractComputerBlock. The only place this
   was needed is in TurtleBlock, and that can be replaced with normal
   Minecraft explosion resistence!

 - Pass in the fuel limit to the turtle block entity, rather than
   deriving it from current family.

 - The turtle BERs now derive their model from the turtle's item, rather
   than the turtle's family.

 - When creating upgrade/overlay recipes, use the item's name, rather
   than {pocket,turtle}_family. This means we can drop getFamily() from
   IComputerItem (it is still needed on to handle the UI).

 - We replace IComputerItem.withFamily with a method to change to a
   different item of the same type. ComputerUpgradeRecipe no longer
   takes a family, and instead just uses the result's item.

 - Computer blocks now use the normal Block.asItem() to find their
   corresponding item, rather than looking it up via family.

The above means we can remove all the family-based XyzItem.create(...)
methods, which have always felt a little ugly.

We still need ComputerFamily for a couple of things:
 - Permission checks for command computers.
 - Checks for mouse/colour support in ServerComputer.
 - UI textures.
This commit is contained in:
Jonathan Coates 2023-12-20 14:16:56 +00:00
parent 78bb3da58c
commit 9d8c933a14
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
34 changed files with 134 additions and 254 deletions

View File

@ -11,7 +11,6 @@
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.platform.ClientPlatformHelper;
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import dan200.computercraft.shared.util.Holiday;
import net.minecraft.client.Minecraft;
@ -21,7 +20,6 @@
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
@ -29,8 +27,6 @@
import javax.annotation.Nullable;
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory");
private static final ModelResourceLocation ADVANCED_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced", "inventory");
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
private static final ResourceLocation ELF_OVERLAY_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
@ -42,13 +38,6 @@ public TurtleBlockEntityRenderer(BlockEntityRendererProvider.Context context) {
font = context.getFont();
}
public static ResourceLocation getTurtleModel(ComputerFamily family, boolean coloured) {
return switch (family) {
default -> coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
case ADVANCED -> coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL;
};
}
public static @Nullable ResourceLocation getTurtleOverlayModel(@Nullable ResourceLocation overlay, boolean christmas) {
if (overlay != null) return overlay;
if (christmas) return ELF_OVERLAY_MODEL;
@ -78,7 +67,6 @@ public void render(TurtleBlockEntity turtle, float partialTicks, PoseStack trans
var matrix = transform.last().pose();
var opacity = (int) (mc.options.getBackgroundOpacity(0.25f) * 255) << 24;
var width = -font.width(label) / 2.0f;
// TODO: Check this looks okay
font.drawInBatch(label, width, (float) 0, 0x20ffffff, false, matrix, buffers, Font.DisplayMode.SEE_THROUGH, opacity, lightmapCoord);
font.drawInBatch(label, width, (float) 0, 0xffffffff, false, matrix, buffers, Font.DisplayMode.NORMAL, 0, lightmapCoord);
@ -96,10 +84,18 @@ public void render(TurtleBlockEntity turtle, float partialTicks, PoseStack trans
// Render the turtle
var colour = turtle.getColour();
var family = turtle.getFamily();
var overlay = turtle.getOverlay();
renderModel(transform, buffers, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
if (colour == -1) {
// Render the turtle using its item model.
var modelManager = Minecraft.getInstance().getItemRenderer().getItemModelShaper();
var model = modelManager.getItemModel(turtle.getBlockState().getBlock().asItem());
if (model == null) model = modelManager.getModelManager().getMissingModel();
renderModel(transform, buffers, lightmapCoord, overlayLight, model, null);
} else {
// Otherwise render it using the colour item.
renderModel(transform, buffers, lightmapCoord, overlayLight, COLOUR_TURTLE_MODEL, new int[]{ colour });
}
// Render the overlay
var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);

View File

@ -13,7 +13,6 @@
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.platform.RecipeIngredients;
import dan200.computercraft.shared.platform.RegistryWrappers;
@ -38,7 +37,6 @@
import net.minecraft.world.level.block.Blocks;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.function.Consumer;
@ -106,14 +104,13 @@ private static List<TurtleItem> turtleItems() {
private void turtleUpgrades(Consumer<FinishedRecipe> add) {
for (var turtleItem : turtleItems()) {
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
var name = RegistryWrappers.ITEMS.getKey(turtleItem);
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
var result = turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, result.getItem())
.group(String.format("%s:turtle_%s", ComputerCraftAPI.MOD_ID, nameId))
.group(name.toString())
.pattern("#T")
.define('T', base.getItem())
.define('#', upgrade.getCraftingItem().getItem())
@ -121,9 +118,7 @@ private void turtleUpgrades(Consumer<FinishedRecipe> add) {
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
new ResourceLocation(ComputerCraftAPI.MOD_ID, String.format("turtle_%s/%s/%s",
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
))
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
);
}
}
@ -141,15 +136,13 @@ private static List<PocketComputerItem> pocketComputerItems() {
private void pocketUpgrades(Consumer<FinishedRecipe> add) {
for (var pocket : pocketComputerItems()) {
var base = pocket.create(-1, null, -1, null);
if (base.isEmpty()) continue;
var nameId = pocket.getFamily().name().toLowerCase(Locale.ROOT);
var name = RegistryWrappers.ITEMS.getKey(pocket).withPath(x -> x.replace("pocket_computer_", "pocket_"));
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
var result = pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, result.getItem())
.group(String.format("%s:pocket_%s", ComputerCraftAPI.MOD_ID, nameId))
.group(name.toString())
.pattern("#")
.pattern("P")
.define('P', base.getItem())
@ -158,9 +151,7 @@ private void pocketUpgrades(Consumer<FinishedRecipe> add) {
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
new ResourceLocation(ComputerCraftAPI.MOD_ID, String.format("pocket_%s/%s/%s",
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
))
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
);
}
}
@ -190,12 +181,10 @@ private void turtleOverlays(Consumer<FinishedRecipe> add) {
private void turtleOverlay(Consumer<FinishedRecipe> add, String overlay, Consumer<ShapelessRecipeBuilder> build) {
for (var turtleItem : turtleItems()) {
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
var group = "%s:turtle_%s_overlay".formatted(ComputerCraftAPI.MOD_ID, nameId);
var name = RegistryWrappers.ITEMS.getKey(turtleItem);
var builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.REDSTONE, base.getItem())
.group(group)
.group(name.withSuffix("_overlay").toString())
.unlockedBy("has_turtle", inventoryChange(base.getItem()));
build.accept(builder);
builder
@ -204,7 +193,7 @@ private void turtleOverlay(Consumer<FinishedRecipe> add, String overlay, Consume
RecipeWrapper
.wrap(ModRegistry.RecipeSerializers.TURTLE_OVERLAY.get(), add)
.withExtraData(x -> x.addProperty("overlay", new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay).toString())),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_%s_overlays/%s".formatted(nameId, overlay))
name.withSuffix("_overlays/" + overlay)
);
}
}
@ -253,7 +242,7 @@ private void basicRecipes(Consumer<FinishedRecipe> add) {
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade")
);
@ -277,7 +266,7 @@ private void basicRecipes(Consumer<FinishedRecipe> add) {
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
.define('I', ingredients.woodenChest())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add).withExtraData(family(ComputerFamily.NORMAL)));
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
@ -288,7 +277,7 @@ private void basicRecipes(Consumer<FinishedRecipe> add) {
.define('C', ModRegistry.Items.COMPUTER_ADVANCED.get())
.define('I', ingredients.woodenChest())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)));
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
@ -300,7 +289,7 @@ private void basicRecipes(Consumer<FinishedRecipe> add) {
.define('B', ingredients.goldBlock())
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade")
);
@ -367,7 +356,7 @@ private void basicRecipes(Consumer<FinishedRecipe> add) {
.define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade")
);
@ -519,10 +508,6 @@ private static CompoundTag playerHead(String name, String uuid) {
return tag;
}
private static Consumer<JsonObject> family(ComputerFamily family) {
return json -> json.addProperty("family", family.getSerializedName());
}
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleCraftingRecipeSerializer<?> special) {
SpecialRecipeBuilder.special(special).save(add, RegistryWrappers.RECIPE_SERIALIZERS.getKey(special).toString());
}

View File

@ -79,6 +79,8 @@ public static void blockTags(TagConsumer<Block> tags) {
ModRegistry.Blocks.WIRED_MODEM_FULL.get(),
ModRegistry.Blocks.CABLE.get()
);
tags.tag(BlockTags.WITHER_IMMUNE).add(ModRegistry.Blocks.COMPUTER_COMMAND.get());
}
public static void itemTags(ItemTagConsumer tags) {

View File

@ -34,6 +34,7 @@
import dan200.computercraft.shared.computer.items.CommandComputerItem;
import dan200.computercraft.shared.computer.items.ComputerItem;
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
import dan200.computercraft.shared.data.ConstantLootConditionSerializer;
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
@ -139,19 +140,17 @@ private static BlockBehaviour.Properties modemProperties() {
}
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_NORMAL = REGISTRY.register("computer_normal",
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), BlockEntities.COMPUTER_NORMAL));
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), BlockEntities.COMPUTER_ADVANCED));
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new CommandComputerBlock<>(
computerProperties().strength(-1, 6000000.0F),
ComputerFamily.COMMAND, BlockEntities.COMPUTER_COMMAND
));
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command",
() -> new CommandComputerBlock<>(computerProperties().strength(-1, 6000000.0F), BlockEntities.COMPUTER_COMMAND));
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), BlockEntities.TURTLE_NORMAL));
public static final RegistryEntry<TurtleBlock> TURTLE_ADVANCED = REGISTRY.register("turtle_advanced",
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD).explosionResistance(TurtleBlock.IMMUNE_EXPLOSION_RESISTANCE), BlockEntities.TURTLE_ADVANCED));
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties().mapColor(MapColor.STONE)));
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties().mapColor(MapColor.STONE)));
@ -192,9 +191,9 @@ private static <T extends BlockEntity> RegistryEntry<BlockEntityType<T>> ofBlock
ofBlock(Blocks.COMPUTER_COMMAND, (p, s) -> new CommandComputerBlockEntity(BlockEntities.COMPUTER_COMMAND.get(), p, s));
public static final RegistryEntry<BlockEntityType<TurtleBlockEntity>> TURTLE_NORMAL =
ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, ComputerFamily.NORMAL));
ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, () -> Config.turtleFuelLimit, ComputerFamily.NORMAL));
public static final RegistryEntry<BlockEntityType<TurtleBlockEntity>> TURTLE_ADVANCED =
ofBlock(Blocks.TURTLE_ADVANCED, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_ADVANCED.get(), p, s, ComputerFamily.ADVANCED));
ofBlock(Blocks.TURTLE_ADVANCED, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_ADVANCED.get(), p, s, () -> Config.advancedTurtleFuelLimit, ComputerFamily.ADVANCED));
public static final RegistryEntry<BlockEntityType<SpeakerBlockEntity>> SPEAKER =
ofBlock(Blocks.SPEAKER, (p, s) -> new SpeakerBlockEntity(BlockEntities.SPEAKER.get(), p, s));
@ -311,7 +310,10 @@ public static class Menus {
() -> new MenuType<>(PrinterMenu::new, FeatureFlags.VANILLA_SET));
public static final RegistryEntry<MenuType<HeldItemMenu>> PRINTOUT = REGISTRY.register("printout",
() -> ContainerData.toType(HeldItemContainerData::new, HeldItemMenu::createPrintout));
() -> ContainerData.toType(
HeldItemContainerData::new,
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.getHand())
));
public static final RegistryEntry<MenuType<ViewComputerMenu>> VIEW_COMPUTER = REGISTRY.register("view_computer",
() -> ContainerData.toType(ComputerContainerData::new, ViewComputerMenu::new));
@ -371,11 +373,11 @@ private static <T extends CustomRecipe> RegistryEntry<SimpleCraftingRecipeSerial
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
public static final RegistryEntry<RecipeSerializer<TurtleRecipe>> TURTLE = REGISTRY.register("turtle", () -> TurtleRecipe.validatingSerialiser(TurtleRecipe::of));
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
public static final RegistryEntry<RecipeSerializer<TurtleOverlayRecipe>> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serializer::new);
public static final RegistryEntry<RecipeSerializer<TurtleOverlayRecipe>> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serialiser::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
public static final RegistryEntry<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", ComputerUpgradeRecipe.Serializer::new);
public static final RegistryEntry<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", () -> CustomShapedRecipe.validatingSerialiser(ComputerUpgradeRecipe::of));
}
public static class Permissions {
@ -464,8 +466,8 @@ public static void register() {
* Register any objects which must be done on the main thread.
*/
public static void registerMainThread() {
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
}
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {

View File

@ -4,8 +4,6 @@
package dan200.computercraft.shared.common;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.network.container.HeldItemContainerData;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.MenuProvider;
@ -28,10 +26,6 @@ public HeldItemMenu(MenuType<? extends HeldItemMenu> type, int id, Player player
stack = player.getItemInHand(hand).copy();
}
public static HeldItemMenu createPrintout(int id, Inventory inventory, HeldItemContainerData data) {
return new HeldItemMenu(ModRegistry.Menus.PRINTOUT.get(), id, inventory.player, data.getHand());
}
public ItemStack getStack() {
return stack;
}

View File

@ -7,7 +7,6 @@
import dan200.computercraft.annotations.ForgeOverride;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.items.IComputerItem;
import dan200.computercraft.shared.platform.RegistryEntry;
import dan200.computercraft.shared.util.BlockEntityHelpers;
@ -42,13 +41,11 @@
public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntity> extends HorizontalDirectionalBlock implements IBundledRedstoneBlock, EntityBlock {
private static final ResourceLocation DROP = new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer");
private final ComputerFamily family;
protected final RegistryEntry<BlockEntityType<T>> type;
private final BlockEntityTicker<T> serverTicker = (level, pos, state, computer) -> computer.serverTick();
protected AbstractComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
protected AbstractComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
super(settings);
this.family = family;
this.type = type;
}
@ -82,10 +79,6 @@ public int getDirectSignal(BlockState state, BlockGetter world, BlockPos pos, Di
protected abstract ItemStack getItem(AbstractComputerBlockEntity tile);
public ComputerFamily getFamily() {
return family;
}
@Override
@Deprecated
public int getSignal(BlockState state, BlockGetter world, BlockPos pos, Direction incomingSide) {

View File

@ -4,7 +4,6 @@
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.platform.RegistryEntry;
import net.minecraft.world.level.block.GameMasterBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
@ -17,7 +16,7 @@
* @see dan200.computercraft.shared.computer.items.CommandComputerItem
*/
public class CommandComputerBlock<T extends CommandComputerBlockEntity> extends ComputerBlock<T> implements GameMasterBlock {
public CommandComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
super(settings, family, type);
public CommandComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
super(settings, type);
}
}

View File

@ -4,9 +4,8 @@
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.items.ComputerItemFactory;
import dan200.computercraft.shared.computer.items.ComputerItem;
import dan200.computercraft.shared.platform.RegistryEntry;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
@ -25,8 +24,8 @@ public class ComputerBlock<T extends ComputerBlockEntity> extends AbstractComput
public static final EnumProperty<ComputerState> STATE = EnumProperty.create("state", ComputerState.class);
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public ComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
super(settings, family, type);
public ComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
super(settings, type);
registerDefaultState(defaultBlockState()
.setValue(FACING, Direction.NORTH)
.setValue(STATE, ComputerState.OFF)
@ -46,6 +45,9 @@ public BlockState getStateForPlacement(BlockPlaceContext placement) {
@Override
protected ItemStack getItem(AbstractComputerBlockEntity tile) {
return tile instanceof ComputerBlockEntity ? ComputerItemFactory.create((ComputerBlockEntity) tile) : ItemStack.EMPTY;
if (!(tile instanceof ComputerBlockEntity computer)) return ItemStack.EMPTY;
if (!(asItem() instanceof ComputerItem item)) return ItemStack.EMPTY;
return item.create(computer.getComputerID(), computer.getLabel());
}
}

View File

@ -32,11 +32,9 @@ public ComputerBlockEntity(BlockEntityType<? extends ComputerBlockEntity> type,
@Override
protected ServerComputer createComputer(int id) {
var family = getFamily();
return new ServerComputer(
(ServerLevel) getLevel(), getBlockPos(), id, label,
family, Config.computerTermWidth,
Config.computerTermHeight
getFamily(), Config.computerTermWidth, Config.computerTermHeight
);
}

View File

@ -4,33 +4,8 @@
package dan200.computercraft.shared.computer.core;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import net.minecraft.util.GsonHelper;
import net.minecraft.util.StringRepresentable;
public enum ComputerFamily implements StringRepresentable {
NORMAL("normal"),
ADVANCED("advanced"),
COMMAND("command");
private final String name;
ComputerFamily(String name) {
this.name = name;
}
public static ComputerFamily getFamily(JsonObject json, String name) {
var familyName = GsonHelper.getAsString(json, name);
for (var family : values()) {
if (family.getSerializedName().equalsIgnoreCase(familyName)) return family;
}
throw new JsonSyntaxException("Unknown computer family '" + familyName + "' for field " + name);
}
@Override
public String getSerializedName() {
return name;
}
public enum ComputerFamily {
NORMAL,
ADVANCED,
COMMAND,
}

View File

@ -8,7 +8,6 @@
import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.config.Config;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
@ -22,11 +21,8 @@
import java.util.List;
public abstract class AbstractComputerItem extends BlockItem implements IComputerItem, IMedia {
private final ComputerFamily family;
public AbstractComputerItem(AbstractComputerBlock<?> block, Properties settings) {
super(block, settings);
family = block.getFamily();
}
@Override
@ -45,13 +41,6 @@ public void appendHoverText(ItemStack stack, @Nullable Level world, List<Compone
return IComputerItem.super.getLabel(stack);
}
@Override
public final ComputerFamily getFamily() {
return family;
}
// IMedia implementation
@Override
public boolean setLabel(ItemStack stack, @Nullable String label) {
if (label != null) {

View File

@ -5,8 +5,8 @@
package dan200.computercraft.shared.computer.items;
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nullable;
@ -24,9 +24,9 @@ public ItemStack create(int id, @Nullable String label) {
}
@Override
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
var result = ComputerItemFactory.create(getComputerID(stack), null, family);
if (stack.hasCustomHoverName()) result.setHoverName(stack.getHoverName());
return result;
public ItemStack changeItem(ItemStack stack, Item newItem) {
return newItem instanceof ComputerItem computer
? computer.create(getComputerID(stack), getLabel(stack))
: ItemStack.EMPTY;
}
}

View File

@ -1,29 +0,0 @@
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
//
// SPDX-License-Identifier: LicenseRef-CCPL
package dan200.computercraft.shared.computer.items;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nullable;
public final class ComputerItemFactory {
private ComputerItemFactory() {
}
public static ItemStack create(ComputerBlockEntity tile) {
return create(tile.getComputerID(), tile.getLabel(), tile.getFamily());
}
public static ItemStack create(int id, @Nullable String label, ComputerFamily family) {
return switch (family) {
case NORMAL -> ModRegistry.Items.COMPUTER_NORMAL.get().create(id, label);
case ADVANCED -> ModRegistry.Items.COMPUTER_ADVANCED.get().create(id, label);
case COMMAND -> ModRegistry.Items.COMPUTER_COMMAND.get().create(id, label);
};
}
}

View File

@ -4,7 +4,7 @@
package dan200.computercraft.shared.computer.items;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nullable;
@ -21,7 +21,15 @@ default int getComputerID(ItemStack stack) {
return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null;
}
ComputerFamily getFamily();
ItemStack withFamily(ItemStack stack, ComputerFamily family);
/**
* Create a new stack, changing the underlying item.
* <p>
* This should copy the computer's data to a different item of the same type (for instance, converting a normal
* computer to an advanced one).
*
* @param stack The current computer stack.
* @param newItem The new item.
* @return The new stack, possibly {@linkplain ItemStack#EMPTY empty} if {@code newItem} is of the same type.
*/
ItemStack changeItem(ItemStack stack, Item newItem);
}

View File

@ -4,57 +4,44 @@
package dan200.computercraft.shared.computer.recipe;
import com.google.gson.JsonObject;
import com.mojang.serialization.DataResult;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.items.IComputerItem;
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeSerializer;
/**
* A recipe which "upgrades" a {@linkplain IComputerItem computer}, converting it from one {@linkplain ComputerFamily
* family} to another.
* A recipe which "upgrades" a {@linkplain IComputerItem computer}, converting to it a new item (for instance a normal
* turtle to an advanced one).
*
* @see IComputerItem#changeItem(ItemStack, Item)
*/
public final class ComputerUpgradeRecipe extends ComputerConvertRecipe {
private final ComputerFamily family;
private final Item result;
private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe, ComputerFamily family) {
private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe) {
super(identifier, recipe);
this.family = family;
this.result = recipe.result().getItem();
}
public static DataResult<ComputerUpgradeRecipe> of(ResourceLocation id, ShapedRecipeSpec recipe) {
if (!(recipe.result().getItem() instanceof IComputerItem)) {
return DataResult.error(() -> recipe.result().getItem() + " is not a computer item");
}
return DataResult.success(new ComputerUpgradeRecipe(id, recipe));
}
@Override
protected ItemStack convert(IComputerItem item, ItemStack stack) {
return item.withFamily(stack, family);
return item.changeItem(stack, result);
}
@Override
public RecipeSerializer<ComputerUpgradeRecipe> getSerializer() {
return ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get();
}
public static class Serializer implements RecipeSerializer<ComputerUpgradeRecipe> {
@Override
public ComputerUpgradeRecipe fromJson(ResourceLocation identifier, JsonObject json) {
var recipe = ShapedRecipeSpec.fromJson(json);
var family = ComputerFamily.getFamily(json, "family");
return new ComputerUpgradeRecipe(identifier, recipe, family);
}
@Override
public ComputerUpgradeRecipe fromNetwork(ResourceLocation identifier, FriendlyByteBuf buf) {
var recipe = ShapedRecipeSpec.fromNetwork(buf);
var family = buf.readEnum(ComputerFamily.class);
return new ComputerUpgradeRecipe(identifier, recipe, family);
}
@Override
public void toNetwork(FriendlyByteBuf buf, ComputerUpgradeRecipe recipe) {
recipe.toSpec().toNetwork(buf);
buf.writeEnum(recipe.family);
}
}
}

View File

@ -12,7 +12,6 @@
import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerContext;
@ -60,14 +59,6 @@ public PocketComputerItem(Properties settings, ComputerFamily family) {
this.family = family;
}
public static ItemStack create(int id, @Nullable String label, int colour, ComputerFamily family, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
return switch (family) {
case NORMAL -> ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().create(id, label, colour, upgrade);
case ADVANCED -> ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().create(id, label, colour, upgrade);
default -> ItemStack.EMPTY;
};
}
public ItemStack create(int id, @Nullable String label, int colour, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
var result = new ItemStack(this);
if (id >= 0) result.getOrCreateTag().putInt(NBT_ID, id);
@ -243,17 +234,16 @@ private static void setComputerID(ItemStack stack, int computerID) {
return IComputerItem.super.getLabel(stack);
}
@Override
public ComputerFamily getFamily() {
return family;
}
@Override
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
return create(
public ItemStack changeItem(ItemStack stack, Item newItem) {
return newItem instanceof PocketComputerItem pocket ? pocket.create(
getComputerID(stack), getLabel(stack), getColour(stack),
family, getUpgradeWithData(stack)
);
getUpgradeWithData(stack)
) : ItemStack.EMPTY;
}
// IMedia

View File

@ -81,7 +81,6 @@ public ItemStack assemble(CraftingContainer inventory, RegistryAccess registryAc
if (upgrade == null) return ItemStack.EMPTY;
// Construct the new stack
var family = itemComputer.getFamily();
var computerID = itemComputer.getComputerID(computer);
var label = itemComputer.getLabel(computer);
var colour = itemComputer.getColour(computer);

View File

@ -10,7 +10,6 @@
import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlockEntity;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.platform.RegistryEntry;
import dan200.computercraft.shared.turtle.core.TurtleBrain;
import dan200.computercraft.shared.turtle.items.TurtleItem;
@ -52,6 +51,16 @@
public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implements SimpleWaterloggedBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
/**
* The explosion resistance to use when a turtle is "immune" to explosions.
* <p>
* This is used as the default explosion resistance for advanced turtles, and the resistance for entity-based
* explosions (e.g. creepers).
*
* @see #getExplosionResistance(BlockState, BlockGetter, BlockPos, Explosion)
*/
public static final float IMMUNE_EXPLOSION_RESISTANCE = 2000f;
private static final VoxelShape DEFAULT_SHAPE = Shapes.box(
0.125, 0.125, 0.125,
0.875, 0.875, 0.875
@ -59,8 +68,8 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
private final BlockEntityTicker<TurtleBlockEntity> clientTicker = (level, pos, state, computer) -> computer.clientTick();
public TurtleBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<TurtleBlockEntity>> type) {
super(settings, family, type);
public TurtleBlock(Properties settings, RegistryEntry<BlockEntityType<TurtleBlockEntity>> type) {
super(settings, type);
registerDefaultState(getStateDefinition().any()
.setValue(FACING, Direction.NORTH)
.setValue(WATERLOGGED, false)
@ -149,20 +158,21 @@ public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable L
@ForgeOverride
public float getExplosionResistance(BlockState state, BlockGetter world, BlockPos pos, Explosion explosion) {
var exploder = explosion.getDirectSourceEntity();
if (getFamily() == ComputerFamily.ADVANCED || exploder instanceof LivingEntity || exploder instanceof AbstractHurtingProjectile) {
return 2000;
if (exploder instanceof LivingEntity || exploder instanceof AbstractHurtingProjectile) {
return IMMUNE_EXPLOSION_RESISTANCE;
}
return explosionResistance;
return getExplosionResistance();
}
@Override
protected ItemStack getItem(AbstractComputerBlockEntity tile) {
if (!(tile instanceof TurtleBlockEntity turtle)) return ItemStack.EMPTY;
if (!(asItem() instanceof TurtleItem item)) return ItemStack.EMPTY;
var access = turtle.getAccess();
return TurtleItem.create(
turtle.getComputerID(), turtle.getLabel(), access.getColour(), turtle.getFamily(),
return item.create(
turtle.getComputerID(), turtle.getLabel(), access.getColour(),
withPersistedData(access.getUpgradeWithData(TurtleSide.LEFT)),
withPersistedData(access.getUpgradeWithData(TurtleSide.RIGHT)),
access.getFuelLevel(), turtle.getOverlay()

View File

@ -38,6 +38,7 @@
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.function.IntSupplier;
public class TurtleBlockEntity extends AbstractComputerBlockEntity implements BasicContainer {
public static final int INVENTORY_SIZE = 16;
@ -53,13 +54,17 @@ enum MoveState {
private final NonNullList<ItemStack> inventory = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY);
private final NonNullList<ItemStack> inventorySnapshot = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY);
private boolean inventoryChanged = false;
private final IntSupplier fuelLimit;
private TurtleBrain brain = new TurtleBrain(this);
private MoveState moveState = MoveState.NOT_MOVED;
private @Nullable IPeripheral peripheral;
private @Nullable Runnable onMoved;
public TurtleBlockEntity(BlockEntityType<? extends TurtleBlockEntity> type, BlockPos pos, BlockState state, ComputerFamily family) {
public TurtleBlockEntity(BlockEntityType<? extends TurtleBlockEntity> type, BlockPos pos, BlockState state, IntSupplier fuelLimit, ComputerFamily family) {
super(type, pos, state, family);
this.fuelLimit = fuelLimit;
}
boolean hasMoved() {
@ -172,8 +177,6 @@ protected boolean isPeripheralBlockedOnSide(ComputerSide localSide) {
return hasPeripheralUpgradeOnSide(localSide);
}
// IDirectionalTile
@Override
public Direction getDirection() {
return getBlockState().getValue(TurtleBlock.FACING);
@ -272,6 +275,10 @@ public void loadClient(CompoundTag nbt) {
// Privates
public int getFuelLimit() {
return fuelLimit.getAsInt();
}
private boolean hasPeripheralUpgradeOnSide(ComputerSide side) {
ITurtleUpgrade upgrade;
switch (side) {

View File

@ -395,11 +395,7 @@ public void setFuelLevel(int level) {
@Override
public int getFuelLimit() {
if (owner.getFamily() == ComputerFamily.ADVANCED) {
return Config.advancedTurtleFuelLimit;
} else {
return Config.turtleFuelLimit;
}
return owner.getFuelLimit();
}
@Override

View File

@ -10,9 +10,7 @@
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.impl.TurtleUpgrades;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
import dan200.computercraft.shared.util.NBTUtil;
@ -20,6 +18,7 @@
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.LayeredCauldronBlock;
@ -32,20 +31,6 @@ public TurtleItem(TurtleBlock block, Properties settings) {
super(block, settings);
}
public static ItemStack create(
int id, @Nullable String label, int colour, ComputerFamily family,
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade, @Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
int fuelLevel, @Nullable ResourceLocation overlay
) {
return switch (family) {
case NORMAL ->
ModRegistry.Items.TURTLE_NORMAL.get().create(id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay);
case ADVANCED ->
ModRegistry.Items.TURTLE_ADVANCED.get().create(id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay);
default -> ItemStack.EMPTY;
};
}
public ItemStack create(
int id, @Nullable String label, int colour,
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade, @Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
@ -119,13 +104,13 @@ public String getCreatorModId(ItemStack stack) {
}
@Override
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
return create(
public ItemStack changeItem(ItemStack stack, Item newItem) {
return newItem instanceof TurtleItem turtle ? turtle.create(
getComputerID(stack), getLabel(stack),
getColour(stack), family,
getColour(stack),
getUpgradeWithData(stack, TurtleSide.LEFT), getUpgradeWithData(stack, TurtleSide.RIGHT),
getFuelLevel(stack), getOverlay(stack)
);
) : ItemStack.EMPTY;
}
public @Nullable ITurtleUpgrade getUpgrade(ItemStack stack, TurtleSide side) {

View File

@ -58,7 +58,7 @@ public RecipeSerializer<TurtleOverlayRecipe> getSerializer() {
return ModRegistry.RecipeSerializers.TURTLE_OVERLAY.get();
}
public static class Serializer implements RecipeSerializer<TurtleOverlayRecipe> {
public static class Serialiser implements RecipeSerializer<TurtleOverlayRecipe> {
@Override
public TurtleOverlayRecipe fromJson(ResourceLocation id, JsonObject json) {
var recipe = ShapelessRecipeSpec.fromJson(json);

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:computer_upgrade",
"category": "redstone",
"family": "advanced",
"key": {"#": {"tag": "c:gold_ingots"}, "C": {"item": "computercraft:computer_normal"}},
"pattern": ["###", "#C#", "# #"],
"result": {"item": "computercraft:computer_advanced"},

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:computer_upgrade",
"category": "redstone",
"family": "advanced",
"key": {"#": {"tag": "c:gold_ingots"}, "C": {"item": "computercraft:pocket_computer_normal"}},
"pattern": ["###", "#C#", "# #"],
"result": {"item": "computercraft:pocket_computer_advanced"},

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:turtle",
"category": "redstone",
"family": "advanced",
"key": {
"#": {"tag": "c:gold_ingots"},
"C": {"item": "computercraft:computer_advanced"},

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:computer_upgrade",
"category": "redstone",
"family": "advanced",
"key": {
"#": {"tag": "c:gold_ingots"},
"B": {"item": "minecraft:gold_block"},

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:turtle",
"category": "redstone",
"family": "normal",
"key": {
"#": {"tag": "c:iron_ingots"},
"C": {"item": "computercraft:computer_normal"},

View File

@ -0,0 +1 @@
{"replace": false, "values": ["computercraft:computer_command"]}

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:computer_upgrade",
"category": "redstone",
"family": "advanced",
"key": {"#": {"tag": "forge:ingots/gold"}, "C": {"item": "computercraft:computer_normal"}},
"pattern": ["###", "#C#", "# #"],
"result": {"item": "computercraft:computer_advanced"},

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:computer_upgrade",
"category": "redstone",
"family": "advanced",
"key": {"#": {"tag": "forge:ingots/gold"}, "C": {"item": "computercraft:pocket_computer_normal"}},
"pattern": ["###", "#C#", "# #"],
"result": {"item": "computercraft:pocket_computer_advanced"},

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:turtle",
"category": "redstone",
"family": "advanced",
"key": {
"#": {"tag": "forge:ingots/gold"},
"C": {"item": "computercraft:computer_advanced"},

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:computer_upgrade",
"category": "redstone",
"family": "advanced",
"key": {
"#": {"tag": "forge:ingots/gold"},
"B": {"tag": "forge:storage_blocks/gold"},

View File

@ -1,7 +1,6 @@
{
"type": "computercraft:turtle",
"category": "redstone",
"family": "normal",
"key": {
"#": {"tag": "forge:ingots/iron"},
"C": {"item": "computercraft:computer_normal"},

View File

@ -0,0 +1 @@
{"values": ["computercraft:computer_command"]}