package org.bonsai.dev; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; public class GameConsole extends GameComponent { private BufferedImage buffer; private BufferedImage[] font; private int maxLines = 128; private String[] lines = new String[maxLines]; private String inputString = ""; private List<String> history = new ArrayList<String>(); private int historyPointer = -1; private String savedInput = ""; private int lineMax = 0; private int height = 0; private int width = 0; private boolean changed = true; private int scroll = 0; private int scrollOffset = 0; private int scrollHeight = 0; private int scrollOffsetOld = 0; private int scrollHeightOld = 0; private boolean autoScroll = true; private boolean submit = false; private boolean cursor = false; private boolean moveCursor = false; private int cursorPos = 0; private float transparency = 0.75f; private boolean initiated = false; public GameConsole(final Game game) { super(game); size(false); game.timer.add("consoleCursor", 250); game.timer.add("consoleRepeatLeft", 35); game.timer.add("consoleRepeatStartLeft", 500); game.timer.add("consoleRepeatRight", 35); game.timer.add("consoleRepeatStartRight", 500); } private final void size(final boolean max) { if (max) { this.height = game.height(); } else { this.height = game.height() / 4; } this.width = game.width() - 8; } public void print(String text) { if (lineMax < maxLines) { lines[lineMax] = text; lineMax++; } else { for (int i = 0; i < lineMax - 1; i++) { lines[i] = lines[i + 1]; } lines[lineMax - 1] = text; } if (autoScroll) { scrollOffset = 0; } changed = true; } public final void control() { // Cursor if (!game.input.keyDown(java.awt.event.KeyEvent.VK_LEFT, true)) { game.timer.set("consoleRepeatStartLeft"); } if (game.input.keyPressed(java.awt.event.KeyEvent.VK_LEFT, true) || (game.input.keyDown(java.awt.event.KeyEvent.VK_LEFT, true) && game.timer.expired("consoleRepeatStartLeft") && game.timer.expired("consoleRepeatLeft"))) { cursorPos -= 1; if (cursorPos < 0) { cursorPos = 0; } moveCursor = true; changed = true; game.timer.set("consoleRepeatLeft"); } if (!game.input.keyDown(java.awt.event.KeyEvent.VK_RIGHT, true)) { game.timer.set("consoleRepeatStartRight"); } if (game.input.keyPressed(java.awt.event.KeyEvent.VK_RIGHT, true) || (game.input.keyDown(java.awt.event.KeyEvent.VK_RIGHT, true) && game.timer.expired("consoleRepeatStartRight") && game.timer.expired("consoleRepeatRight"))) { cursorPos += 1; if (cursorPos > inputString.length()) { cursorPos = inputString.length(); } moveCursor = true; changed = true; game.timer.set("consoleRepeatRight"); } if (!game.input.keyDown(java.awt.event.KeyEvent.VK_LEFT, true) && !game.input.keyDown(java.awt.event.KeyEvent.VK_RIGHT, true)) { moveCursor = false; } // History if (game.input.keyPressed(java.awt.event.KeyEvent.VK_UP, true)) { if (historyPointer == -1) { savedInput = inputString; } if (historyPointer < history.size() - 1) { historyPointer += 1; } inputString = history.get(history.size() - 1 - historyPointer); cursorPos = inputString.length(); changed = true; } if (game.input.keyPressed(java.awt.event.KeyEvent.VK_DOWN, true)) { if (historyPointer == -1) { savedInput = inputString; } if (historyPointer > -1) { historyPointer -= 1; } if (historyPointer == -1) { inputString = savedInput; cursorPos = inputString.length(); } else { inputString = history.get(history.size() - 1 - historyPointer); cursorPos = inputString.length(); } changed = true; } // Scrolling if (game.input.keyDown(java.awt.event.KeyEvent.VK_PAGE_UP, true)) { scrollOffset += 12; changed = true; } if (game.input.keyDown(java.awt.event.KeyEvent.VK_PAGE_DOWN, true)) { scrollOffset -= 12; changed = true; } if (scrollOffset < 0) { scrollOffset = 0; } if (scrollOffset > scrollHeight - height) { scrollOffset = scrollHeight - height; } if (submit == true) { String out = inputString.trim(); if (!out.equals("")) { history.add(out); historyPointer = -1; print(">>" + out); onSubmit(out); inputString = ""; cursorPos = 0; } submit = false; } if (game.timer.expired("consoleCursor")) { cursor = !cursor; game.timer.set("consoleCursor"); } } public final void onSubmit(String input) { if (input.equals("clear") || input.equals("cls")) { scrollOffset = 0; lineMax = 0; lines = new String[maxLines]; scrollHeight = 0; scroll = 0; } else if (input.equals("pause")) { game.pause(!game.paused); } else if (input.equals("full")) { size(true); } else { game.onConsole(input); } } public final void draw(Graphics2D g, int x, int y) { if (!initiated) { font = game.image.gets("/images/console.png", 32, 4); buffer = game.image.create(game.width(), height, false); initiated = true; } // Draw Graphics2D bg = (Graphics2D) buffer.getGraphics(); String drawInputString = inputString + (cursorPos == inputString.length() ? " " : ""); int inputHeight = textHeight(drawInputString) + 2; if (changed || scrollOffsetOld != scrollOffset || scrollHeightOld != scrollHeight) { scrollOffsetOld = scrollOffset; scrollHeightOld = scrollHeight; bg.setColor(Color.BLACK); bg.fillRect(0, 0, width, height); // Cache int oy = 0; for (int i = 0; i < lineMax; i++) { // if (i < lineMax) { oy += drawText(bg, 0, oy - scroll + scrollOffset, lines[i], false); if (oy - scroll > height - inputHeight) { int dec = (oy - scroll) - (height - inputHeight); scroll += dec; oy -= dec; if (!autoScroll && scrollOffset != 0) { scrollOffset += dec; } } // } } // Scrollbars scrollHeight = scroll + height; double windowHeight = height / (scrollHeight + 0.0); bg.setColor(Color.DARK_GRAY); bg.fillRect(width, 0, 8, height); bg.setColor(Color.WHITE); bg.fillRect( width, (int) ((height - height * windowHeight) - (scrollOffset * windowHeight)), 8, (int) (height * windowHeight)); changed = false; } // Input bg.setColor(Color.BLACK); bg.fillRect(0, height - inputHeight + 2, width, inputHeight + 2); bg.setColor(Color.WHITE); bg.fillRect(0, height - inputHeight, width, 2); drawText(bg, 0, height - inputHeight + 2, drawInputString, true); // fade Composite tmp = g.getComposite(); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparency)); g.drawImage(buffer, x, y, null); g.setComposite(tmp); } private final int textHeight(final String text) { final String empty = text.replace("\n", "").replace("\n", ""); final int lineheight = ((empty.length() - 1) / (width / 8)) * 12 + 12; final int lines = text.length() - empty.length(); return lineheight + lines * 12; } private final int drawText(final Graphics2D g, final int x, final int y, final String text, final boolean drawCursor) { final int length = text.length(); final int lineheight = textHeight(text); if (y < height && y + height > -12) { int ox = 0; int oy = 0; for (int i = 0; i < length; i++) { int c = (int) text.charAt(i); if (c < 128) { if (c == 13 || c == 10) { oy += 12; ox = 0; } else if (y + oy >= -12) { if (c == 9) { ox += 4; } else { g.drawImage(font[c], x + ox * 8, y + oy, null); if (drawCursor && i == cursorPos && (moveCursor || cursor)) { g.drawImage(font[8], x + ox * 8, y + oy, null); } } } ox += 1; if (ox * 8 >= width) { oy += 12; ox = 0; } } } } return lineheight; } public final synchronized void onKey(final int key) { if (key == 8) { if (!inputString.equals("")) { inputString = inputString.substring(0, cursorPos - 1) + inputString.substring(cursorPos); cursorPos -= 1; } } else if (key == 10) { if (!inputString.equals("")) { submit = true; } } else { inputString = inputString.substring(0, cursorPos) + (char) key + inputString.substring(cursorPos); cursorPos += 1; } cursor = true; changed = true; } }