diff --git a/src/main/java/li/cil/oc2/Constants.java b/src/main/java/li/cil/oc2/Constants.java index f8a27789..ef3d9aae 100644 --- a/src/main/java/li/cil/oc2/Constants.java +++ b/src/main/java/li/cil/oc2/Constants.java @@ -29,6 +29,10 @@ public final class Constants { /////////////////////////////////////////////////////////////////// + public static final String COMPUTER_SCREEN_CAPTURE_INPUT_CAPTION = "gui.oc2.computer.capture_input.capt"; + public static final String COMPUTER_SCREEN_CAPTURE_INPUT_DESCRIPTION = "gui.oc2.computer.capture_input.desc"; + public static final String COMPUTER_SCREEN_POWER_CAPTION = "gui.oc2.computer.power.capt"; + public static final String COMPUTER_SCREEN_POWER_DESCRIPTION = "gui.oc2.computer.power.desc"; public static final String COMPUTER_BOOT_ERROR_UNKNOWN = "gui.oc2.computer.boot_error.unknown"; public static final String COMPUTER_BOOT_ERROR_NO_MEMORY = "gui.oc2.computer.boot_error.no_memory"; public static final String COMPUTER_BUS_STATE_INCOMPLETE = "gui.oc2.computer.bus_state.incomplete"; diff --git a/src/main/java/li/cil/oc2/client/gui/TerminalScreen.java b/src/main/java/li/cil/oc2/client/gui/TerminalScreen.java index 97ab39ba..d1acd5de 100644 --- a/src/main/java/li/cil/oc2/client/gui/TerminalScreen.java +++ b/src/main/java/li/cil/oc2/client/gui/TerminalScreen.java @@ -2,15 +2,21 @@ package li.cil.oc2.client.gui; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.systems.RenderSystem; +import li.cil.oc2.Constants; import li.cil.oc2.api.API; import li.cil.oc2.client.gui.terminal.TerminalInput; +import li.cil.oc2.client.gui.widget.Sprite; +import li.cil.oc2.client.gui.widget.ToggleImageButton; import li.cil.oc2.common.block.entity.ComputerTileEntity; import li.cil.oc2.common.network.Network; +import li.cil.oc2.common.network.message.ComputerPowerMessage; import li.cil.oc2.common.network.message.TerminalBlockInputMessage; import li.cil.oc2.common.vm.Terminal; +import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.client.gui.screen.Screen; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TranslationTextComponent; import org.lwjgl.glfw.GLFW; import java.nio.ByteBuffer; @@ -21,13 +27,24 @@ public final class TerminalScreen extends Screen { private static final ResourceLocation BACKGROUND = new ResourceLocation(API.MOD_ID, "textures/gui/screen/terminal.png"); private static final ResourceLocation BACKGROUND_TERMINAL_FOCUSED = new ResourceLocation(API.MOD_ID, "textures/gui/screen/terminal_focused.png"); private static final int TEXTURE_SIZE = 512; - private static final int SCREEN_WIDTH = 8 + 80 * 8 / 2 + 8; - private static final int SCREEN_HEIGHT = 8 + 24 * 16 / 2 + 8; + + private static final Sprite CAPTURE_INPUT_BASE = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 15, 241); + private static final Sprite CAPTURE_INPUT_PRESSED = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 29, 241); + private static final Sprite CAPTURE_INPUT_ACTIVE = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 1, 241); + + private static final Sprite POWER_BASE = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 15, 255); + private static final Sprite POWER_PRESSED = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 29, 255); + private static final Sprite POWER_ACTIVE = new Sprite(BACKGROUND, TEXTURE_SIZE, 12, 12, 1, 255); + + private static final int SCREEN_WIDTH = 336; + private static final int SCREEN_HEIGHT = 228; private static final int TERMINAL_AREA_X = 8; private static final int TERMINAL_AREA_Y = 8; private static final int TERMINAL_AREA_WIDTH = 80 * 8 / 2; private static final int TERMINAL_AREA_HEIGHT = 24 * 16 / 2; + private static boolean enableInputCapture; + /////////////////////////////////////////////////////////////////// private final ComputerTileEntity tileEntity; @@ -55,7 +72,7 @@ public final class TerminalScreen extends Screen { requireNonNull(minecraft).getTextureManager().bindTexture(BACKGROUND); blit(matrixStack, windowLeft, windowTop, 0, 0, windowWidth, windowHeight, TEXTURE_SIZE, TEXTURE_SIZE); - if (isMouseOverTerminal && tileEntity.isRunning()) { + if (shouldCaptureInput()) { requireNonNull(minecraft).getTextureManager().bindTexture(BACKGROUND_TERMINAL_FOCUSED); blit(matrixStack, windowLeft, windowTop, 0, 0, windowWidth, windowHeight, TEXTURE_SIZE, TEXTURE_SIZE); } @@ -67,6 +84,18 @@ public final class TerminalScreen extends Screen { stack.translate(windowLeft + TERMINAL_AREA_X, windowTop + TERMINAL_AREA_Y, this.itemRenderer.zLevel); stack.scale(TERMINAL_AREA_WIDTH / (float) terminal.getWidth(), TERMINAL_AREA_HEIGHT / (float) terminal.getHeight(), 1f); terminal.render(stack); + } else { + final ITextComponent bootError = tileEntity.getBootError(); + if (bootError != null) { + final int textWidth = font.getStringPropertyWidth(bootError); + final int textOffsetX = (TERMINAL_AREA_WIDTH - textWidth) / 2; + final int textOffsetY = (TERMINAL_AREA_HEIGHT - font.FONT_HEIGHT) / 2; + font.func_243246_a(matrixStack, + bootError, + windowLeft + TERMINAL_AREA_X + textOffsetX, + windowTop + TERMINAL_AREA_Y + textOffsetY, + 0xEE3322); + } } } @@ -79,7 +108,10 @@ public final class TerminalScreen extends Screen { Network.INSTANCE.sendToServer(new TerminalBlockInputMessage(tileEntity, input)); } - if (!tileEntity.isRunning()) { + assert minecraft != null; + final ClientPlayerEntity player = minecraft.player; + assert player != null; + if (!player.isAlive() || !tileEntity.getPos().withinDistance(player.getPositionVec(), 8)) { closeScreen(); } } @@ -97,7 +129,7 @@ public final class TerminalScreen extends Screen { @Override public boolean keyPressed(final int keyCode, final int scanCode, final int modifiers) { - if ((!isMouseOverTerminal || !tileEntity.isRunning()) && keyCode == GLFW.GLFW_KEY_ESCAPE) { + if (!shouldCaptureInput() && keyCode == GLFW.GLFW_KEY_ESCAPE) { return super.keyPressed(keyCode, scanCode, modifiers); } @@ -135,6 +167,53 @@ public final class TerminalScreen extends Screen { this.windowTop = (this.height - this.windowHeight) / 2; requireNonNull(minecraft).keyboardListener.enableRepeatEvents(true); + + addButton(new ToggleImageButton( + this, windowLeft + 104, windowTop + 212, 12, 12, + new TranslationTextComponent(Constants.COMPUTER_SCREEN_CAPTURE_INPUT_CAPTION), + new TranslationTextComponent(Constants.COMPUTER_SCREEN_CAPTURE_INPUT_DESCRIPTION), + CAPTURE_INPUT_BASE, + CAPTURE_INPUT_PRESSED, + CAPTURE_INPUT_ACTIVE + ) { + @Override + public void onPress() { + super.onPress(); + enableInputCapture = !enableInputCapture; + } + + @Override + public boolean isToggled() { + return enableInputCapture; + } + }); + + addButton(new ToggleImageButton( + this, windowLeft + 220, windowTop + 212, 12, 12, + new TranslationTextComponent(Constants.COMPUTER_SCREEN_POWER_CAPTION), + new TranslationTextComponent(Constants.COMPUTER_SCREEN_POWER_DESCRIPTION), + POWER_BASE, + POWER_PRESSED, + POWER_ACTIVE + ) { + @Override + public void onPress() { + super.onPress(); + final ComputerPowerMessage message = new ComputerPowerMessage(tileEntity, !tileEntity.isRunning()); + Network.INSTANCE.sendToServer(message); + } + + @Override + public boolean isToggled() { + return tileEntity.isRunning(); + } + }); + } + + /////////////////////////////////////////////////////////////////// + + private boolean shouldCaptureInput() { + return isMouseOverTerminal && enableInputCapture && tileEntity.isRunning(); } private boolean isPointInRegion(final int x, final int y, final int width, final int height, double mouseX, double mouseY) { diff --git a/src/main/java/li/cil/oc2/client/gui/widget/Sprite.java b/src/main/java/li/cil/oc2/client/gui/widget/Sprite.java new file mode 100644 index 00000000..be5e672a --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/widget/Sprite.java @@ -0,0 +1,27 @@ +package li.cil.oc2.client.gui.widget; + +import com.mojang.blaze3d.matrix.MatrixStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.AbstractGui; +import net.minecraft.util.ResourceLocation; + +public final class Sprite extends AbstractGui { + public final ResourceLocation image; + public final int textureSize; + public final int width, height; + public final int u0, v0; + + public Sprite(final ResourceLocation atlas, final int textureSize, final int width, final int height, final int u0, final int v0) { + this.image = atlas; + this.textureSize = textureSize; + this.width = width; + this.height = height; + this.u0 = u0; + this.v0 = v0; + } + + public void draw(final MatrixStack stack, final int x, final int y) { + Minecraft.getInstance().getTextureManager().bindTexture(image); + blit(stack, x, y, u0, v0, width, height, textureSize, textureSize); + } +} diff --git a/src/main/java/li/cil/oc2/client/gui/widget/ToggleImageButton.java b/src/main/java/li/cil/oc2/client/gui/widget/ToggleImageButton.java new file mode 100644 index 00000000..edeeb81e --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/widget/ToggleImageButton.java @@ -0,0 +1,89 @@ +package li.cil.oc2.client.gui.widget; + +import com.mojang.blaze3d.matrix.MatrixStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.button.AbstractButton; +import net.minecraft.util.text.Color; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.client.gui.GuiUtils; + +import java.util.Arrays; +import java.util.List; + +public abstract class ToggleImageButton extends AbstractButton { + private static final long PRESS_DURATION = 200; + private static final long TOOLTIP_DELAY = 250; + + private final Screen parent; + private final List tooltip; + private final Sprite baseImage; + private final Sprite pressedImage; + private final Sprite activeImage; + private boolean isToggled; + private long lastPressedAt; + private long hoveringStartedAt; + + public ToggleImageButton( + final Screen parent, + final int x, final int y, + final int width, final int height, + final ITextComponent caption, + final ITextComponent description, + final Sprite baseImage, + final Sprite pressedImage, + final Sprite activeImage) { + super(x, y, width, height, caption); + this.parent = parent; + this.tooltip = Arrays.asList(caption, new StringTextComponent("").modifyStyle(style -> style.setColor(Color.fromTextFormatting(TextFormatting.GRAY))).append(description)); + this.baseImage = baseImage; + this.pressedImage = pressedImage; + this.activeImage = activeImage; + } + + @Override + public void onPress() { + lastPressedAt = System.currentTimeMillis(); + } + + public boolean isToggled() { + return isToggled; + } + + public void setToggled(final boolean value) { + isToggled = value; + } + + @Override + public void renderButton(final MatrixStack stack, final int mouseX, final int mouseY, final float partialTicks) { + Sprite background = baseImage; + if ((System.currentTimeMillis() - lastPressedAt) < PRESS_DURATION) { + background = pressedImage; + } + + background.draw(stack, x, y); + + if (isToggled()) { + activeImage.draw(stack, x, y); + } + + if (isHovered()) { + if (hoveringStartedAt == 0) { + hoveringStartedAt = System.currentTimeMillis(); + } + + if ((System.currentTimeMillis() - hoveringStartedAt) > TOOLTIP_DELAY) { + renderToolTip(stack, mouseX, mouseY); + } + } else { + hoveringStartedAt = 0; + } + } + + @Override + public void renderToolTip(final MatrixStack stack, final int mouseX, final int mouseY) { + GuiUtils.drawHoveringText(stack, tooltip, mouseX, mouseY, parent.width, parent.height, 200, Minecraft.getInstance().fontRenderer); + } +} diff --git a/src/main/java/li/cil/oc2/client/gui/widget/package-info.java b/src/main/java/li/cil/oc2/client/gui/widget/package-info.java new file mode 100644 index 00000000..f94de5ad --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/widget/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package li.cil.oc2.client.gui.widget; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java b/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java index 31612e1f..be2c709c 100644 --- a/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java +++ b/src/main/java/li/cil/oc2/client/render/tile/ComputerTileEntityRenderer.java @@ -3,7 +3,6 @@ package li.cil.oc2.client.render.tile; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.vertex.IVertexBuilder; -import li.cil.oc2.Constants; import li.cil.oc2.api.API; import li.cil.oc2.client.render.OpenComputersRenderType; import li.cil.oc2.common.block.ComputerBlock; @@ -24,7 +23,6 @@ import net.minecraft.util.math.vector.Vector3f; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.ITextProperties; import net.minecraft.util.text.Style; -import net.minecraft.util.text.TranslationTextComponent; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -174,32 +172,15 @@ public final class ComputerTileEntityRenderer extends TileEntityRenderer void sendToClientsTrackingChunk(final T message, final Chunk chunk) { diff --git a/src/main/java/li/cil/oc2/common/network/message/ComputerBootErrorMessage.java b/src/main/java/li/cil/oc2/common/network/message/ComputerBootErrorMessage.java index 19583659..82169c37 100644 --- a/src/main/java/li/cil/oc2/common/network/message/ComputerBootErrorMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/ComputerBootErrorMessage.java @@ -9,7 +9,7 @@ import net.minecraftforge.fml.network.NetworkEvent; import java.util.function.Supplier; -public class ComputerBootErrorMessage { +public final class ComputerBootErrorMessage { private BlockPos pos; private ITextComponent value; diff --git a/src/main/java/li/cil/oc2/common/network/message/ComputerBusStateMessage.java b/src/main/java/li/cil/oc2/common/network/message/ComputerBusStateMessage.java index 54e0aa14..3a0abfda 100644 --- a/src/main/java/li/cil/oc2/common/network/message/ComputerBusStateMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/ComputerBusStateMessage.java @@ -9,7 +9,7 @@ import net.minecraftforge.fml.network.NetworkEvent; import java.util.function.Supplier; -public class ComputerBusStateMessage { +public final class ComputerBusStateMessage { private BlockPos pos; private AbstractDeviceBusController.BusState busState; diff --git a/src/main/java/li/cil/oc2/common/network/message/ComputerPowerMessage.java b/src/main/java/li/cil/oc2/common/network/message/ComputerPowerMessage.java new file mode 100644 index 00000000..7ad816f8 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/ComputerPowerMessage.java @@ -0,0 +1,53 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.block.entity.ComputerTileEntity; +import li.cil.oc2.common.network.MessageUtils; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.math.BlockPos; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.function.Supplier; + +public final class ComputerPowerMessage { + private BlockPos pos; + private boolean power; + + /////////////////////////////////////////////////////////////////// + + public ComputerPowerMessage(final ComputerTileEntity computer, final boolean power) { + this.pos = computer.getPos(); + this.power = power; + } + + public ComputerPowerMessage(final PacketBuffer buffer) { + fromBytes(buffer); + } + + /////////////////////////////////////////////////////////////////// + + public static boolean handleMessage(final ComputerPowerMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withServerTileEntityAt(context, message.pos, ComputerTileEntity.class, + (computer) -> { + final ServerPlayerEntity player = context.get().getSender(); + if (player != null && computer.getPos().withinDistance(player.getPositionVec(), 8)) { + if (message.power) { + computer.start(); + } else { + computer.stop(); + } + } + })); + return true; + } + + public void fromBytes(final PacketBuffer buffer) { + pos = buffer.readBlockPos(); + power = buffer.readBoolean(); + } + + public static void toBytes(final ComputerPowerMessage message, final PacketBuffer buffer) { + buffer.writeBlockPos(message.pos); + buffer.writeBoolean(message.power); + } +} diff --git a/src/main/java/li/cil/oc2/common/network/message/ComputerRunStateMessage.java b/src/main/java/li/cil/oc2/common/network/message/ComputerRunStateMessage.java index 47fe6129..d456e49f 100644 --- a/src/main/java/li/cil/oc2/common/network/message/ComputerRunStateMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/ComputerRunStateMessage.java @@ -8,7 +8,7 @@ import net.minecraftforge.fml.network.NetworkEvent; import java.util.function.Supplier; -public class ComputerRunStateMessage { +public final class ComputerRunStateMessage { private BlockPos pos; private ComputerTileEntity.RunState runState; diff --git a/src/main/resources/assets/oc2/lang/en_us.json b/src/main/resources/assets/oc2/lang/en_us.json index d3753886..dad5c0dc 100644 --- a/src/main/resources/assets/oc2/lang/en_us.json +++ b/src/main/resources/assets/oc2/lang/en_us.json @@ -19,6 +19,11 @@ "gui.oc2.computer.bus_state.too_complex": "Bus Too Complex", "gui.oc2.computer.bus_state.multiple_controllers": "Multiple Bus Controllers", + "gui.oc2.computer.capture_input.capt": "Capture Input", + "gui.oc2.computer.capture_input.desc": "When enabled, as long as the mouse cursor is hovering the terminal contents, all input will be captured, including the escape key.", + "gui.oc2.computer.power.capt": "Toggle Power", + "gui.oc2.computer.power.desc": "Toggles the power state of the computer.", + "gui.oc2.device_type.eeprom": "EEPROM", "gui.oc2.device_type.memory": "Memory", "gui.oc2.device_type.hard_drive": "Hard Drive", diff --git a/src/main/resources/assets/oc2/textures/gui/screen/terminal.png b/src/main/resources/assets/oc2/textures/gui/screen/terminal.png index ee714648..2a0c6560 100644 Binary files a/src/main/resources/assets/oc2/textures/gui/screen/terminal.png and b/src/main/resources/assets/oc2/textures/gui/screen/terminal.png differ