Skip to content

Commit

Permalink
Revert "revert: "thistitlemakesthebothappy: replace the client with s…
Browse files Browse the repository at this point in the history
…omething better""
  • Loading branch information
ThatGamerBlue authored Jun 28, 2022
1 parent 09b0a5f commit aa1f0a2
Show file tree
Hide file tree
Showing 5 changed files with 404 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.thatgamerblue.snake;

public enum Direction {
UP(0, -1, "^"),
DOWN(0, 1, "v"),
LEFT(-1, 0, "<"),
RIGHT(1, 0, ">");

public int xOffset;
public int yOffset;
public String display;

Direction(int xOff, int yOff, String display) {
xOffset = xOff;
yOffset = yOff;
this.display = display;
}
}
191 changes: 191 additions & 0 deletions runelite-client/src/main/java/com/thatgamerblue/snake/GameHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package com.thatgamerblue.snake;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JOptionPane;

public class GameHandler implements KeyListener {
private static final int DEFAULT_REFRESH_INTERVAL = 100;
private static final int PX_PER_TILE = 16;
private static final int WIDTH = SnakeGame.FRAME_WIDTH / PX_PER_TILE;
private static final int HEIGHT = SnakeGame.FRAME_HEIGHT / PX_PER_TILE;
private static final int MIDDLE_X = WIDTH / 2;
private static final int MIDDLE_Y = HEIGHT / 2;
private static final Color SNAKE_HEAD_COLOR = Color.GREEN;
private static final Color SNAKE_BODY_COLOR = new Color(0, 128, 0);
private static final Color APPLE_COLOR = Color.RED;
private static final Font HEAD_FONT = new Font("Arial", Font.BOLD, 12);
private static final Font FONT = new Font("Arial", Font.BOLD, 16);

private final Set<Integer> keysPressed = new HashSet<>();
private Direction pendingDirection = Direction.LEFT;
private Direction direction = Direction.LEFT;
private SnakePart head;
private SnakePart apple = new SnakePart();
private int score;

private SnakeGame game;

public GameHandler(SnakeGame game) {
this.game = game;
}

public void reset() {
if (score != 0) {
JOptionPane.showMessageDialog(null, "You died! Your score: " + score, "OpenOSRS Snake", JOptionPane.INFORMATION_MESSAGE);
}
head = new SnakePart();
SnakePart current = head;
current.x = MIDDLE_X;
current.y = MIDDLE_Y;

for (int i = 0; i < 4; i++) {
current.next = new SnakePart();
current = current.next;
current.x = MIDDLE_X + i + 1;
current.y = MIDDLE_Y;
}

direction = Direction.LEFT;
pendingDirection = Direction.LEFT;
createNewApple();
score = 0;
SnakeGame.currentRefreshInterval = DEFAULT_REFRESH_INTERVAL;
}

public void update() throws Exception {
processInput();
direction = pendingDirection;
SnakePart newHead = new SnakePart();
newHead.x = head.x + direction.xOffset;
newHead.y = head.y + direction.yOffset;
newHead.next = head;
head = newHead;

boolean ateApple = false;
if (head.x == apple.x && head.y == apple.y) {
createNewApple();
ateApple = true;
score++;
SnakeGame.currentRefreshInterval = Math.max(50, DEFAULT_REFRESH_INTERVAL - score);
if (score == 15) {
game.stop();
}
}

if (!ateApple) {
SnakePart current = head;
while (current.next.next != null) {
current = current.next;
}
current.next = null;
}

if (head.x < 0 || head.x >= WIDTH || head.y < 0 || head.y >= HEIGHT) {
reset();
}

SnakePart current = head.next;
while (current.next != null) {
if (current.x == head.x && current.y == head.y) {
reset();
break;
}
current = current.next;
}
}

private Point findAppleSpawn() {
Set<Point> used = new HashSet<>();
for (SnakePart part = head; part != null; part = part.next) {
used.add(new Point(part.x, part.y));
}
int x, y;
do {
x = (int) (Math.random() * (WIDTH - 2)) + 1;
y = (int) (Math.random() * (HEIGHT - 2)) + 1;
} while (used.contains(new Point(x, y)));
return new Point(x, y);
}

private void createNewApple() {
Point spawn = findAppleSpawn();
apple.x = spawn.x;
apple.y = spawn.y;
}

private void processInput() {
if (keysPressed.contains(KeyEvent.VK_UP)) {
if (direction != Direction.DOWN) {
pendingDirection = Direction.UP;
}
} else if (keysPressed.contains(KeyEvent.VK_DOWN)) {
if (direction != Direction.UP) {
pendingDirection = Direction.DOWN;
}
} else if (keysPressed.contains(KeyEvent.VK_LEFT)) {
if (direction != Direction.RIGHT) {
pendingDirection = Direction.LEFT;
}
} else if (keysPressed.contains(KeyEvent.VK_RIGHT)) {
if (direction != Direction.LEFT) {
pendingDirection = Direction.RIGHT;
}
}
}





public void draw(Graphics g) {
// draw snake head
g.setColor(SNAKE_HEAD_COLOR);
SnakePart current = head;
while (current != null) {
g.fillRect(current.x * PX_PER_TILE, current.y * PX_PER_TILE, PX_PER_TILE, PX_PER_TILE);
g.setColor(Color.BLACK);
g.drawRect(current.x * PX_PER_TILE, current.y * PX_PER_TILE, PX_PER_TILE, PX_PER_TILE);
if (current == head) {
g.setFont(HEAD_FONT);
g.drawString(direction.display, head.x * PX_PER_TILE + 6, (head.y + 1) * PX_PER_TILE - 3);
}

current = current.next;
g.setColor(SNAKE_BODY_COLOR);
}
g.setColor(APPLE_COLOR);
g.fillRect(apple.x * PX_PER_TILE, apple.y * PX_PER_TILE, PX_PER_TILE, PX_PER_TILE);
g.setColor(Color.BLACK);
g.drawRect(apple.x * PX_PER_TILE, apple.y * PX_PER_TILE, PX_PER_TILE, PX_PER_TILE);

g.setColor(Color.BLACK);
g.setFont(FONT);
g.drawString("Score: " + score, SnakeGame.FRAME_WIDTH - 100, SnakeGame.FRAME_HEIGHT - 10);
}




@Override
public void keyTyped(KeyEvent e) {

}

@Override
public void keyPressed(KeyEvent e) {
keysPressed.add(e.getKeyCode());
}

@Override
public void keyReleased(KeyEvent e) {
keysPressed.remove(e.getKeyCode());
}
}
165 changes: 165 additions & 0 deletions runelite-client/src/main/java/com/thatgamerblue/snake/SnakeGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package com.thatgamerblue.snake;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import lombok.SneakyThrows;
import net.runelite.client.RuneLite;

