diff --git a/src/main/java/gregtech/api/gui/Widget.java b/src/main/java/gregtech/api/gui/Widget.java index ee31218cb03..d95afce09fd 100644 --- a/src/main/java/gregtech/api/gui/Widget.java +++ b/src/main/java/gregtech/api/gui/Widget.java @@ -143,7 +143,7 @@ protected void onSizeUpdate() { } public boolean isMouseOverElement(int mouseX, int mouseY, boolean correctPositionOnMouseWheelMoveEvent) { - mouseX = correctPositionOnMouseWheelMoveEvent ? mouseX + this.gui.getGuiLeft(): mouseX; + mouseX = correctPositionOnMouseWheelMoveEvent ? mouseX + this.gui.getGuiLeft() : mouseX; return isMouseOverElement(mouseX, mouseY); } @@ -474,7 +474,7 @@ public static void drawSector(float x, float y, float r, int color, int segments } tessellator.draw(); GlStateManager.enableTexture2D(); - GlStateManager.color(1,1,1,1); + GlStateManager.color(1, 1, 1, 1); } public static void drawTorus(float x, float y, float outer, float inner, int color, int segments, int from, int to) { @@ -486,13 +486,13 @@ public static void drawTorus(float x, float y, float outer, float inner, int col setColor(color); bufferbuilder.begin(GL11.GL_QUAD_STRIP, DefaultVertexFormats.POSITION); for (int i = from; i <= to; i++) { - float angle = ( i / (float)segments ) * 3.14159f * 2.0f; - bufferbuilder.pos( x + inner * Math.cos(-angle), y + inner * Math.sin(-angle), 0).endVertex(); - bufferbuilder.pos( x + outer * Math.cos(-angle), y + outer * Math.sin(-angle), 0).endVertex(); + float angle = (i / (float) segments) * 3.14159f * 2.0f; + bufferbuilder.pos(x + inner * Math.cos(-angle), y + inner * Math.sin(-angle), 0).endVertex(); + bufferbuilder.pos(x + outer * Math.cos(-angle), y + outer * Math.sin(-angle), 0).endVertex(); } tessellator.draw(); GlStateManager.enableTexture2D(); - GlStateManager.color(1,1,1,1); + GlStateManager.color(1, 1, 1, 1); } @SideOnly(Side.CLIENT) @@ -559,7 +559,7 @@ public static List genBezierPoints(Vec2f from, Vec2f to, boolean horizont c1 = new Vec2f(from.x, (from.y + to.y) / 2); c2 = new Vec2f(to.x, (from.y + to.y) / 2); } - Vec2f[] controlPoint = new Vec2f[]{from,c1,c2,to}; + Vec2f[] controlPoint = new Vec2f[]{from, c1, c2, to}; int n = controlPoint.length - 1; int i, r; List bezierPoints = new ArrayList<>(); diff --git a/src/main/java/gregtech/api/gui/widgets/SimpleTextWidget.java b/src/main/java/gregtech/api/gui/widgets/SimpleTextWidget.java index 49435bb121d..f6ca61c1188 100644 --- a/src/main/java/gregtech/api/gui/widgets/SimpleTextWidget.java +++ b/src/main/java/gregtech/api/gui/widgets/SimpleTextWidget.java @@ -105,7 +105,7 @@ public void drawInBackground(int mouseX, int mouseY, IRenderContext context) { int height = fontRenderer.FONT_HEIGHT * texts.size(); for (int i = 0; i < texts.size(); i++) { String resultText = texts.get(i); - int width = fontRenderer.getStringWidth(resultText);; + int width = fontRenderer.getStringWidth(resultText); float x = pos.x - (isCentered ? width / 2f : 0); float y = pos.y - (isCentered ? height / 2f : 0) + i * fontRenderer.FONT_HEIGHT; fontRenderer.drawString(resultText, x, y, color, false); diff --git a/src/main/java/gregtech/api/terminal/TerminalRegistry.java b/src/main/java/gregtech/api/terminal/TerminalRegistry.java index 246bff99767..d0c9a8d7f7f 100644 --- a/src/main/java/gregtech/api/terminal/TerminalRegistry.java +++ b/src/main/java/gregtech/api/terminal/TerminalRegistry.java @@ -10,6 +10,9 @@ import gregtech.common.ConfigHolder; import gregtech.common.items.MetaItems; import gregtech.common.terminal.app.ThemeSettingApp; +import gregtech.common.terminal.app.game.maze.MazeApp; +import gregtech.common.terminal.app.game.minesweeper.MinesweeperApp; +import gregtech.common.terminal.app.game.pong.PongApp; import gregtech.common.terminal.app.appstore.AppStoreApp; import gregtech.common.terminal.app.multiblockhelper.MultiBlockPreviewARApp; import gregtech.common.terminal.app.batterymanager.BatteryManagerApp; @@ -66,6 +69,17 @@ public static void init() { AppRegistryBuilder.create(new TutorialGuideApp()).defaultApp().build(); AppRegistryBuilder.create(new GuideEditorApp()).defaultApp().build(); AppRegistryBuilder.create(new ThemeSettingApp()).defaultApp().build(); + + AppRegistryBuilder.create(new PongApp()) + .battery(GTValues.LV, 75) + .build(); + AppRegistryBuilder.create(new MazeApp()) + .battery(GTValues.LV, 150) + .build(); + AppRegistryBuilder.create(new MinesweeperApp()) + .battery(GTValues.LV, 150) + .build(); + AppRegistryBuilder.create(new OreProspectorApp()) .battery(GTValues.MV, 1000) .upgrade(MetaItems.COIN_DOGE.getStackForm(10)) diff --git a/src/main/java/gregtech/api/util/TwoDimensionalRayTracer.java b/src/main/java/gregtech/api/util/TwoDimensionalRayTracer.java new file mode 100644 index 00000000000..e612779a001 --- /dev/null +++ b/src/main/java/gregtech/api/util/TwoDimensionalRayTracer.java @@ -0,0 +1,83 @@ +package gregtech.api.util; + +import org.lwjgl.util.vector.Vector2f; + +import java.awt.*; +import java.util.List; + +import static net.minecraft.util.math.MathHelper.clamp; + +// Huge thanks to https://noonat.github.io/intersect! +public class TwoDimensionalRayTracer { + public static class TwoDimensionalRayTraceResult { + public Vector2f pos = new Vector2f(); + public Vector2f delta = new Vector2f(); + public Vector2f normal = new Vector2f(); + public float time = -1; + public Rectangle collidedWith = new Rectangle(); + } + + /** + * Detects the intersection between a segment and a box, if there is any. + * + * @param pos The original position of the point. + * @param delta The proposed new position of the point. + * @param boxSize The half-width and half-height of the box + * @return + */ + public static TwoDimensionalRayTraceResult intersectBoxSegment(Vector2f pos, Vector2f delta, Vector2f boxCenter, Vector2f boxSize) { + float scaleX = (float) (1.0 / delta.x); + float scaleY = (float) (1.0 / delta.y); + float signX = Math.signum(scaleX); + float signY = Math.signum(scaleY); + float nearTimeX = (boxCenter.x - signX * (boxSize.x) - pos.x) * scaleX; + float nearTimeY = (boxCenter.y - signY * (boxSize.y) - pos.y) * scaleY; + float farTimeX = (boxCenter.x + signX * (boxSize.x) - pos.x) * scaleX; + float farTimeY = (boxCenter.y + signY * (boxSize.y) - pos.y) * scaleY; + + if (nearTimeX > farTimeY || nearTimeY > farTimeX) { + return null; + } + + double nearTime = Math.max(nearTimeX, nearTimeY); + double farTime = Math.min(farTimeX, farTimeY); + + if (nearTime >= 1 || farTime <= 0) { + return null; + } + + TwoDimensionalRayTraceResult result = new TwoDimensionalRayTraceResult(); + + result.time = (float) clamp(nearTime, 0, 1); + if (nearTimeX > nearTimeY) { + result.normal.x = -signX; + result.normal.y = 0; + } else { + result.normal.x = 0; + result.normal.y = -signY; + } + result.delta.x = (float) ((1.0 - result.time) * -delta.x); + result.delta.y = (float) ((1.0 - result.time) * -delta.y); + result.pos.x = pos.x + delta.x * result.time; + result.pos.y = pos.y + delta.y * result.time; + return result; + } + + public static TwoDimensionalRayTraceResult nearestBoxSegmentCollision(Vector2f pos, Vector2f delta, List boxes, Vector2f padding) { + TwoDimensionalRayTraceResult result = new TwoDimensionalRayTraceResult(); + result.time = 1; + result.pos.x = pos.x + delta.x; + result.pos.y = pos.y + delta.y; + for (Rectangle box : boxes) { + TwoDimensionalRayTraceResult sweep = intersectBoxSegment(pos, delta, + new Vector2f((float) box.getCenterX(), (float) box.getCenterY()), + new Vector2f((float) box.getWidth() / 2 + padding.x, (float) box.getHeight() / 2 + padding.y)); + if (sweep != null && sweep.time < result.time) { + result = sweep; + result.collidedWith = box; + } + } + return result; + } + +} diff --git a/src/main/java/gregtech/api/util/interpolate/RGBInterpolator.java b/src/main/java/gregtech/api/util/interpolate/RGBInterpolator.java new file mode 100644 index 00000000000..d12a86c8dfc --- /dev/null +++ b/src/main/java/gregtech/api/util/interpolate/RGBInterpolator.java @@ -0,0 +1,50 @@ +package gregtech.api.util.interpolate; + +import gregtech.api.util.function.TriConsumer; +import net.minecraft.util.ITickable; + +public class RGBInterpolator implements ITickable { + private final int speed; + private float r = 255; + private float g = 0; + private float b = 0; + private final TriConsumer interpolate; + private final TriConsumer callback; + private boolean isOn = false; + + public RGBInterpolator(int speed, TriConsumer interpolate, TriConsumer callback) { + this.speed = speed; + this.interpolate = interpolate; + this.callback = callback; + } + + @Override + public void update() { + if (isOn) { + if (r == 255 && g < 255) { + b -= Math.min(speed, b); + g += Math.min(speed, 255 - g); + } else if (g == 255 && b < 255) { + r -= Math.min(speed, r); + b += Math.min(speed, 255 - b); + } else if (b == 255 && r < 255) { + g -= Math.min(speed, g); + r += Math.min(speed, 255 - r); + } + interpolate.accept(r / 255, g / 255, b / 255); + } + } + + public void stop() { + callback.accept(r, g, b); + isOn = false; + } + + public void start() { + isOn = true; + } + + public boolean isActivated() { + return isOn; + } +} diff --git a/src/main/java/gregtech/common/items/behaviors/ClipboardBehavior.java b/src/main/java/gregtech/common/items/behaviors/ClipboardBehavior.java index 0655cd38adf..f90c7712f3a 100644 --- a/src/main/java/gregtech/common/items/behaviors/ClipboardBehavior.java +++ b/src/main/java/gregtech/common/items/behaviors/ClipboardBehavior.java @@ -62,7 +62,7 @@ public ModularUI createMTEUI(PlayerInventoryHolder holder, EntityPlayer entityPl ModularUI.Builder builder = ModularUI.builder(GuiTextures.BACKGROUND, 170, 238); builder.image(18, 8, 130, 14, GuiTextures.CLIPBOARD_TEXT_BOX); - builder.widget(new SimpleTextWidget(20, 10, "", 0xFFFFFF, () -> getTitle(holder)).setCenter(false)); + builder.widget(new SimpleTextWidget(20, 10, "", 0xFFFFFF, () -> getTitle(holder), true).setCenter(false)); for (int i = 0; i < 8; i++) { @@ -70,7 +70,7 @@ public ModularUI createMTEUI(PlayerInventoryHolder holder, EntityPlayer entityPl builder.widget(new ImageCycleButtonWidget(6, 37 + 20 * i, 15, 15, GuiTextures.CLIPBOARD_BUTTON, 4, () -> getButtonState(holder, finalI), (x) -> setButton(holder, finalI, x))); builder.image(22, 38 + 20 * i, 140, 12, GuiTextures.CLIPBOARD_TEXT_BOX); - builder.widget(new SimpleTextWidget(24, 40 + 20 * i, "", 0xFFFFFF, () -> getString(holder, finalI)).setCenter(false)); + builder.widget(new SimpleTextWidget(24, 40 + 20 * i, "", 0xFFFFFF, () -> getString(holder, finalI), true).setCenter(false)); } builder.widget(new ClickButtonWidget(30, 200, 16, 16, "", (x) -> incrPageNum(holder, -1)) @@ -78,7 +78,7 @@ public ModularUI createMTEUI(PlayerInventoryHolder holder, EntityPlayer entityPl builder.widget(new ClickButtonWidget(124, 200, 16, 16, "", (x) -> incrPageNum(holder, 1)) .setButtonTexture(GuiTextures.BUTTON_RIGHT).setShouldClientCallback(true)); builder.widget(new SimpleTextWidget(85, 208, "", 0xFFFFFF, - () -> (getPageNum(holder) + 1) + " / " + MAX_PAGES)); + () -> (getPageNum(holder) + 1) + " / " + MAX_PAGES, true)); return builder.build(holder, entityPlayer); } diff --git a/src/main/java/gregtech/common/terminal/app/game/maze/MazeApp.java b/src/main/java/gregtech/common/terminal/app/game/maze/MazeApp.java new file mode 100644 index 00000000000..944cd6c527f --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/maze/MazeApp.java @@ -0,0 +1,258 @@ +package gregtech.common.terminal.app.game.maze; + +import gregtech.api.gui.Widget; +import gregtech.api.gui.resources.ColorRectTexture; +import gregtech.api.gui.widgets.ClickButtonWidget; +import gregtech.api.gui.widgets.ImageWidget; +import gregtech.api.gui.widgets.LabelWidget; +import gregtech.api.gui.widgets.SimpleTextWidget; +import gregtech.api.terminal.app.AbstractApplication; +import gregtech.api.terminal.os.TerminalTheme; +import gregtech.common.terminal.app.game.maze.widget.EnemyWidget; +import gregtech.common.terminal.app.game.maze.widget.MazeWidget; +import gregtech.common.terminal.app.game.maze.widget.PlayerWidget; +import org.lwjgl.input.Keyboard; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class MazeApp extends AbstractApplication { + private int gameState = 0; + private PlayerWidget player; + private EnemyWidget enemy; + private MazeWidget maze; + private int timer = 0; + private int mazesSolved = 0; + private float speed = 25; + private int lastPlayerInput = -2; + public static int MAZE_SIZE = 9; + private List movementStore; + private boolean lastPausePress; + + private List[] FSM; + + public MazeApp() { + super("maze"); + } + + public AbstractApplication initApp() { + if (isClient) { + movementStore = new ArrayList<>(); + FSM = new List[4]; + FSM[0] = new LinkedList<>(); + FSM[1] = new LinkedList<>(); + FSM[2] = new LinkedList<>(); + FSM[3] = new LinkedList<>(); + this.setOs(os); + this.addWidget(new ImageWidget(5, 5, 333 - 10, 232 - 10, TerminalTheme.COLOR_B_2)); + // enemy 0: Title + this.addWidget(new LabelWidget(333 / 2, 222 / 2 - 50, "Theseus's Escape", 0xFFFFFFFF), 0); + this.addWidget(new ClickButtonWidget(323 / 2 - 10, 222 / 2 - 10, 30, 30, "Play", + (clickData -> { + this.setGameState(1); + this.resetGame(); + })).setShouldClientCallback(true), 0); + // GameState 1: Play + this.setMaze(new MazeWidget()); + this.setPlayer(new PlayerWidget(0, 0, this)); + this.setEnemy(new EnemyWidget(-100, -100, this)); + // GameState 2: Pause + this.addWidget(new ImageWidget(5, 5, 333 - 10, 232 - 10, new ColorRectTexture(0xFF000000)), 2, 3); + this.addWidget(new ClickButtonWidget(323 / 2 - 10, 222 / 2 - 10, 50, 20, "Continue", (clickData) -> this.setGameState(1)).setShouldClientCallback(true), 2); + this.addWidget(new LabelWidget(333 / 2, 222 / 2 - 50, "Game Paused", 0xFFFFFFFF).setXCentered(true), 2); + // GameState 3: Death + this.addWidget(new SimpleTextWidget(333 / 2, 232 / 2 - 40, "", 0xFFFFFFFF, () -> "Oh no! You were eaten by the Minotaur!", true), 3); + this.addWidget(new SimpleTextWidget(333 / 2, 232 / 2 - 28, "", 0xFFFFFFFF, () -> "You got through " + this.getMazesSolved() + " mazes before losing.", true), 3); + this.addWidget(new SimpleTextWidget(333 / 2, 232 / 2 - 16, "", 0xFFFFFFFF, () -> "Try again?", true), 3); + this.addWidget(new ClickButtonWidget(323 / 2 - 10, 222 / 2 + 10, 40, 20, "Retry", (clickData -> { + this.setGameState(1); + this.setMazesSolved(0); + MAZE_SIZE = 9; + speed = 25; + this.resetGame(); + })).setShouldClientCallback(true), 3); + } + return this; + } + + public void addWidget(Widget widget, int... visibleStates) { + this.addWidget(widget); + for (int state : visibleStates) { + FSM[state].add(widget); + } + widget.setVisible(Arrays.stream(visibleStates).allMatch(state->state==gameState)); + } + + public void setPlayer(PlayerWidget player) { + this.player = player; + this.addWidget(player, 1, 2, 3); + } + + public void setMaze(MazeWidget maze) { + this.maze = maze; + this.addWidget(maze, 1, 2, 3); + } + + public void setEnemy(EnemyWidget enemy) { + this.enemy = enemy; + this.addWidget(enemy, 1, 2, 3); + } + + @Override + public boolean isClientSideApp() { + return true; + } + + @Override + public void updateScreen() { + super.updateScreen(); + int lastState = gameState; + if (gameState == 1) { + if (Keyboard.isKeyDown(Keyboard.KEY_P)) { + gameState = 2; + lastPausePress = true; + } + if (Keyboard.isKeyDown(Keyboard.KEY_LEFT) ^ Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) { + if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)) + attemptMovePlayer(0); // Left + else + attemptMovePlayer(1); // Right + } + if (Keyboard.isKeyDown(Keyboard.KEY_UP) ^ Keyboard.isKeyDown(Keyboard.KEY_DOWN)) { + if (Keyboard.isKeyDown(Keyboard.KEY_UP)) + attemptMovePlayer(2); // Up + else + attemptMovePlayer(3); // Down + } + timer++; + if (enemy.posX < 0 && timer % (speed * MAZE_SIZE - 1) < 1) { + enemy.setGridPosition(0, 0); + } else if (timer % speed < 1) { + moveEnemy(); + } + if (enemy.posX == player.posX && enemy.posY == player.posY) { + gameState = 3; + } + } + if (gameState == 2) { + if(!Keyboard.isKeyDown(Keyboard.KEY_P)) + lastPausePress = false; + if(Keyboard.isKeyDown(Keyboard.KEY_P) && !lastPausePress) + gameState = 1; + } + if (gameState != lastState) { + FSM[lastState].forEach(widget -> widget.setVisible(false)); + FSM[gameState].forEach(widget -> widget.setVisible(true)); + } + } + + public int getGameState() { + return gameState; + } + + public void setGameState(int gameState) { + if (gameState != this.gameState) { + FSM[this.gameState].forEach(widget -> widget.setVisible(false)); + FSM[gameState].forEach(widget -> widget.setVisible(true)); + } + this.gameState = gameState; + } + + public int getRenderX(int posX) { + return this.maze.getSelfPosition().x + posX * 10; + } + + public int getRenderY(int posY) { + return this.maze.getSelfPosition().y + posY * 10; + } + + public int getTimer() { + return timer; + } + + private void attemptMovePlayer(int direction) { + if (timer < lastPlayerInput + 2) { + return; + } + lastPlayerInput = timer; + + // Did the player reach the end? + if (player.posX == MAZE_SIZE - 1 && player.posY == MAZE_SIZE - 1 && direction == 3) { + mazesSolved++; + speed *= 0.95; + if (mazesSolved % 4 == 0) { + MAZE_SIZE += 2; + speed *= 1.07; + } + resetGame(); + return; + } + + if (direction == 0 && !maze.isThereWallAt(player.posX, player.posY, false)) { + player.move(-1, 0); + if (movementStore.size() > 0 && movementStore.get(movementStore.size() - 1) == 1) { + movementStore.remove(movementStore.size() - 1); + } else { + movementStore.add(direction); + } + } else if (direction == 1 && !maze.isThereWallAt(player.posX + 1, player.posY, false)) { + player.move(1, 0); + if (movementStore.size() > 0 && movementStore.get(movementStore.size() - 1) == 0) { + movementStore.remove(movementStore.size() - 1); + } else { + movementStore.add(direction); + } + } else if (direction == 2 && !maze.isThereWallAt(player.posX, player.posY, true)) { + player.move(0, -1); + if (movementStore.size() > 0 && movementStore.get(movementStore.size() - 1) == 3) { + movementStore.remove(movementStore.size() - 1); + } else { + movementStore.add(direction); + } + } else if (direction == 3 && !maze.isThereWallAt(player.posX, player.posY + 1, true)) { + player.move(0, 1); + if (movementStore.size() > 0 && movementStore.get(movementStore.size() - 1) == 2) { + movementStore.remove(movementStore.size() - 1); + } else { + movementStore.add(direction); + } + } + } + + private void moveEnemy() { // Move enemy with the latest movements + if (enemy.posX < 0 || movementStore.isEmpty()) + return; + + int direction = movementStore.get(0); + if (direction == 0) { + enemy.move(-1, 0); + } else if (direction == 1) { + enemy.move(1, 0); + } else if (direction == 2) { + enemy.move(0, -1); + } else if (direction == 3) { + enemy.move(0, 1); + } + movementStore.remove(0); + } + + private void resetGame() { + player.setGridPosition(0, 0); + maze.recalculateSize(); + maze.initMaze(); + movementStore.clear(); + timer = 0; + lastPlayerInput = -5; + enemy.setGridPosition(-100, -100); + } + + public int getMazesSolved() { + return mazesSolved; + } + + public void setMazesSolved(int mazesSolved) { + this.mazesSolved = mazesSolved; + } +} diff --git a/src/main/java/gregtech/common/terminal/app/game/maze/widget/EnemyWidget.java b/src/main/java/gregtech/common/terminal/app/game/maze/widget/EnemyWidget.java new file mode 100644 index 00000000000..f3ec600aee8 --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/maze/widget/EnemyWidget.java @@ -0,0 +1,18 @@ +package gregtech.common.terminal.app.game.maze.widget; + +import gregtech.api.gui.IRenderContext; +import gregtech.api.util.Position; +import gregtech.common.terminal.app.game.maze.MazeApp; + +public class EnemyWidget extends PlayerWidget { + + public EnemyWidget(int posX, int posY, MazeApp app) { + super(posX, posY, app); + } + + public void drawInBackground(int mouseX, int mouseY, float partialTicks, IRenderContext context) { + this.setSelfPosition(new Position(app.getRenderX(posX), app.getRenderY(posY))); + drawSolidRect(this.getPosition().x, this.getPosition().y, 10, 10, 0xFFFFAAAA); + } + +} diff --git a/src/main/java/gregtech/common/terminal/app/game/maze/widget/MazeWidget.java b/src/main/java/gregtech/common/terminal/app/game/maze/widget/MazeWidget.java new file mode 100644 index 00000000000..edcb57664c9 --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/maze/widget/MazeWidget.java @@ -0,0 +1,185 @@ +package gregtech.common.terminal.app.game.maze.widget; + +import gregtech.api.gui.IRenderContext; +import gregtech.api.gui.Widget; +import gregtech.api.util.Position; +import gregtech.api.util.Size; +import net.minecraft.util.math.Vec2f; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static gregtech.common.terminal.app.game.maze.MazeApp.MAZE_SIZE; + +public class MazeWidget extends Widget { + + boolean[][] topWalls = new boolean[MAZE_SIZE][MAZE_SIZE]; + boolean[][] leftWalls = new boolean[MAZE_SIZE][MAZE_SIZE]; + boolean[][] includedSpots; + private int squaresChecked; + + public MazeWidget() { + super(333 / 2 - (MAZE_SIZE * 5), 232 / 2 - (MAZE_SIZE * 5), MAZE_SIZE * 10, MAZE_SIZE * 10); + initMaze(); + } + + @Override + public void drawInBackground(int mouseX, int mouseY, float partialTicks, IRenderContext context) { + // Draw outer lines + createBorder(); + // Draw inner lines + createInternalLines(); + } + + public void recalculateSize() { + this.setSelfPosition(new Position(333 / 2 - (MAZE_SIZE * 5), 232 / 2 - (MAZE_SIZE * 5))); + this.setSize(new Size(MAZE_SIZE * 10, MAZE_SIZE * 10)); + topWalls = new boolean[MAZE_SIZE][MAZE_SIZE]; + leftWalls = new boolean[MAZE_SIZE][MAZE_SIZE]; + + } + + public void createBorder() { + List lineBuffer = new ArrayList<>(); + lineBuffer.add(new Vec2f(getPosition().x + 10, getPosition().y)); + lineBuffer.add(new Vec2f(this.getSize().width + getPosition().x, getPosition().y)); + lineBuffer.add(new Vec2f(this.getSize().width + getPosition().x, this.getSize().height + getPosition().y + 2)); // Corrects for line width misalignment + drawLines(lineBuffer, 0xFFFFFFFF, 0xFFFFFFFF, 4); + lineBuffer.clear(); + lineBuffer.add(new Vec2f(this.getSize().width + getPosition().x - 10, this.getSize().height + getPosition().y)); + lineBuffer.add(new Vec2f(getPosition().x, this.getSize().height + getPosition().y)); + lineBuffer.add(new Vec2f(getPosition().x, getPosition().y - 2)); + drawLines(lineBuffer, 0xFFFFFFFF, 0xFFFFFFFF, 4); + } + + public boolean isThereWallAt(int x, int y, boolean onTops) { + if (x >= MAZE_SIZE || y >= MAZE_SIZE) + return true; + if (x < 0 || y < 0) + return true; + if ((x == 0 && !onTops) || (y == 0 && onTops)) + return true; + if (onTops) { + return topWalls[x][y]; + } else { + return leftWalls[x][y]; + } + } + + public void createInternalLines() { + for (int i = 0; i < MAZE_SIZE; i++) { + for (int j = 0; j < MAZE_SIZE; j++) { + List list = new ArrayList<>(); + if (j != 0 && isThereWallAt(i, j, true)) { + list.add(new Vec2f(getPosition().x + 10 * i, getPosition().y + 10 * j)); + list.add(new Vec2f(getPosition().x + 10 * (i + 1), getPosition().y + 10 * j)); + drawLines(list, 0xFFFFFFFF, 0xFFFFFFFF, 2); + list.clear(); + } + if (i != 0 && isThereWallAt(i, j, false)) { + list.add(new Vec2f(getPosition().x + 10 * i, getPosition().y + 10 * j)); + list.add(new Vec2f(getPosition().x + 10 * i, getPosition().y + 10 * (j + 1))); + drawLines(list, 0xFFFFFFFF, 0xFFFFFFFF, 2); + } + } + } + } + + public void initMaze() { + includedSpots = new boolean[MAZE_SIZE][MAZE_SIZE]; + for (int i = 0; i < MAZE_SIZE; i++) { // Fill array with walls so that they can be carved out + for (int j = 0; j < MAZE_SIZE; j++) { + leftWalls[i][j] = true; + topWalls[i][j] = true; + } + } + + includedSpots[(int) (Math.random() * MAZE_SIZE)][(int) (Math.random() * MAZE_SIZE)] = true; // Can seed our particular maze + // Improves maze randomization. + List positions = new ArrayList<>(); + for(int i = 0; i < MAZE_SIZE * MAZE_SIZE; i++) { + positions.add(i); + } + Collections.shuffle(positions); + + for (int position : positions) { + if (!includedSpots[position / MAZE_SIZE][position % MAZE_SIZE]) { + do { + resetStuckCounter(); + } while (!this.createPath(position / MAZE_SIZE, position % MAZE_SIZE, new boolean[MAZE_SIZE][MAZE_SIZE])); + } + } + } + + // Wilson random walk maze generation + public boolean createPath(int x, int y, boolean[][] walkedPaths) { + squaresChecked++; + if(squaresChecked > 20000) // Probably stuck. + return false; + if(walkedPaths[x][y]) + return false; + if(this.includedSpots[x][y]) + return true; + this.includedSpots[x][y] = true; + walkedPaths[x][y] = true; + // Find unoccupied directions + // Left 0 + List directions = new ArrayList<>(); + if (x != 0 && !walkedPaths[x - 1][y]) { + directions.add(0); + } + // Right 1 + if (x != MAZE_SIZE - 1 && !walkedPaths[x + 1][y]) { + directions.add(1); + } + // Up 2 + if (y != 0 && !walkedPaths[x][y - 1]) { + directions.add(2); + } + // Down 3 + if (y != MAZE_SIZE - 1 && !walkedPaths[x][y + 1]) { + directions.add(3); + } + Collections.shuffle(directions); + // Select one + while (directions.size() > 0) { + int direction = directions.get(directions.size() - 1); + // Use direction to create new coordinates + int newX = x; + int newY = y; + if (direction == 0) { + newX--; + } else if (direction == 1) { + newX++; + } else if (direction == 2) { + newY--; + } else if (direction == 3) { + newY++; + } + if (createPath(newX, newY, walkedPaths)) { + // Delete walls and return + if (direction == 0) { + leftWalls[x][y] = false; + } else if (direction == 1) { + leftWalls[x + 1][y] = false; + } else if (direction == 2) { + topWalls[x][y] = false; + } else if (direction == 3) { + topWalls[x][y + 1] = false; + } + return true; + } else { + directions.remove(directions.size() - 1); + } + } + // Reset current position + this.includedSpots[x][y] = false; + walkedPaths[x][y] = false; + return false; + } + + public void resetStuckCounter() { + squaresChecked = 0; + } +} diff --git a/src/main/java/gregtech/common/terminal/app/game/maze/widget/PlayerWidget.java b/src/main/java/gregtech/common/terminal/app/game/maze/widget/PlayerWidget.java new file mode 100644 index 00000000000..16dbea8b46b --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/maze/widget/PlayerWidget.java @@ -0,0 +1,35 @@ +package gregtech.common.terminal.app.game.maze.widget; + +import gregtech.api.gui.IRenderContext; +import gregtech.api.gui.Widget; +import gregtech.api.util.Position; +import gregtech.common.terminal.app.game.maze.MazeApp; + +public class PlayerWidget extends Widget { + protected MazeApp app; + public int posX; + public int posY; + public PlayerWidget(int posX, int posY, MazeApp app) { + super(app.getRenderX(posX), app.getRenderY(posY), 10, 10); + this.app = app; + this.posX = posX; + this.posY = posY; + } + + @Override + public void drawInBackground(int mouseX, int mouseY, float partialTicks, IRenderContext context) { + this.setSelfPosition(new Position(app.getRenderX(posX), app.getRenderY(posY))); + drawSolidRect(this.getPosition().x, this.getPosition().y, 10, 10, 0xAAAAAAFF); + } + + public void move(int deltaX, int deltaY) { + this.posX += deltaX; + this.posY += deltaY; + } + + + public void setGridPosition(int posX, int posY) { + this.posX = posX; + this.posY = posY; + } +} diff --git a/src/main/java/gregtech/common/terminal/app/game/minesweeper/MinesweeperApp.java b/src/main/java/gregtech/common/terminal/app/game/minesweeper/MinesweeperApp.java new file mode 100644 index 00000000000..a5866a4a969 --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/minesweeper/MinesweeperApp.java @@ -0,0 +1,59 @@ +package gregtech.common.terminal.app.game.minesweeper; + +import gregtech.api.gui.widgets.SimpleTextWidget; +import gregtech.api.terminal.app.AbstractApplication; +import gregtech.common.terminal.app.game.minesweeper.widget.MineMapWidget; + +public class MinesweeperApp extends AbstractApplication { + private MineMapWidget mineField; + private int timer; + private int resetCountdown = 100; + + public MinesweeperApp() { + super("minesweeper"); + } + + @Override + public AbstractApplication initApp() { + mineField = new MineMapWidget(20, 12, 40); + this.addWidget(mineField); + this.addWidget(new SimpleTextWidget(333 / 6, 10, "", 0xFFCCCCCC, this::getFlagsPercentage, true)); + this.addWidget(new SimpleTextWidget(333 / 8 * 5, 10, "", 0xFFCCCCCC, this::getStatus, true)); + + return this; + } + + @Override + public void updateScreen() { + super.updateScreen(); + if(mineField.hasWon() || mineField.hasLost()) { + if(mineField.hasWon()) { + mineField.notifyWon(); + } + resetCountdown--; + } else + timer++; + if (resetCountdown == 0) { + mineField.resetData(); + resetCountdown = 100; + timer = 0; + } + } + + public String getFlagsPercentage() { + return mineField.flagsPlaced + "/" + mineField.mineCount; + } + + public String getStatus() { + return resetCountdown == 100 ? + (timer / 20) + " seconds elapsed" : // Normal + mineField.hasLost() ? + "You lost. Game will restart in " + resetCountdown / 20 + " seconds." : // Losing condition + "You won in " + (timer / 20) + " seconds! Game will restart in " + resetCountdown / 20; // Winning condition + } + + @Override + public boolean isClientSideApp() { + return true; + } +} diff --git a/src/main/java/gregtech/common/terminal/app/game/minesweeper/widget/MineMapWidget.java b/src/main/java/gregtech/common/terminal/app/game/minesweeper/widget/MineMapWidget.java new file mode 100644 index 00000000000..ec3ad0f73fa --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/minesweeper/widget/MineMapWidget.java @@ -0,0 +1,215 @@ +package gregtech.common.terminal.app.game.minesweeper.widget; + +import gregtech.api.gui.IRenderContext; +import gregtech.api.gui.Widget; +import gregtech.api.gui.resources.TextureArea; +import gregtech.api.util.interpolate.RGBInterpolator; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.ResourceLocation; + +public class MineMapWidget extends Widget { + + private static final TextureArea COVERED = new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/covered.png"), 0, 0, 1, 1); + private static final TextureArea FLAG = new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/flag.png"), 0, 0, 1, 1); + private static final TextureArea BOMB = new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/bomb.png"), 0, 0, 1, 1); + + private static final TextureArea[] NUMBERS = { + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/blank.png"), 0, 0, 1, 1), + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/1.png"), 0, 0, 1, 1), + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/2.png"), 0, 0, 1, 1), + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/3.png"), 0, 0, 1, 1), + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/4.png"), 0, 0, 1, 1), + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/5.png"), 0, 0, 1, 1), + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/6.png"), 0, 0, 1, 1), + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/7.png"), 0, 0, 1, 1), + new TextureArea(new ResourceLocation("gregtech:textures/gui/terminal/minesweeper/8.png"), 0, 0, 1, 1) + }; + + public int mineCount; + public int flagsPlaced; + + private int width; + private int height; + + private boolean isPrepared; + + private boolean[][] mines; + private boolean[][] flags; + private boolean[][] checkedSpaces; + private int[][] generatedNumbers; + + private boolean isLost; + private boolean isWon; + private int timer = 0; + private static final RGBInterpolator interpolator = new RGBInterpolator(5, + (r, g, b) -> GlStateManager.color(r.floatValue(), g.floatValue(), b.floatValue()), + (r, g, b) -> GlStateManager.color(0, 0, 0)); + + public MineMapWidget(int width, int height, int mineCount) { + super(333 / 2 - width * 8, 232 / 2 - height * 8, width * 16, height * 16); + this.width = width; + this.height = height; + this.resetData(); + this.mineCount = mineCount; + } + + public void resetData() { + mines = new boolean[width][height]; + generatedNumbers = new int[width][height]; + checkedSpaces = new boolean[width][height]; + flags = new boolean[width][height]; + isLost = false; + isWon = false; + isPrepared = false; + flagsPlaced = 0; + } + + public void initMines(int startX, int startY) { + int minesPlaced = 0; + while (minesPlaced < mineCount) { + for (; minesPlaced < mineCount; minesPlaced++) { + placeMine(startX, startY); + } + + // Are there any sections that we can't figure out what's inside? + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + boolean isTrapped = true; + // The weird ternaries here are making sure to not cause overflows + for (int xMod = i == 0 ? 0 : -1; xMod < (i == width - 1 ? 1 : 2); xMod++) { + for (int yMod = j == 0 ? 0 : -1; yMod < (j == height - 1 ? 1 : 2); yMod++) { + isTrapped &= mines[i + xMod][j + yMod]; + } + } + if (isTrapped) { + // Yes, so just take out the middle + mines[i][j] = false; + minesPlaced--; + } + } + } + } + + + // Add to surrounding numbers for the mine + // The weird ternaries here are making sure to not cause overflows + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + if(mines[x][y]) { + for (int xMod = x == 0 ? 0 : -1; xMod < (x == width - 1 ? 1 : 2); xMod++) { + for (int yMod = y == 0 ? 0 : -1; yMod < (y == height - 1 ? 1 : 2); yMod++) { + generatedNumbers[x + xMod][y + yMod]++; + } + } + } + } + } + isPrepared = true; + } + + private void placeMine(int startX, int startY) { + int x = (int) (Math.random() * width); + int y = (int) (Math.random() * height); + + // The weird part to the right is making sure the player doesn't start on a numbered tile + while (mines[x][y] || ((startX < x + 3 && startX > x - 3) && (startY < y + 3 && startY > y - 3))) { + x = (int) (Math.random() * width); + y = (int) (Math.random() * height); + } + mines[x][y] = true; + } + + @Override + public void drawInBackground(int mouseX, int mouseY, float partialTicks, IRenderContext context) { + timer++; + if (isWon && !interpolator.isActivated()) { // Fancy colors :) + interpolator.start(); + } + if (!isWon && interpolator.isActivated()) { + interpolator.stop(); + } + interpolator.update(); + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + if (isLost && mines[i][j]) // If the player lost, show where the mines are. + BOMB.draw(i * 16 + getPosition().getX(), j * 16 + getPosition().getY(), 16, 16); + else if (!checkedSpaces[i][j]) { + if (flags[i][j]) + FLAG.draw(i * 16 + getPosition().getX(), j * 16 + getPosition().getY(), 16, 16); + else + COVERED.draw(i * 16 + getPosition().getX(), j * 16 + getPosition().getY(), 16, 16); + } else if (!mines[i][j]) + NUMBERS[generatedNumbers[i][j]].draw(i * 16 + getPosition().getX(), j * 16 + getPosition().getY(), 16, 16); + else + BOMB.draw(i * 16 + getPosition().getX(), j * 16 + getPosition().getY(), 16, 16); + } + } + } + + @Override + public boolean mouseClicked(int mouseX, int mouseY, int button) { + if(isWon || isLost) { + return false; // Don't let them interact now... + } + + int gridX = (mouseX - getPosition().getX()) / 16; + int gridY = (mouseY - getPosition().getY()) / 16; + if (gridX >= width || gridY >= height || gridX < 0 || gridY < 0) { + return false; + } + + + if (button == 0 && !flags[gridX][gridY]) { + if (!isPrepared) + initMines(gridX, gridY); + if (generatedNumbers[gridX][gridY] == 0) + uncoverSafeTiles(gridX, gridY); + else + checkedSpaces[gridX][gridY] = true; + if (mines[gridX][gridY]) + isLost = true; + } else if (button == 1 && !checkedSpaces[gridX][gridY]) { + flags[gridX][gridY] = !flags[gridX][gridY]; + if (flags[gridX][gridY]) + flagsPlaced++; + else + flagsPlaced--; + } + + return true; + } + + private void uncoverSafeTiles(int x, int y) { + checkedSpaces[x][y] = true; + if(generatedNumbers[x][y] != 0) + return; + // Weird ternaries again for preventing ArrayIndexOutOfBounds exceptions + for (int xMod = x == 0 ? 0 : -1; xMod < (x == width - 1 ? 1 : 2); xMod++) { + for (int yMod = y == 0 ? 0 : -1; yMod < (y == height - 1 ? 1 : 2); yMod++) { + if (!checkedSpaces[x + xMod][y + yMod]) + uncoverSafeTiles(x + xMod, y + yMod); + } + } + } + + public boolean hasLost() { + return isLost; + } + + public boolean hasWon() { + if (!isPrepared) + return false; + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + if (mines[i][j] != flags[i][j] || checkedSpaces[i][j] == mines[i][j]) { // If there is an unchecked safe square, or an uncovered bomb... + return false; + } + } + } + return true; + } + + public void notifyWon() { + isWon = true; + } +} diff --git a/src/main/java/gregtech/common/terminal/app/game/pong/PongApp.java b/src/main/java/gregtech/common/terminal/app/game/pong/PongApp.java new file mode 100644 index 00000000000..92c635bb194 --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/pong/PongApp.java @@ -0,0 +1,164 @@ +package gregtech.common.terminal.app.game.pong; + +import gregtech.api.gui.resources.ColorRectTexture; +import gregtech.api.gui.resources.TextureArea; +import gregtech.api.gui.widgets.ImageWidget; +import gregtech.api.gui.widgets.SimpleTextWidget; +import gregtech.api.terminal.app.AbstractApplication; +import gregtech.api.terminal.os.TerminalOSWidget; +import gregtech.api.terminal.os.TerminalTheme; +import gregtech.api.util.Position; +import gregtech.api.util.TwoDimensionalRayTracer; +import gregtech.common.terminal.app.game.pong.widget.BallWidget; +import gregtech.common.terminal.app.game.pong.widget.PaddleWidget; +import net.minecraft.nbt.NBTTagCompound; +import org.lwjgl.input.Keyboard; +import org.lwjgl.util.vector.Vector2f; + +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + + +public class PongApp extends AbstractApplication { + + private BallWidget ball; + private int leftScore; + private int rightScore; + private List paddles; + private List solidObjects; + private int userInput = -1; + private int timer = 0; + + public PongApp() { + super("pong"); + } + + @Override + public AbstractApplication initApp() { + if(isClient) { + paddles = new ArrayList<>(); + solidObjects = new ArrayList<>(); + this.addWidget(new ImageWidget(5, 5, 333 - 10, 232 - 10, TerminalTheme.COLOR_B_2)); + this.addWidget(new ImageWidget(333 / 2 - 4, 5, 6, 232 - 10, new ColorRectTexture(0xAAAAAAAA))); + this.setBall(new BallWidget(333 / 2 - 1, 232 / 2 - 1)); + this.addWidget(new SimpleTextWidget(50, 20, "", 0xAAAAAA, () -> String.valueOf(this.getScore(true)), true)); + this.addWidget(new SimpleTextWidget(283, 20, "", 0xAAAAAA, () -> String.valueOf(this.getScore(false)), true)); + this.initPaddles(); + } + return this; + } + + @Override + public boolean isClientSideApp() { + return true; + } + + public void setBall(BallWidget ball) { + this.ball = ball; + this.addWidget(ball); + } + + public void initPaddles() { + paddles.add(new PaddleWidget(20, 232 / 2 - 1, 4, 20, (PaddleWidget paddle) -> this.getUserInput())); + paddles.add(new PaddleWidget(313, 232 / 2 - 1, 4, 20, this::simplePaddleAI)); + paddles.forEach(this::addWidget); + this.solidObjects.add(new Rectangle(0, 0, 333, 10)); + this.solidObjects.add(new Rectangle(0, 222, 333, 10)); + } + + public void score(boolean side) { + if (side) { + leftScore++; + ball.theta = (float) Math.PI; + } else { + rightScore++; + ball.theta = (float) 0; + } + ball.theta += Math.random() * 0.2; + ball.setSelfPosition(new Position(333 / 2 - 1, 232 / 2 - 1)); + } + + @Override + public void updateScreen() { + super.updateScreen(); + timer++; + if (Keyboard.isKeyDown(Keyboard.KEY_UP) ^ Keyboard.isKeyDown(Keyboard.KEY_DOWN)) { + if (Keyboard.isKeyDown(Keyboard.KEY_UP)) + userInput = 1; + else + userInput = 0; + } else { + userInput = -1; + } + if (ball.getSelfPosition().getX() < 10) { + this.score(false); // Right side gains a point + } else if (ball.getSelfPosition().getX() > 323) { + this.score(true); // Left side gains a point + } else { + paddles.forEach((paddle) -> solidObjects.add(new Rectangle(paddle.toSelfRectangleBox()))); + int timeLeft = 1; + + TwoDimensionalRayTracer.TwoDimensionalRayTraceResult result = TwoDimensionalRayTracer.nearestBoxSegmentCollision( + new Vector2f(ball.getSelfPosition().x, ball.getSelfPosition().y), + new Vector2f((float) (Math.cos(ball.theta) * 6), (float) (Math.sin(ball.theta) * 6)), + solidObjects, + new Vector2f(4, 4)); + while (result.time != 1 && timeLeft != 0) { + float angleMod = 0; + if (result.pos.y < result.collidedWith.getCenterY() - 2) { + angleMod -= Math.signum(result.normal.x) * 0.6; + } else if (result.pos.x > result.collidedWith.getCenterY() + 2) { + angleMod += Math.signum(result.normal.x) * 0.6; + } + ball.theta = (float) (Math.acos(result.normal.x) * 2 - ball.theta + Math.PI + angleMod) % (2 * Math.PI); // Reflects with a slight angle modification. + + if (ball.theta > Math.PI / 2 - 0.5 && ball.theta < Math.PI / 2 + 0.5) { + if (ball.theta <= Math.PI / 2) + ball.theta = Math.PI / 2 - 0.51; + else + ball.theta = Math.PI / 2 + 0.51; + } + if (ball.theta > 3 * Math.PI / 2 - 0.5 && ball.theta < 3 * Math.PI / 2 + 0.5) { + if (ball.theta < 3 * Math.PI / 2) + ball.theta = 3 * Math.PI / 2 - 0.51; + else + ball.theta = 3 * Math.PI / 2 + 0.51; + } + timeLeft -= result.time * timeLeft; + result = TwoDimensionalRayTracer.nearestBoxSegmentCollision( + new Vector2f(ball.getSelfPosition().x, ball.getSelfPosition().y), + new Vector2f((float) (Math.cos(ball.theta) * 8 * timeLeft), (float) (Math.sin(ball.theta) * 8 * timeLeft)), + solidObjects, + new Vector2f(4, 4)); + // To prevent it getting permanently lodged into something. + ball.addSelfPosition((Math.cos(ball.theta) * 6 * (result.time + 0.1) * timeLeft), (Math.sin(ball.theta) * 6 * (result.time + 0.1) * timeLeft)); + } + ball.addSelfPosition((Math.cos(ball.theta) * 6 * timeLeft), (Math.sin(ball.theta) * 6 * timeLeft)); + solidObjects.remove(2); + solidObjects.remove(2); + } + if (ball.getSelfPosition().getY() > 222) { + ball.setSelfPosition(new Position(ball.getSelfPosition().getX(), 211)); + } else if (ball.getSelfPosition().getY() < 10) + ball.setSelfPosition(new Position(ball.getSelfPosition().getX(), 21)); + } + + public int simplePaddleAI(PaddleWidget paddle) { + if (this.timer % 3 == 0) + return -1; + if ((ball.getSelfPosition().getY() + 2 * paddle.getSelfPosition().getY()) / 3 < paddle.getSelfPosition().getY()) + return 1; + else if ((ball.getSelfPosition().getY() + 2 * paddle.getSelfPosition().getY()) / 3 > paddle.getSelfPosition().getY()) + return 0; + return -1; + } + + public int getScore(boolean side) { + return side ? leftScore : rightScore; + } + + public int getUserInput() { + return userInput; + } +} diff --git a/src/main/java/gregtech/common/terminal/app/game/pong/widget/BallWidget.java b/src/main/java/gregtech/common/terminal/app/game/pong/widget/BallWidget.java new file mode 100644 index 00000000000..b4e9b63e9e2 --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/pong/widget/BallWidget.java @@ -0,0 +1,44 @@ +package gregtech.common.terminal.app.game.pong.widget; + +import gregtech.api.gui.resources.TextureArea; +import gregtech.api.gui.widgets.ImageWidget; +import gregtech.api.util.Position; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.util.vector.Vector2f; + +public class BallWidget extends ImageWidget { + public double theta; + private double xAccurate; + private double yAccurate; + + public BallWidget(int xPosition, int yPosition) { + super(xPosition, yPosition, 8, 8, new TextureArea(new ResourceLocation("gregtech:textures/gui/widget/pong_ball.png"), 0, 0, 1, 1)); + theta = (Math.random() > 0.5 ? Math.PI : Math.PI / 2) + Math.random() * 0.2; + xAccurate = xPosition; + yAccurate = yPosition; + } + + @Override + public void setSelfPosition(Position selfPosition) { + super.setSelfPosition(selfPosition); + xAccurate = selfPosition.x; + yAccurate = selfPosition.y; + } + + @Override + public Position addSelfPosition(int addX, int addY) { + xAccurate += addX; + yAccurate += addY; + return super.addSelfPosition(addX, addY); + } + + public void addSelfPosition(double addX, double addY) { + xAccurate += addX; + yAccurate += addY; + super.setSelfPosition(new Position((int) xAccurate, (int) yAccurate)); + } + + public Vector2f getPreciseSelfPosition() { + return new Vector2f((float) xAccurate , (float) yAccurate); + } +} diff --git a/src/main/java/gregtech/common/terminal/app/game/pong/widget/PaddleWidget.java b/src/main/java/gregtech/common/terminal/app/game/pong/widget/PaddleWidget.java new file mode 100644 index 00000000000..a847117eec0 --- /dev/null +++ b/src/main/java/gregtech/common/terminal/app/game/pong/widget/PaddleWidget.java @@ -0,0 +1,51 @@ +package gregtech.common.terminal.app.game.pong.widget; + +import gregtech.api.gui.IRenderContext; +import gregtech.api.gui.Widget; +import gregtech.api.util.Position; +import java.awt.*; +import java.util.function.Function; + +public class PaddleWidget extends Widget { + Function controlSupplier; + + public PaddleWidget(int x, int y, int width, int height, Function controlSupplier) { + super(x, y, width, height); + this.controlSupplier = controlSupplier; + } + + @Override + public void drawInBackground(int mouseX, int mouseY, float partialTicks, IRenderContext context) { + drawSolidRect(this.toRectangleBox().x - this.toRectangleBox().width / 2, this.toRectangleBox().y - this.toRectangleBox().height / 2, this.toRectangleBox().width, this.toRectangleBox().height, 0xFFFFFFFF); + } + + @Override + public void updateScreen() { + super.updateScreen(); + if (this.getSelfPosition().getY() < 30) { + this.setSelfPosition(new Position(this.getSelfPosition().getX(), 30)); + } + if (this.getSelfPosition().getY() > 202) { + this.setSelfPosition(new Position(this.getSelfPosition().getX(), 202)); + } + int speed; + switch (controlSupplier.apply(this)) { + case 0: + speed = 6; + break; + case 1: + speed = -6; + break; + default: + speed = 0; + } + this.addSelfPosition(0, speed); + + } + + public Rectangle toSelfRectangleBox() { + return new Rectangle(this.getSelfPosition().x - this.toRectangleBox().width / 2, this.getSelfPosition().y - this.toRectangleBox().height / 2, + this.toRectangleBox().width, this.toRectangleBox().height); + } + +} diff --git a/src/main/resources/assets/gregtech/lang/en_us.lang b/src/main/resources/assets/gregtech/lang/en_us.lang index e5cc517d433..40b4c877a15 100644 --- a/src/main/resources/assets/gregtech/lang/en_us.lang +++ b/src/main/resources/assets/gregtech/lang/en_us.lang @@ -3962,6 +3962,11 @@ gregtech.terminal.app_name.theme_settings=Theme Settings gregtech.terminal.app_name.guide_editor=Guide Editor gregtech.terminal.app_name.recipe_chart=Recipe Chart gregtech.terminal.app_name.ore_prospector=Ore Prospector +gregtech.terminal.app_name.pong=Pong +gregtech.terminal.app_name.minesweeper=Minesweeper +gregtech.terminal.app_name.maze=Theseus's Escape + + gregtech.terminal.app_name.console=GT Console gregtech.terminal.app_name.battery=Battery Manager gregtech.terminal.app_name.hardware=Hardware Manager @@ -3975,6 +3980,9 @@ terminal.items.description=A guide book about items. terminal.machines.description=A guide book about gt machines. terminal.multiblocks.description=A guide book about multi-blocks. terminal.tutorials.description=Introduces all kinds of things, CT integration, tips, tutorials and more. +terminal.pong.description=A classic pong game, if you're really that bored of waiting for that tungstensteel. +terminal.minesweeper.description=A classic minesweeper game, if you're in class. +terminal.maze.description=A GTOS exclusive game finding you racing through an endless labyrinth to survive the Minotaur! texture.modify_gui_texture.missing=Missing Texture texture.url_texture.fail=Load Failed diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/maze/icon.png b/src/main/resources/assets/gregtech/textures/gui/terminal/maze/icon.png new file mode 100644 index 00000000000..1d878d98e14 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/maze/icon.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/1.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/1.png new file mode 100644 index 00000000000..a1d1739ff43 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/1.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/2.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/2.png new file mode 100644 index 00000000000..11be18c5486 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/2.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/3.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/3.png new file mode 100644 index 00000000000..e66644946ca Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/3.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/4.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/4.png new file mode 100644 index 00000000000..9cdd3e2fd5a Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/4.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/5.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/5.png new file mode 100644 index 00000000000..1790060b9e9 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/5.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/6.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/6.png new file mode 100644 index 00000000000..9fcf0523d12 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/6.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/7.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/7.png new file mode 100644 index 00000000000..c0c3f740a9f Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/7.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/8.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/8.png new file mode 100644 index 00000000000..38bf860d341 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/8.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/blank.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/blank.png new file mode 100644 index 00000000000..6706f57bb61 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/blank.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/bomb.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/bomb.png new file mode 100644 index 00000000000..11ee1f36580 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/bomb.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/covered.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/covered.png new file mode 100644 index 00000000000..2b2e5bc6ccc Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/covered.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/flag.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/flag.png new file mode 100644 index 00000000000..f003b2e15ac Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/flag.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/icon.png b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/icon.png new file mode 100644 index 00000000000..7ec035c5919 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/minesweeper/icon.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/terminal/pong/icon.png b/src/main/resources/assets/gregtech/textures/gui/terminal/pong/icon.png new file mode 100644 index 00000000000..7d017b0a15c Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/terminal/pong/icon.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/pong_ball.png b/src/main/resources/assets/gregtech/textures/gui/widget/pong_ball.png new file mode 100644 index 00000000000..672ef24df58 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/widget/pong_ball.png differ