Skip to content

Instantly share code, notes, and snippets.

@SamuelDavis
Last active September 27, 2017 01:29
Show Gist options
  • Select an option

  • Save SamuelDavis/a7a3ada4c077850d24aef20d72f09c8f to your computer and use it in GitHub Desktop.

Select an option

Save SamuelDavis/a7a3ada4c077850d24aef20d72f09c8f to your computer and use it in GitHub Desktop.
Will Thimbleby's tiny roguelike (normal version): http://will.thimbleby.net/roguelike/
import javax.swing.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Vector;
public class RogueApplet extends Applet {
static Font drawFont = new Font("Monospaced", Font.PLAIN, 14);
DrawPanel drawPanel;
int width = 50;
int height = 50;
char map[][] = new char[50][50];
char floorchar = '\u00B7';
Vector<RogueApplet.Monster> monsters = new Vector<RogueApplet.Monster>();
Character player;
Color whiteColor = new Color(255, 255, 255, 255);
int level = 0;
Monster mtypes[] = {
new Monster(3, 2, 'a', "ant"),
new Monster(5, 3, 'd', "dog"),
new Monster(7, 4, 'k', "kobol"),
new Monster(3, 6, 'g', "goblin"),
new Monster(10, 3, 'h', "hobbit"),
new Monster(7, 7, 'x', "xan"),
new Monster(15, 15, 'D', "dragon")};
public void tick() {
for (int i = 0; i < monsters.size(); i++) {
Monster m = monsters.elementAt(i);
m.ai();
}
}
public void genMap() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
map[x][y] = '#';
}
Vector<RogueApplet.Room> connectedRooms = new Vector<RogueApplet.Room>();
Vector<RogueApplet.Room> rooms = new Vector<RogueApplet.Room>();
int nrooms = (int) (Math.random() * 4) + 5;
for (int i = 0; i < nrooms; i++) {
rooms.add(new Room());
}
connectedRooms.add(new Room());
while (rooms.size() > 0) {
int a, b;
a = (int) (Math.random() * connectedRooms.size());
b = (int) (Math.random() * rooms.size());
connectedRooms.get(a).createPathTo(rooms.get(b));
connectedRooms.add(rooms.get(b));
rooms.remove(b);
}
// gold
for (int i = 0; i < (level + 10); i++) {
int x = (int) (Math.random() * width);
int y = (int) (Math.random() * height);
map[x][y] = '$';
}
// potions
for (int i = 0; i < 10; i++) {
int x = (int) (Math.random() * width);
int y = (int) (Math.random() * height);
map[x][y] = '!';
}
// down staircase
while (true) {
int x = (int) (Math.random() * width);
int y = (int) (Math.random() * height);
if (map[x][y] == floorchar) {
map[x][y] = '>';
break;
}
}
// monsters
int c = nrooms + (int) (Math.random() * (nrooms + ((float) level / 4)));
for (int i = 0; i < c; i++) {
int ind = (int) (Math.abs((Math.random() + Math.random() - 1)) * (level + 2));
if (ind >= mtypes.length) ind = mtypes.length - 1;
Monster t = mtypes[ind];
t = new Monster(t.health, t.strength, t.symbol, t.name);
t.ai_interest = Math.random() / (ind + 2);
monsters.add(t);
t.place();
}
level++;
}
public void init() {
setLayout(new BorderLayout());
drawPanel = new DrawPanel();
drawPanel.setPreferredSize(new Dimension(300, 300));
add(drawPanel, BorderLayout.CENTER);
genMap();
player = new Character();
monsters.add(player);
player.reset();
drawPanel.repaint();
}
public class DrawPanel extends JPanel implements KeyListener {
HashMap<java.lang.Character, TextLayout> textcache = new HashMap<java.lang.Character, TextLayout>();
public DrawPanel() {
addKeyListener(this);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
DrawPanel.this.requestFocusInWindow();
}
});
}
public boolean isFocusable() {
return true;
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Rectangle r = getBounds();
Rectangle2D rbounds = new Rectangle2D.Float(0f, 0f, (float) r.getWidth() - 1, (float) r.getHeight() - 1);
g2d.setColor(Color.black);
g2d.fill(rbounds);
FontRenderContext frc = g2d.getFontRenderContext();
if (player.health < 0) {
g.setColor(new Color(255, 0, 0, 255));
String txt = "Dead & buried on level " + level + " with $" + player.score;
TextLayout layout = new TextLayout(txt, drawFont, frc);
layout.draw(g2d, (600 / 11 - txt.length()) * 11 / 2, (height + 1) * 11 / 2);
return;
}
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Color tileColor;
char c = map[x][y];
if (player.seen[x][y] == false || c == ' ') continue;
// lighting
float light = 1;
if (player.sees[x][y]) {
int dx = player.x - x;
int dy = player.y - y;
float rr = (dx * dx + dy * dy);
light = rr > 1 ? 0.5f + 4 / (rr - 1) : 1;
if (light > 1) light = 1;
} else
light = 0.25f;
// colouring
if (c == '$')
tileColor = new Color(1.0f, 1.0f, 0, 1.0f);
else if (c == ',' || c == ';' || c == '\'' || c == '`')
tileColor = new Color(1.0f, 0, 0, 1.0f);
else if (c == '!')
tileColor = new Color(0, 1.0f, 0, 1.0f);
else
tileColor = new Color(1.0f, 1.0f, 1.0f, 1.0f);
if (player.sees[x][y]) {
for (Monster m : monsters) {
if (m.x == x && m.y == y) {
tileColor = new Color(1.0f, 0, 0, 1.0f);
c = m.symbol;
if (c == '@')
tileColor = new Color(0, 0, 1.0f, 1.0f);
break;
}
}
}
tileColor = new Color(tileColor.getRed() / 255 * light, tileColor.getGreen() / 255 * light, tileColor.getBlue() / 255 * light, tileColor.getAlpha() / 255);
// drawing
g.setColor(tileColor);
TextLayout layout = (TextLayout) textcache.get(c);
if (layout == null)
layout = new TextLayout("" + c, drawFont, frc);
textcache.put(c, layout);
layout.draw(g2d, x * 11, y * 11);
}
}
// draw player info
g.setColor(whiteColor);
TextLayout layout = new TextLayout("Score: " + player.score, drawFont, frc);
layout.draw(g2d, 1 * 11, (height + 1) * 11);
layout = new TextLayout("Lvl: " + level, drawFont, frc);
layout.draw(g2d, 1 * 11, (height + 2) * 11);
layout = new TextLayout("Health: " + player.health + "/" + player.maxhealth, drawFont, frc);
layout.draw(g2d, 16 * 11, (height + 1) * 11);
layout = new TextLayout("Expr: " + (1 + player.experience / 20) + ":" + player.experience, drawFont, frc);
layout.draw(g2d, 16 * 11, (height + 2) * 11);
// status
for (int i = 0; i < 3; i++) {
g.setColor(player.statusColor[i]);
layout = new TextLayout(player.status[i], drawFont, frc);
layout.draw(g2d, 30 * 11, (height + i + 1) * 11);
}
}
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_Y:
case KeyEvent.VK_NUMPAD7:
player.move(-1, -1);
break;
case KeyEvent.VK_K:
case KeyEvent.VK_NUMPAD8:
case KeyEvent.VK_UP:
player.move(0, -1);
break;
case KeyEvent.VK_U:
case KeyEvent.VK_NUMPAD9:
player.move(1, -1);
break;
case KeyEvent.VK_L:
case KeyEvent.VK_NUMPAD6:
case KeyEvent.VK_RIGHT:
player.move(1, 0);
break;
case KeyEvent.VK_N:
case KeyEvent.VK_NUMPAD3:
player.move(1, 1);
break;
case KeyEvent.VK_J:
case KeyEvent.VK_NUMPAD2:
case KeyEvent.VK_DOWN:
player.move(0, 1);
break;
case KeyEvent.VK_B:
case KeyEvent.VK_NUMPAD1:
player.move(-1, 1);
break;
case KeyEvent.VK_H:
case KeyEvent.VK_NUMPAD4:
case KeyEvent.VK_LEFT:
player.move(-1, 0);
break;
case KeyEvent.VK_PERIOD:
case KeyEvent.VK_NUMPAD5:
player.move(0, 0);
break;
}
if (e.getKeyChar() == '>' && map[player.x][player.y] == '>')
player.goDown();
repaint();
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
class Character extends Monster {
boolean seen[][] = new boolean[50][50];
boolean sees[][] = new boolean[50][50];
int score = 0;
int rest = 0;
int seeRadius = 7;
String status[] = {" ", " ", " "};
Color statusColor[] = {whiteColor, whiteColor, whiteColor};
public Character() {
super(20, 5, '@', "player");
}
public void reset() {
super.place();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
seen[x][y] = false;
}
see();
}
public void setStatus(String s, Color c) {
for (int i = 1; i >= 0; i--) {
statusColor[i + 1] = statusColor[i];
status[i + 1] = status[i];
}
status[0] = s;
statusColor[0] = c;
}
public void see() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
sees[x][y] = false;
}
for (int yy = -seeRadius; yy < seeRadius; yy++) {
if (y + yy < 0 || y + yy >= height) continue;
for (int xx = -seeRadius; xx < seeRadius; xx++) {
if (xx * xx + yy * yy > seeRadius * seeRadius) continue;
if (x + xx < 0 || x + xx >= width) continue;
if (y + yy < 0 || y + yy >= height) continue;
if (lineOfSight(x + xx, y + yy))
sees[x + xx][y + yy] = seen[x + xx][y + yy] = true;
}
}
}
public void gainExperience(int e) {
if (experience / 20 < (experience + e) / 20) {
setStatus("Gained a new level.", new Color(255, 255, 0, 255));
maxhealth += 2;
}
experience += e;
}
public void damage(int d, Monster m) {
super.damage(d, m);
setStatus("Hurt by an " + m.name + ".", whiteColor);
}
public void move(int dx, int dy) {
super.move(dx, dy);
if (map[x][y] == '$') {
map[x][y] = floorchar;
int dscore = (int) (Math.random() * 20) + 2;
setStatus("Got $" + dscore + ".", whiteColor);
score += dscore;
}
if (map[x][y] == '!') {
map[x][y] = floorchar;
health += (int) (Math.random() * 5 + 5);
if (health > maxhealth) health = maxhealth;
setStatus("Drank a healing potion.", whiteColor);
}
if (health < maxhealth)
rest++;
if (rest > 20) {
rest = 0;
health++;
}
see();
tick();
drawPanel.repaint();
}
public void goDown() {
monsters.removeAllElements();
monsters.add(player);
genMap();
reset();
setStatus("Went downstairs.", whiteColor);
}
public void ai() {
}
}
class Monster {
int x = 10, y = 10;
char symbol = 'a';
int health, maxhealth;
int strength;
String name;
int experience;
double ai_interest = 0.3;
int tx, ty;
boolean memory = false;
public Monster(int h, int str, char s, String n) {
maxhealth = health = h;
strength = str;
symbol = s;
name = n;
experience = 0;
}
public void place() {
while (true) {
x = (int) (Math.random() * width);
y = (int) (Math.random() * height);
if (map[x][y] == floorchar)
break;
}
}
public boolean lineOfSight(float x2, float y2) {
// pick corner nearest monster
if (x2 < x) x2 += 0.5f;
else if (x2 > x) x2 -= 0.5f;
if (y2 < y) y2 += 0.5f;
else if (y2 > y) y2 -= 0.5f;
float dx = x2 - x;
float dy = y2 - y;
float l = Math.max(Math.abs(dx), Math.abs(dy));
dx /= l;
dy /= l;
float xx = x;
float yy = y;
while (l > 0) {
int ix = (int) (xx + 0.5);
int iy = (int) (yy + 0.5);
if (x2 == ix && y2 == iy) break;
if (!(x == ix && y == iy) && map[ix][iy] == '#')
return false;
xx += dx;
yy += dy;
l--;
}
return true;
}
public void gainExperience(int e) {
experience += e;
}
public void damage(int d, Monster m) {
health -= d;
if (health < 0) {
// death
char fl = name.charAt(0);
player.setStatus(((fl == 'a' /*...*/) ? "An " : "A ") + name + " dies.", whiteColor);
monsters.remove(this);
// experience
m.gainExperience((int) (Math.random() * strength));
}
// carnage
if (map[x][y] == floorchar) {
switch ((int) (Math.random() * 3)) {
case 0:
map[x][y] = ',';
break;
case 1:
map[x][y] = '\'';
break;
case 2:
map[x][y] = '`';
break;
}
}
}
public void move(int dx, int dy) {
if (dx == 0 && dy == 0) return;
if (x + dx < 0 || x + dx >= width) return;
if (y + dy < 0 || y + dy >= height) return;
if (map[x + dx][y + dy] == '#') return;
// if walk into a monster -- do damage
for (Monster m : monsters) {
if (m.x == x + dx && m.y == y + dy) {
m.damage((int) (Math.random() * (strength + (experience / 20))), this);
return;
}
}
x += dx;
y += dy;
}
public void ai() {
// if can see/remember player move towards them
boolean see = lineOfSight(player.x, player.y);
if ((see || memory) && Math.random() > ai_interest) {
if (see) {
memory = true;
tx = player.x;
ty = player.y;
}
int dx = tx - x;
int dy = ty - y;
// attempts to move straight or diagonally
int ddx = dx > 0 ? 1 : dx < 0 ? -1 : 0;
int ddy = dy > 0 ? 1 : dy < 0 ? -1 : 0;
int attempt[][] = new int[3][2];
for (int c = 0; c < 3; c++) {
attempt[c][0] = ddx;
attempt[c][1] = ddy;
}
if (dx * dx > dy * dy) {
attempt[1][1] = 0;
attempt[2][0] = 0;
} else {
attempt[1][0] = 0;
attempt[2][1] = 0;
}
for (int c = 0; c < 3; c++) {
ddx = attempt[c][0];
ddy = attempt[c][1];
if (map[x + ddx][y + ddy] != '#') break;
}
move(ddx, ddy);
} else
move((int) (Math.random() * 3) - 1, (int) (Math.random() * 3) - 1);
}
}
class Room {
int x, y, w, h;
public Room() {
while (true) {
x = (int) (Math.random() * (width - 13)) + 2;
y = (int) (Math.random() * (height - 13)) + 2;
w = (int) (Math.random() * 10) + 3;
h = (int) (Math.random() * 10) + 3;
boolean clear = true;
for (int yy = y - 1; yy < y + h + 1; yy++) {
for (int xx = x - 1; xx < x + w + 1; xx++) {
if (xx >= width || yy >= height || map[xx][yy] == floorchar) {
clear = false;
break;
}
}
if (!clear) break;
}
if (!clear) continue;
for (int yy = y; yy < y + h; yy++) {
for (int xx = x; xx < x + w; xx++) {
map[xx][yy] = floorchar;
}
}
break;
}
}
public void createPathTo(Room b) {
int x = (int) (Math.random() * (w)) + this.x;
int y = (int) (Math.random() * (h)) + this.y;
int x2 = (int) (Math.random() * (b.w)) + b.x;
int y2 = (int) (Math.random() * (b.h)) + b.y;
int dx = x2 - x;
int dy = y2 - y;
dx = dx > 0 ? 1 : (dx < 0 ? -1 : 0);
dy = dy > 0 ? 1 : (dy < 0 ? -1 : 0);
boolean horizontal = (Math.random() > 0.5);
while (x != x2 || y != y2) {
if (y == y2 || (horizontal && x != x2))
x += dx;
else
y += dy;
if (!(x > 0 && y > 0 && x < width && y < height)) break;
map[x][y] = floorchar;
if (Math.random() < 0.1)
horizontal = !horizontal;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment