Added block operations module for breaking and placing blocks.

This commit is contained in:
Florian Nücke
2021-01-18 17:26:32 +01:00
parent 17644647a4
commit 2a055646ee
19 changed files with 386 additions and 29 deletions

View File

@@ -1,13 +1,18 @@
package li.cil.oc2.common;
import li.cil.oc2.api.API;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.common.ToolType;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import org.apache.commons.lang3.tuple.Pair;
import java.util.UUID;
@Mod.EventBusSubscriber(modid = API.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
public final class Config {
private static final CommonSettings COMMON_INSTANCE;
@@ -20,6 +25,10 @@ public final class Config {
public static int maxHardDriveSize = 8 * Constants.MEGABYTE;
public static int maxFlashMemorySize = 4 * Constants.KILOBYTE;
public static int blockOperationsModuleToolLevel = Items.DIAMOND_PICKAXE.getHarvestLevel(new ItemStack(Items.DIAMOND_PICKAXE), ToolType.PICKAXE, null, null);
public static UUID fakePlayerUUID = UUID.fromString("e39dd9a7-514f-4a2d-aa5e-b6030621416d");
///////////////////////////////////////////////////////////////////
static {
@@ -43,6 +52,8 @@ public final class Config {
maxMemorySize = COMMON_INSTANCE.maxMemorySize.get();
maxHardDriveSize = COMMON_INSTANCE.maxHardDriveSize.get();
maxFlashMemorySize = COMMON_INSTANCE.maxFlashMemorySize.get();
fakePlayerUUID = UUID.fromString(COMMON_INSTANCE.fakePlayerUUID.get());
}
}
@@ -54,6 +65,10 @@ public final class Config {
public ForgeConfigSpec.IntValue maxHardDriveSize;
public ForgeConfigSpec.IntValue maxFlashMemorySize;
public ForgeConfigSpec.IntValue blockOperationsModuleToolLevel;
public ForgeConfigSpec.ConfigValue<String> fakePlayerUUID;
public CommonSettings(final ForgeConfigSpec.Builder builder) {
builder.push("vm");
@@ -78,6 +93,24 @@ public final class Config {
.defineInRange("maxFlashMemorySize", Config.maxFlashMemorySize, 0, 128 * Constants.MEGABYTE);
builder.pop();
builder.push("gameplay");
blockOperationsModuleToolLevel = builder
.translation(Constants.CONFIG_BLOCK_OPERATIONS_MODULE_TOOL_LEVEL)
.comment("Tool level the block operations module operates at.")
.defineInRange("modules.block_operations.toolLevel", Config.blockOperationsModuleToolLevel, 0, Integer.MAX_VALUE);
builder.pop();
builder.push("admin");
fakePlayerUUID = builder
.translation(Constants.CONFIG_FAKE_PLAYER_UUID)
.comment("The UUID used for the ForgeFakePlayer used by robots.")
.define("fakePlayerUUID", Config.fakePlayerUUID.toString());
builder.pop();
}
}
}

View File

@@ -39,7 +39,8 @@ public final class Constants {
public static final String REDSTONE_INTERFACE_CARD_ITEM_NAME = "redstone_interface_card";
public static final String NETWORK_INTERFACE_CARD_ITEM_NAME = "network_interface_card";
public static final String INVENTORY_AUTOMATION_MODULE_ITEM_NAME = "inventory_automation_module";
public static final String INVENTORY_OPERATIONS_MODULE_ITEM_NAME = "inventory_operations_module";
public static final String BLOCK_OPERATIONS_MODULE_ITEM_NAME = "block_operations_module";
///////////////////////////////////////////////////////////////////
@@ -51,10 +52,12 @@ public final class Constants {
///////////////////////////////////////////////////////////////////
public static final String CONFIG_MAX_ALLOCATED_MEMORY = "config.oc2.maxAllocatedMemory";
public static final String CONFIG_MAX_MEMORY_SIZE = "config.oc2.maxMemorySize";
public static final String CONFIG_MAX_HARD_DRIVE_SIZE = "config.oc2.maxHardDriveSize";
public static final String CONFIG_MAX_FLASH_MEMORY_SIZE = "config.oc2.maxFlashMemorySize";
public static final String CONFIG_MAX_ALLOCATED_MEMORY = "config.oc2.vm.maxAllocatedMemory";
public static final String CONFIG_MAX_MEMORY_SIZE = "config.oc2.vm.maxMemorySize";
public static final String CONFIG_MAX_HARD_DRIVE_SIZE = "config.oc2.vm.maxHardDriveSize";
public static final String CONFIG_MAX_FLASH_MEMORY_SIZE = "config.oc2.vm.maxFlashMemorySize";
public static final String CONFIG_BLOCK_OPERATIONS_MODULE_TOOL_LEVEL = "config.oc2.modules.block_operations.toolLevel";
public static final String CONFIG_FAKE_PLAYER_UUID = "config.oc2.admin.fakePlayerUUID";
///////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,212 @@
package li.cil.oc2.common.bus.device.item;
import li.cil.oc2.api.bus.device.ItemDevice;
import li.cil.oc2.api.bus.device.object.Callback;
import li.cil.oc2.api.bus.device.object.ObjectDevice;
import li.cil.oc2.api.bus.device.object.Parameter;
import li.cil.oc2.api.bus.device.rpc.RPCDevice;
import li.cil.oc2.api.bus.device.rpc.RPCMethod;
import li.cil.oc2.api.capabilities.Robot;
import li.cil.oc2.common.Config;
import li.cil.oc2.common.bus.device.util.IdentityProxy;
import li.cil.oc2.common.util.FakePlayerUtils;
import net.minecraft.block.*;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.GameType;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemStackHandler;
import javax.annotation.Nullable;
import java.util.List;
public final class BlockOperationsModuleDevice extends IdentityProxy<ItemStack> implements RPCDevice, ItemDevice {
public enum PlacementDirection {
FRONT,
UP,
DOWN,
}
private final Entity entity;
private final Robot robot;
private final ObjectDevice device;
///////////////////////////////////////////////////////////////////
public BlockOperationsModuleDevice(final ItemStack identity, final Entity entity, final Robot robot) {
super(identity);
this.entity = entity;
this.robot = robot;
this.device = new ObjectDevice(this, "block_operations");
}
///////////////////////////////////////////////////////////////////
@Override
public List<String> getTypeNames() {
return device.getTypeNames();
}
@Override
public List<RPCMethod> getMethods() {
return device.getMethods();
}
@Callback
public boolean excavate(@Parameter("direction") @Nullable final PlacementDirection direction) {
final World world = entity.getEntityWorld();
if (!(world instanceof ServerWorld)) {
return false;
}
final int selectedSlot = robot.getSelectedSlot(); // Get once to avoid change due to threading.
final ItemStackHandler inventory = robot.getInventory();
final List<ItemEntity> oldItems = getItemsInRange();
if (!tryHarvestBlock(world, entity.getPosition().offset(getAdjustedDirection(direction)))) {
return false;
}
final List<ItemEntity> droppedItems = getItemsInRange();
droppedItems.removeAll(oldItems);
for (final ItemEntity itemEntity : droppedItems) {
ItemStack stack = itemEntity.getItem();
stack = insertStartingAt(inventory, stack, selectedSlot, false);
itemEntity.setItem(stack);
}
return true;
}
@Callback
public boolean place(@Parameter("direction") @Nullable final PlacementDirection direction) {
final World world = entity.getEntityWorld();
if (!(world instanceof ServerWorld)) {
return false;
}
final int selectedSlot = robot.getSelectedSlot(); // Get once to avoid change due to threading.
final ItemStackHandler inventory = robot.getInventory();
final ItemStack extracted = inventory.extractItem(selectedSlot, 1, true);
if (extracted.isEmpty() || !(extracted.getItem() instanceof BlockItem)) {
return false;
}
final BlockPos blockPos = entity.getPosition().offset(getAdjustedDirection(direction));
final Direction side = getAdjustedDirection(direction).getOpposite();
final BlockRayTraceResult hit = new BlockRayTraceResult(
Vector3d.copyCentered(blockPos).add(Vector3d.copy(side.getDirectionVec()).scale(0.5)),
side,
blockPos,
false);
final ItemStack itemStack = extracted.copy();
final BlockItem blockItem = (BlockItem) itemStack.getItem();
final ServerPlayerEntity player = FakePlayerUtils.getFakePlayer((ServerWorld) world, entity);
final BlockItemUseContext context = new BlockItemUseContext(player, Hand.MAIN_HAND, itemStack, hit);
final ActionResultType result = blockItem.tryPlace(context);
if (!result.isSuccessOrConsume()) {
return false;
}
if (itemStack.isEmpty()) {
inventory.extractItem(selectedSlot, 1, false);
}
return true;
}
///////////////////////////////////////////////////////////////////
private Direction getAdjustedDirection(@Nullable final PlacementDirection placementDirection) {
if (placementDirection == PlacementDirection.UP) {
return Direction.UP;
} else if (placementDirection == PlacementDirection.DOWN) {
return Direction.DOWN;
} else {
Direction direction = Direction.SOUTH;
final int horizontalIndex = entity.getHorizontalFacing().getHorizontalIndex();
for (int i = 0; i < horizontalIndex; i++) {
direction = direction.rotateY();
}
return direction;
}
}
private List<ItemEntity> getItemsInRange() {
return entity.getEntityWorld().getEntitiesWithinAABB(ItemEntity.class, entity.getBoundingBox().grow(2));
}
private boolean tryHarvestBlock(final World world, final BlockPos blockPos) {
// This method is based on PlayerInteractionManager::tryHarvestBlock. Simplified for our needs.
final BlockState blockState = world.getBlockState(blockPos);
if (blockState.isAir(world, blockPos)) {
return false;
}
final ServerPlayerEntity player = FakePlayerUtils.getFakePlayer((ServerWorld) world, entity);
final int experience = net.minecraftforge.common.ForgeHooks.onBlockBreakEvent(world, GameType.NOT_SET, player, blockPos);
if (experience == -1) {
return false;
}
final TileEntity tileEntity = world.getTileEntity(blockPos);
final Block block = blockState.getBlock();
final boolean isCommandBlock = block instanceof CommandBlockBlock || block instanceof StructureBlock || block instanceof JigsawBlock;
if (isCommandBlock && !player.canUseCommandBlock()) {
return false;
}
if (player.blockActionRestricted(world, blockPos, GameType.NOT_SET)) {
return false;
}
final boolean canHarvest = Config.blockOperationsModuleToolLevel >= blockState.getHarvestLevel();
if (!canHarvest) {
return false;
}
if (!ForgeEventFactory.doPlayerHarvestCheck(player, blockState, canHarvest)) {
return false;
}
if (!blockState.removedByPlayer(world, blockPos, player, true, world.getFluidState(blockPos))) {
return false;
}
block.onPlayerDestroy(world, blockPos, blockState);
block.harvestBlock(world, player, blockPos, blockState, tileEntity, ItemStack.EMPTY);
return true;
}
private ItemStack insertStartingAt(final IItemHandler handler, ItemStack stack, final int startSlot, final boolean simulate) {
for (int i = 0; i < handler.getSlots(); i++) {
final int slot = (startSlot + i) % handler.getSlots();
stack = handler.insertItem(slot, stack, simulate);
if (stack.isEmpty()) {
return ItemStack.EMPTY;
}
}
return stack;
}
}

View File

@@ -29,18 +29,18 @@ import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class InventoryAutomationRobotModuleDevice extends IdentityProxy<ItemStack> implements RPCDevice, ItemDevice {
public final class InventoryOperationsModuleDevice extends IdentityProxy<ItemStack> implements RPCDevice, ItemDevice {
private final Entity entity;
private final Robot robot;
private final ObjectDevice device;
///////////////////////////////////////////////////////////////////
public InventoryAutomationRobotModuleDevice(final ItemStack identity, final Entity entity, final Robot robot) {
public InventoryOperationsModuleDevice(final ItemStack identity, final Entity entity, final Robot robot) {
super(identity);
this.entity = entity;
this.robot = robot;
this.device = new ObjectDevice(this, "inventory_automation");
this.device = new ObjectDevice(this, "inventory_operations");
}
///////////////////////////////////////////////////////////////////

View File

@@ -3,6 +3,7 @@ package li.cil.oc2.common.bus.device.provider;
import li.cil.oc2.api.API;
import li.cil.oc2.api.bus.device.provider.BlockDeviceProvider;
import li.cil.oc2.api.bus.device.provider.ItemDeviceProvider;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.bus.device.provider.block.*;
import li.cil.oc2.common.bus.device.provider.item.*;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
@@ -31,13 +32,14 @@ public final class Providers {
BLOCK_DEVICE_PROVIDERS.register("fluid_handler", FluidHandlerBlockDeviceProvider::new);
BLOCK_DEVICE_PROVIDERS.register("item_handler", ItemHandlerBlockDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("item_memory", MemoryItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("item_hard_drive", HardDriveItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("item_flash_memory", FlashMemoryItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("item_redstone_interface_card", RedstoneInterfaceCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("item_network_interface_card", NetworkInterfaceCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register(Constants.MEMORY_ITEM_NAME, MemoryItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register(Constants.HARD_DRIVE_ITEM_NAME, HardDriveItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register(Constants.FLASH_MEMORY_ITEM_NAME, FlashMemoryItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register(Constants.REDSTONE_INTERFACE_CARD_ITEM_NAME, RedstoneInterfaceCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register(Constants.NETWORK_INTERFACE_CARD_ITEM_NAME, NetworkInterfaceCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("inventory_automation_module", InventoryAutomationRobotModuleDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register(Constants.INVENTORY_OPERATIONS_MODULE_ITEM_NAME, InventoryOperationsModuleDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register(Constants.BLOCK_OPERATIONS_MODULE_ITEM_NAME, BlockOperationsModuleDeviceProvider::new);
BLOCK_DEVICE_PROVIDERS.register(FMLJavaModLoadingContext.get().getModEventBus());
ITEM_DEVICE_PROVIDERS.register(FMLJavaModLoadingContext.get().getModEventBus());

View File

@@ -4,16 +4,16 @@ import li.cil.oc2.api.bus.device.DeviceType;
import li.cil.oc2.api.bus.device.DeviceTypes;
import li.cil.oc2.api.bus.device.ItemDevice;
import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
import li.cil.oc2.common.bus.device.item.InventoryAutomationRobotModuleDevice;
import li.cil.oc2.common.bus.device.item.BlockOperationsModuleDevice;
import li.cil.oc2.common.bus.device.provider.util.AbstractItemDeviceProvider;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.common.item.Items;
import java.util.Optional;
public final class InventoryAutomationRobotModuleDeviceProvider extends AbstractItemDeviceProvider {
public InventoryAutomationRobotModuleDeviceProvider() {
super(Items.INVENTORY_AUTOMATION_MODULE);
public final class BlockOperationsModuleDeviceProvider extends AbstractItemDeviceProvider {
public BlockOperationsModuleDeviceProvider() {
super(Items.BLOCK_OPERATIONS_MODULE);
}
///////////////////////////////////////////////////////////////////
@@ -27,6 +27,6 @@ public final class InventoryAutomationRobotModuleDeviceProvider extends Abstract
protected Optional<ItemDevice> getItemDevice(final ItemDeviceQuery query) {
return query.getContainerEntity().flatMap(entity ->
entity.getCapability(Capabilities.ROBOT).map(robot ->
new InventoryAutomationRobotModuleDevice(query.getItemStack(), entity, robot)));
new BlockOperationsModuleDevice(query.getItemStack(), entity, robot)));
}
}

View File

@@ -0,0 +1,32 @@
package li.cil.oc2.common.bus.device.provider.item;
import li.cil.oc2.api.bus.device.DeviceType;
import li.cil.oc2.api.bus.device.DeviceTypes;
import li.cil.oc2.api.bus.device.ItemDevice;
import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
import li.cil.oc2.common.bus.device.item.InventoryOperationsModuleDevice;
import li.cil.oc2.common.bus.device.provider.util.AbstractItemDeviceProvider;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.common.item.Items;
import java.util.Optional;
public final class InventoryOperationsModuleDeviceProvider extends AbstractItemDeviceProvider {
public InventoryOperationsModuleDeviceProvider() {
super(Items.INVENTORY_OPERATIONS_MODULE);
}
///////////////////////////////////////////////////////////////////
@Override
protected Optional<DeviceType> getItemDeviceType(final ItemDeviceQuery query) {
return Optional.of(DeviceTypes.ROBOT_MODULE);
}
@Override
protected Optional<ItemDevice> getItemDevice(final ItemDeviceQuery query) {
return query.getContainerEntity().flatMap(entity ->
entity.getCapability(Capabilities.ROBOT).map(robot ->
new InventoryOperationsModuleDevice(query.getItemStack(), entity, robot)));
}
}

View File

@@ -39,7 +39,8 @@ public final class Items {
public static final RegistryObject<Item> REDSTONE_INTERFACE_CARD_ITEM = register(Constants.REDSTONE_INTERFACE_CARD_ITEM_NAME);
public static final RegistryObject<Item> NETWORK_INTERFACE_CARD_ITEM = register(Constants.NETWORK_INTERFACE_CARD_ITEM_NAME);
public static final RegistryObject<Item> INVENTORY_AUTOMATION_MODULE = register(Constants.INVENTORY_AUTOMATION_MODULE_ITEM_NAME);
public static final RegistryObject<Item> INVENTORY_OPERATIONS_MODULE = register(Constants.INVENTORY_OPERATIONS_MODULE_ITEM_NAME);
public static final RegistryObject<Item> BLOCK_OPERATIONS_MODULE = register(Constants.BLOCK_OPERATIONS_MODULE_ITEM_NAME);
///////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,57 @@
package li.cil.oc2.common.util;
import com.mojang.authlib.GameProfile;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import li.cil.oc2.api.API;
import li.cil.oc2.common.Config;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.IPacket;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.PacketDirection;
import net.minecraft.network.play.ServerPlayNetHandler;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.FakePlayerFactory;
import org.jetbrains.annotations.Nullable;
public final class FakePlayerUtils {
private static final String FAKE_PLAYER_NAME = "[" + API.MOD_ID + "]";
///////////////////////////////////////////////////////////////////
public static ServerPlayerEntity getFakePlayer(final ServerWorld world, final Entity entity) {
final ServerPlayerEntity player = getFakePlayer(world);
player.copyLocationAndAnglesFrom(entity);
player.prevRotationPitch = player.rotationPitch;
player.prevRotationYaw = player.rotationYaw;
player.rotationYawHead = player.rotationYaw;
player.prevRotationYawHead = player.rotationYawHead;
return player;
}
public static ServerPlayerEntity getFakePlayer(final ServerWorld world) {
final FakePlayer player = FakePlayerFactory.get(world, new GameProfile(Config.fakePlayerUUID, FAKE_PLAYER_NAME));
// We need to give our fake player a fake network handler because some events we want
// to use the fake player with will unconditionally access this field.
if (player.connection == null) {
player.connection = new FakeServerPlayNetHandler(player);
}
return player;
}
///////////////////////////////////////////////////////////////////
private static class FakeServerPlayNetHandler extends ServerPlayNetHandler {
public FakeServerPlayNetHandler(final FakePlayer fakePlayer) {
super(fakePlayer.server, new NetworkManager(PacketDirection.CLIENTBOUND), fakePlayer);
}
@Override
public void sendPacket(final IPacket<?> packetIn, @Nullable final GenericFutureListener<? extends Future<? super Void>> futureListeners) {
}
}
}

View File

@@ -2,6 +2,8 @@ package li.cil.oc2.common.util;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
@@ -11,6 +13,7 @@ import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.IWorld;
import javax.annotation.Nullable;
import java.util.List;
import java.util.function.Function;
public final class WorldUtils {

View File

@@ -45,7 +45,8 @@ public final class ModItemModelProvider extends ItemModelProvider {
simple(Items.REDSTONE_INTERFACE_CARD_ITEM, "items/redstone_interface_card");
simple(Items.NETWORK_INTERFACE_CARD_ITEM, "items/network_interface_card");
simple(Items.INVENTORY_AUTOMATION_MODULE, "items/inventory_automation_module");
simple(Items.INVENTORY_OPERATIONS_MODULE, "items/inventory_operations_module");
simple(Items.BLOCK_OPERATIONS_MODULE, "items/block_operations_module");
withExistingParent(Constants.ROBOT_ENTITY_NAME, "template_shulker_box");
}

View File

@@ -42,7 +42,8 @@ public final class ModItemTagsProvider extends ItemTagsProvider {
Items.NETWORK_INTERFACE_CARD_ITEM.get()
);
getOrCreateBuilder(DEVICES_ROBOT_MODULE).add(
Items.INVENTORY_AUTOMATION_MODULE.get()
Items.INVENTORY_OPERATIONS_MODULE.get(),
Items.BLOCK_OPERATIONS_MODULE.get()
);
getOrCreateBuilder(WRENCHES).add(Items.WRENCH_ITEM.get());
}

View File

@@ -21,15 +21,20 @@
"item.oc2.network_interface_card": "Network Interface Card",
"item.oc2.robot": "Robot",
"item.oc2.robot.desc": "Use a Scrench or compatible wrench to add and remove components.",
"item.oc2.inventory_automation_module": "Inventory Automation Module",
"item.oc2.inventory_operations_module": "Inventory Operations Module",
"item.oc2.inventory_operations_module.desc": "Enables robots to move items in their inventory, including to and from adjacent inventories.",
"item.oc2.block_operations_module": "Block Operations Module",
"item.oc2.block_operations_module.desc": "Enables robots to break and place blocks.",
"entity.oc2.robot": "Robot",
"config.oc2.maxAllocatedMemory": "Maximum allocated memory",
"config.oc2.maxMemorySize:": "Maximum memory device size",
"config.oc2.maxHardDriveSize": "Maximum hard drive device size",
"config.oc2.maxFlashMemorySize:": "Maximum flash memory device size",
"config.oc2.vm.maxAllocatedMemory": "Maximum allocated memory",
"config.oc2.vm.maxMemorySize:": "Maximum memory device size",
"config.oc2.vm.maxHardDriveSize": "Maximum hard drive device size",
"config.oc2.vm.maxFlashMemorySize:": "Maximum flash memory device size",
"config.oc2.computerEnergyPerTick": "Energy/tick used by computers",
"config.oc2.modules.block_operations.toolLevel": "Block operations module tool level",
"config.oc2.admin.fakePlayerUUID": "Fake Player UUID",
"gui.oc2.computer.boot_error.unknown": "Unknown Error",
"gui.oc2.computer.boot_error.no_memory": "Insufficient Memory",

View File

@@ -1,6 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "oc2:items/inventory_automation_module"
"layer0": "oc2:items/block_operations_module"
}
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "oc2:items/inventory_operations_module"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,6 +1,7 @@
{
"replace": false,
"values": [
"oc2:inventory_automation_module"
"oc2:inventory_operations_module",
"oc2:block_operations_module"
]
}