public class SnakeGame {
public static String[] arguments;
public static final int FRAME_WIDTH = 800;
public static final int FRAME_HEIGHT = 608;
public static long currentRefreshInterval = 100;
private static GameHandler gameHandler;
private final Object redrawLock = new Object();
private Component component;
private Image imageBuffer;
private boolean running = true;

public void start(Component component) {
this.component = component;
gameHandler.reset();
imageBuffer = component.createImage(component.getWidth(),
component.getHeight());
Thread thread = new Thread(this::runGameLoop);
thread.start();
}

public void stop() throws Exception {
int option = JOptionPane.showConfirmDialog(null,
"You scored 15! Press Yes to launch OpenOSRS, press No to keep playing snake.",
"OpenOSRS Snake", JOptionPane.YES_NO_OPTION);
if (option == JOptionPane.YES_OPTION) {
running = false;
Component c = component;
while (c != null) {
c.setVisible(false);
c = c.getParent();
}
RuneLite.oldMain(arguments);
}
}

@SneakyThrows
private void runGameLoop() {
// update the game repeatedly
while (running) {
long durationMs = redraw();
try {
Thread.sleep(Math.max(0, currentRefreshInterval - durationMs));
} catch (InterruptedException e) {
}
}
}

private long redraw() throws Exception {

long t = System.currentTimeMillis();

// At this point perform changes to the model that the component will
// redraw

updateModel();

// draw the model state to a buffered image which will get
// painted by component.paint().
drawModelToImageBuffer();

// asynchronously signals the paint to happen in the awt event
// dispatcher thread
component.repaint();

// use a lock here that is only released once the paintComponent
// has happened so that we know exactly when the paint was completed and
// thus know how long to pause till the next redraw.
waitForPaint();

// return time taken to do redraw in ms
return System.currentTimeMillis() - t;
}

private void updateModel() throws Exception {
gameHandler.update();
}

private void drawModelToImageBuffer() {
drawModel(imageBuffer.getGraphics());
}

private void drawModel(Graphics g) {
g.setColor(component.getBackground());
g.fillRect(0, 0, component.getWidth(), component.getHeight());
g.setColor(component.getForeground());
gameHandler.draw(g);
}

private void waitForPaint() {
try {
synchronized (redrawLock) {
redrawLock.wait();
}
} catch (InterruptedException e) {
}
}

private void resume() {
synchronized (redrawLock) {
redrawLock.notify();
}
}

public void paint(Graphics g) {
// paint the buffered image to the graphics
g.drawImage(imageBuffer, 0, 0, component);

// resume the game loop
resume();
}

public static class SnakeComponent extends JPanel {

private final SnakeGame game;

public SnakeComponent(SnakeGame game) {
this.game = game;
}

@Override
protected void paintComponent(Graphics g) {
game.paint(g);
}
}

public static void main(String[] args) {
arguments = args;
java.awt.EventQueue.invokeLater(() -> {
SnakeGame game = new SnakeGame();
SnakeComponent component = new SnakeComponent(game);

gameHandler = new GameHandler(game);

component.setPreferredSize(new java.awt.Dimension(FRAME_WIDTH, FRAME_HEIGHT));
JFrame frame = new JFrame();

frame.setTitle("OpenOSRS Snake");
frame.setResizable(false);

frame.addKeyListener(gameHandler);
frame.setFocusable(true);
frame.setFocusTraversalKeysEnabled(false);

frame.setLayout(new BorderLayout());
frame.getContentPane().add(component, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();

game.start(component);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.thatgamerblue.snake;

public class SnakePart {
public SnakePart next;
int x, y;
}
Loading

0 comments on commit aa1f0a2

Please sign in to comment.