Boulderdash
Java Source:
/*================================
BOULDERDASH
-------------
Dave Koelle
August 2000
http://www.ultranet.com/~dkoelle
Sound has been disabled for this
version to make the applet load
faster. To bring back sound,
uncomment parts of redrawAndSound(),
redrawCoords(), init(), and three
instances of moveSound[].play()
=================================*/
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import java.lang.Thread;
import java.io.*;
import java.util.StringTokenizer;
import java.util.Vector;
class Coord {
int x,y;
public Coord() {
x = 0;
y = 0;
}
public Coord(int x, int y) {
this.x = x;
this.y = y;
}
}
class BotPosition {
int x,y;
int heading,direction;
public BotPosition() {
x = 0;
y = 0;
heading = 1;
direction = 0;
}
public BotPosition(int x, int y, int heading, int direction) {
this.x = x;
this.y = y;
this.heading = heading;
this.direction = direction;
}
}
public class Boulderdash extends Applet implements Runnable {
private static final String VERSION = "1.0";
private static final String ID = "200009131200";
Vector redraw = new Vector();
Vector sounds = new Vector();
Graphics g;
Thread botThread;
boolean paused;
private static final int SLEEP_DELAY = 2750;
int maxtimer=1,timer,level;
int score,accumulatedScore,scorePerDiamond=5,scorePerExtraDiamond=10;
public static final int UP = 0;
public static final int RIGHT = 1;
public static final int DOWN = 2;
public static final int LEFT = 3;
// Control buttons
private Button buttonPrev = new Button("< Prev");
private Button buttonRestart = new Button("Restart");
private Button buttonNext = new Button("Next >");
// Game variables
Coord coordPlayer,coordExit;
int numNeededDiamonds,numFulfilledDiamonds;
boolean levelDone,playerAlmostDead,playerDead;
// Arrays to handle input from APPLET tag
int[] arrNumNeededDiamonds,arrScorePerDiamond,arrScorePerExtraDiamond,arrMaxtimer;
String[] arrLevelTitle,arrLevelAuthor,arrImageFilename;
// Image and icon data
private Image[] img;
private static final int NUM_IMAGES = 15;
private static final String imgRep = ".:#WMPO+*BFA][R";
private String imageFilename = "images.gif";
private static int[][] imgCoord =
{{ 0, 0,20,20}, // Space
{ 20, 0,20,20}, // Dirt
{ 40, 0,20,20}, // Titanium Wall
{ 0,20,20,20}, // Brick Wall
{ 20,20,20,20}, // Magic Wall
{ 40,20,20,20}, // Player
{ 0,40,20,20}, // Rock
{ 20,40,20,20}, // Diamond
{ 40,40,20,20}, // Explosion
{ 0,60,20,20}, // Butterfly
{ 20,60,20,20}, // Firefly
{ 40,60,20,20}, // Amoeba
{ 0,80,20,20}, // Closed Exit
{ 20,80,20,20}, // Open Exit
{ 40,80,20,20}}; // Robot (not programmed)
// Sound data
private static final int NUM_SOUNDS = 13;
private static final int MOVE_MAGICWALL_SOUND = 0;
private static final int MOVE_ROCK_SOUND = 1;
private static final int MOVE_DIAMOND_SOUND = 2;
private static final int MOVE_OPENEXIT_SOUND = 3;
private static final int MOVE_AMOEBA_SOUND = 4;
private static final int MOVE_SMASH_SOUND = 5;
private static final int MOVE_NULL_SOUND = 6;
private static final int MOVE_DIRT_SOUND = 7;
private static final int MOVE_PLAYERSMASH_SOUND = 8;
private static final int MOVE_DIED_SOUND = 9;
private static final int MOVE_CONGRATULATIONS_SOUND = 10;
private static final int MOVE_COMPLETE_SOUND = 11;
private static final int MOVE_ROCK2_SOUND = 12;
private static String[] soundFilename =
{"magicwall.wav", "rock.wav", "diamond.au", "openexit.wav", "amoeba.au", "smash.au",
"null.au", "dirt.au", "playersmash.au", "died.wav", "congrats.wav", "complete.wav", "rock2.wav"};
private AudioClip[] moveSound;
// Map and level data
private static final int X_DIMEN = 20;
private static final int Y_DIMEN = 20;
private static final int SQUARE_DIMEN = 20;
private static final int CON_DIMEN = 40;
private static final int VER_DIMEN = 12;
private static final int SIG_DIMEN = 24;
private char[][] levelMap;
private int NUM_LEVELS;
private String levelTitle,levelAuthor;
private String[][] levelData;
Vector bots = new Vector();
public void init() {
if (g == null) {
g = getGraphics();
}
this.requestFocus(); // Ask for keyboard focus so we get key events
// Get parameters from the HTML file
String numlevels = getParameter("levels");
if (numlevels == null) return;
NUM_LEVELS = Integer.parseInt(numlevels);
levelData = new String[NUM_LEVELS][Y_DIMEN];
arrNumNeededDiamonds = new int[NUM_LEVELS];
arrScorePerDiamond = new int[NUM_LEVELS];
arrScorePerExtraDiamond = new int[NUM_LEVELS];
arrMaxtimer = new int[NUM_LEVELS];
arrLevelTitle = new String[NUM_LEVELS];
arrLevelAuthor = new String[NUM_LEVELS];
arrImageFilename = new String[NUM_LEVELS];
for (int i=0; i<NUM_LEVELS; i++) {
for (int u=0; u<Y_DIMEN; u++) {
levelData[i][u] = getParameter("LEVEL_DATA_"+(i+1)+"_"+(u+1));
}
arrNumNeededDiamonds[i] = Integer.parseInt(getParameter("NEEDED_DIAMONDS_"+(i+1)));
arrScorePerDiamond[i] = Integer.parseInt(getParameter("POINTS_PER_DIAMOND_"+(i+1)));
arrScorePerExtraDiamond[i] = Integer.parseInt(getParameter("POINTS_PER_EXTRA_DIAMOND_"+(i+1)));
// arrMaxtimer[i] = Integer.parseInt(getParameter("MAX_TIMER_"+(i+1)));
arrLevelTitle[i] = getParameter("LEVEL_TITLE_"+(i+1));
if (arrLevelTitle[i] == null) arrLevelTitle[i] = "Unnamed";
arrLevelAuthor[i] = getParameter("LEVEL_AUTHOR_"+(i+1));
if (arrLevelAuthor[i] == null) arrLevelAuthor[i] = "Anonymous";
arrImageFilename[i] = getParameter("IMAGEFILE_"+(i+1));
if (arrImageFilename[i] == null) arrImageFilename[i] = "images.gif";
}
// Load up all of the sounds
/* CURRENTLY DISABLED
moveSound = new AudioClip[NUM_SOUNDS];
for (int i=0; i<NUM_SOUNDS; i++) {
moveSound[i] = getAudioClip(getCodeBase(), soundFilename[i]);
}
*/
// Set up player variables
coordPlayer = new Coord();
coordExit = new Coord();
// Just in case someone forgets to define these, let's make them exist
coordPlayer.x = 2; coordPlayer.y = 2;
coordExit.x = 1; coordExit.y = 1;
// Set up the map
levelMap = new char[X_DIMEN][];
for (int i=0; i<X_DIMEN; i++) {
levelMap[i] = new char[Y_DIMEN];
}
setLayout(new BorderLayout());
Panel p = new Panel();
p.add(buttonPrev);
p.add(buttonRestart);
p.add(buttonNext);
add("South", p);
level = 1;
setupLevel(level);
}
private void loadImages()
{
// Break up source image into individual images
// First, load the large image
Image sourceImage;
MediaTracker tracker = new MediaTracker(this);
sourceImage = getImage(getCodeBase(), imageFilename);
tracker.addImage(sourceImage,0);
// Now break it up into pieces
img = new Image[NUM_IMAGES];
for (int i=0; i<NUM_IMAGES; i++) {
img[i] = splitSourceImage(sourceImage,imgCoord[i]);
tracker.addImage(img[i], i+1);
}
// Wait for all of the image pieces to get into memory
try {
tracker.waitForAll();
} catch (InterruptedException e) {
System.out.println("Boulderdash.init() caught "+e);
e.printStackTrace();
}
}
private Image splitSourceImage(Image sourceImage, int[] dimen)
{
ImageFilter filter;
ImageProducer producer;
filter = new CropImageFilter(dimen[0],dimen[1],dimen[2],dimen[3]);
producer = new FilteredImageSource(sourceImage.getSource(), filter);
return (createImage(producer));
}
private void setupLevel(int level) {
g.setColor(Color.black);
g.fillRect(0,0,X_DIMEN*SQUARE_DIMEN,Y_DIMEN*SQUARE_DIMEN);
levelTitle = new String(arrLevelTitle[level-1]);
levelAuthor = new String(arrLevelAuthor[level-1]);
numNeededDiamonds = arrNumNeededDiamonds[level-1];
scorePerDiamond = arrScorePerDiamond[level-1];
scorePerExtraDiamond = arrScorePerExtraDiamond[level-1];
// maxtimer = arrMaxtimer[level-1];
imageFilename = new String(arrImageFilename[level-1]);
/* Get the images*/
loadImages();
/* Set up the level */
levelDone = false;
playerAlmostDead = false;
playerDead = false;
bots = new Vector();
numFulfilledDiamonds = 0;
for (int y=0; y<Y_DIMEN; y++) {
for (int x=0; x<X_DIMEN; x++) {
levelMap[x][y] = levelData[level-1][y].charAt(x);
drawCoord(x,y);
if ((levelMap[x][y] == '[') || (levelMap[x][y] == ']')) {
coordExit.x = x;
coordExit.y = y;
} else
if (levelMap[x][y] == 'P') {
coordPlayer.x = x;
coordPlayer.y = y;
} else
if ((levelMap[x][y] == 'B') || (levelMap[x][y] == 'F')) {
int dir = (levelMap[x][y] == 'F')?0:1;
BotPosition botpos = new BotPosition(x,y,1,dir);
bots.addElement(botpos);
}
}
}
updateStatus();
}
public void start() {
if ((botThread == null) && (paused == false)) {
botThread = new Thread(this);
botThread.start();
}
}
public void run() {
// This will take care of animation
if (paused) {
return;
}
boolean botSmash,rockSmash,amoebaSmash,explosionsQuenched=false;
Coord smashCoord = new Coord();
char me = '.';
while (true) {
if (playerAlmostDead && explosionsQuenched) {
for (int y=Y_DIMEN-2; (y>=0) && (explosionsQuenched); y--) {
for (int x=0; (x<X_DIMEN) && (explosionsQuenched); x++) {
if (levelMap[x][y] == '*') {
explosionsQuenched = false;
}
}
}
if (explosionsQuenched) {
playerAlmostDead = false;
playerDead = true;
}
}
explosionsQuenched = false;
for (int y=Y_DIMEN-2; y>=0; y--) {
for (int x=0; x<X_DIMEN; x++) {
botSmash = false;
rockSmash = false;
amoebaSmash = false;
// Is this item smashed by amoeba?
if (((y-1 > 0) && (levelMap[x][y-1] == 'A')) ||
((x+1 < X_DIMEN) && (levelMap[x+1][y] == 'A')) ||
((y+1 < Y_DIMEN) && (levelMap[x][y+1] == 'A')) ||
((x-1 > 0) && (levelMap[x-1][y] == 'A')) &&
((levelMap[x][y] == 'B') || (levelMap[x][y] == 'F'))) {
amoebaSmash = true;
smashCoord = new Coord(x,y);
}
// Quench all explosions
if (levelMap[x][y] == '*') {
levelMap[x][y] = '.';
redrawAndSound(new Coord(x,y),MOVE_NULL_SOUND);
explosionsQuenched = true;
} else
// If, for any reason, the player is dead, restart the level
// Do this AFTER the explosions take place
if (playerDead) {
// paused = true;
// All done with the level! Give a little pause...
niceBanner("Oh no, you've died!");
/* moveSound[MOVE_DIED_SOUND].play();*/
try {
Thread.sleep(SLEEP_DELAY);
} catch (Exception e) {
e.printStackTrace();
}
score = accumulatedScore;
setupLevel(level);
paused = false;
}
// Is the player standing next to a bot?
if (levelMap[x][y] == 'P') {
if (((y-1 > 0) && ((levelMap[x][y-1] == 'B') || (levelMap[x][y-1] == 'F'))) ||
((x+1 < X_DIMEN) && ((levelMap[x+1][y] == 'B') || (levelMap[x+1][y] == 'F'))) ||
((y+1 < Y_DIMEN) && ((levelMap[x][y+1] == 'B') || (levelMap[x][y+1] == 'F'))) ||
((x-1 > 0) && ((levelMap[x-1][y] == 'B') || (levelMap[x-1][y] == 'F')))) {
botSmash = true;
smashCoord = new Coord(x,y);
playerAlmostDead = true;
}
} else
// Work with rocks and diamonds
if ((levelMap[x][y] == 'O') || (levelMap[x][y] == '+') || (amoebaSmash || botSmash)) {
if (levelMap[x][y] == 'O') {
me = 'O';
} else
if (levelMap[x][y] == '+') {
me = '+';
} else {
me = '?';
}
// Gravity first...
if ((me != '?') && (levelMap[x][y+1] == '.')) {
levelMap[x][y+1] = me;
levelMap[x][y] = '.';
redrawAndSound(new Coord(x,y),MOVE_ROCK_SOUND);
redrawAndSound(new Coord(x,y+1),MOVE_ROCK2_SOUND);
if ((y+2 < Y_DIMEN) && ((levelMap[x][y+2] == 'B') || (levelMap[x][y+2] == 'F') || (levelMap[x][y+2] == 'P'))) {
rockSmash = true;
smashCoord = new Coord(x,y+2);
}
} else
// ... then handle stack collapsing
if ((me != '?') && ((levelMap[x][y+1] == 'O') || (levelMap[x][y+1] == '+'))) {
if ((levelMap[x+1][y+1] == '.') && (levelMap[x+1][y] == '.')) {
levelMap[x+1][y+1] = me;
levelMap[x][y] = '.';
redrawAndSound(new Coord(x,y),MOVE_ROCK2_SOUND);
redrawAndSound(new Coord(x+1,y+1),MOVE_ROCK_SOUND);
if ((y+2 < Y_DIMEN) && ((levelMap[x+1][y+2] == 'B') && (levelMap[x+1][y+2] == 'F') || (levelMap[x+1][y+2] == 'P'))) {
rockSmash = true;
smashCoord = new Coord(x+1,y+2);
}
} else
if ((levelMap[x-1][y+1] == '.') && (levelMap[x-1][y] == '.')) {
levelMap[x-1][y+1] = me;
levelMap[x][y] = '.';
redrawAndSound(new Coord(x,y),MOVE_ROCK2_SOUND);
redrawAndSound(new Coord(x-1,y+1),MOVE_ROCK2_SOUND);
if ((y+2 < Y_DIMEN) && ((levelMap[x-1][y+2] == 'B') && (levelMap[x-1][y+2] == 'F') || (levelMap[x-1][y+2] == 'P'))) {
rockSmash = true;
smashCoord = new Coord(x-1,y+2);
}
}
}
}
// Smash a butterfly, fly, or player
label1: if ((botSmash || amoebaSmash || rockSmash) && ((levelMap[smashCoord.x][smashCoord.y] == 'B') || (levelMap[smashCoord.x][smashCoord.y] == 'F') || (levelMap[smashCoord.x][smashCoord.y] == 'P'))) {
boolean diamond = (levelMap[smashCoord.x][smashCoord.y] == 'B')?true:false;
if (levelMap[smashCoord.x][smashCoord.y] == 'P') {
if (botSmash || rockSmash) {
playerAlmostDead = true;
} else break label1;
}
for (int i=smashCoord.x-1;i<=smashCoord.x+1;i++) {
for (int u=smashCoord.y-1;u<=smashCoord.y+1;u++) {
if ((levelMap[i][u] == '.') || (levelMap[i][u] == ':') || (levelMap[i][u] == 'W') || (levelMap[i][u] == 'M') || (levelMap[i][u] == '+') || (levelMap[i][u] == 'A')
|| ((i==smashCoord.x) && (u==smashCoord.y-1)) || ((i==smashCoord.x) && (u==smashCoord.y)) && (i > 0) && (i < X_DIMEN) && (u > 0) && (u < Y_DIMEN)) {
if (diamond) {
levelMap[i][u]='+';
} else {
levelMap[i][u]='*';
}
redrawAndSound(new Coord(i,u),MOVE_SMASH_SOUND);
for (int b=0; b<bots.size(); b++) {
BotPosition bot = (BotPosition)bots.elementAt(b);
if ((bot.x == smashCoord.x) && (bot.y == smashCoord.y)) {
bots.removeElementAt(b);
}
}
}
}
}
}
// Go into a magic wall
if ((levelMap[x][y] == 'M') && (y > 1) && ((levelMap[x][y-1] == 'O') || (levelMap[x][y-1] == '+'))) {
if (levelMap[x][y+1] == '.') {
if (levelMap[x][y-1] == 'O') {
levelMap[x][y+1] = '+';
redrawAndSound(new Coord(x,y+1),MOVE_MAGICWALL_SOUND);
} else {
levelMap[x][y+1] = 'O';
redrawAndSound(new Coord(x,y+1),MOVE_MAGICWALL_SOUND);
}
}
levelMap[x][y-1] = '.';
redrawAndSound(new Coord(x,y-1),MOVE_NULL_SOUND);
}
// Grow the amoeba
if (levelMap[x][y] == 'A') {
if (timer >= maxtimer) {
if (Math.random() < 0.10) {
int dx=0,dy=0;
if (Math.random() < 0.5) {
dx = (int)((Math.round(Math.random()*2))-1);
dy = 0;
} else {
dy = (int)((Math.round(Math.random()*2))-1);
dx = 0;
}
// Spread it only to places that are empty or have dirt
if ((x+dx < X_DIMEN) && (x+dx > 0) && (y+dy < Y_DIMEN) && (y+dy > 0)) {
if ((levelMap[x+dx][y+dy] == '.') || (levelMap[x+dx][y+dy] == ':')) {
levelMap[x+dx][y+dy] = 'A';
redrawAndSound(new Coord(x+dx,y+dy),MOVE_AMOEBA_SOUND);
}
}
}
}
}
} // for
} // for
// Move any Fireflies clockwise, and Butterflies counterclockwise
if (timer >= maxtimer) {
for (int b = 0; b < bots.size(); b++) {
BotPosition bot = (BotPosition)bots.elementAt(b);
Coord c = new Coord(bot.x,bot.y);
if (bot.heading == UP) {
if ((bot.direction == UP) && (levelMap[bot.x+1][bot.y] == '.')) {
bot.x++;
bot.heading = RIGHT;
} else
if ((bot.direction == RIGHT) && (levelMap[bot.x-1][bot.y] == '.')) {
bot.x--;
bot.heading = LEFT;
} else
if (levelMap[bot.x][bot.y-1] == '.') {
bot.y--;
} else {
bot.heading = DOWN;
}
}
if (bot.heading == RIGHT) {
if ((bot.direction == UP) && (levelMap[bot.x][bot.y+1] == '.')) {
bot.y++;
bot.heading = DOWN;
} else
if ((bot.direction == RIGHT) && (levelMap[bot.x][bot.y-1] == '.')) {
bot.y--;
bot.heading = UP;
} else
if (levelMap[bot.x+1][bot.y] == '.') {
bot.x++;
} else {
bot.heading = LEFT;
}
}
if (bot.heading == DOWN) {
if ((bot.direction == UP) && (levelMap[bot.x-1][bot.y] == '.')) {
bot.x--;
bot.heading = LEFT;
} else
if ((bot.direction == RIGHT) && (levelMap[bot.x+1][bot.y] == '.')) {
bot.x++;
bot.heading = RIGHT;
} else
if (levelMap[bot.x][bot.y+1] == '.') {
bot.y++;
} else {
bot.heading = UP;
}
}
if (bot.heading == LEFT) {
if ((bot.direction == UP) && (levelMap[bot.x][bot.y-1] == '.')) {
bot.y--;
bot.heading = UP;
} else
if ((bot.direction == RIGHT) && (levelMap[bot.x][bot.y+1] == '.')) {
bot.y++;
bot.heading = DOWN;
} else
if (levelMap[bot.x-1][bot.y] == '.') {
bot.x--;
} else {
bot.heading = RIGHT;
}
}
bots.setElementAt(bot,b);
if ((bot.x != c.x) || (bot.y != c.y)) {
levelMap[c.x][c.y] = '.';
levelMap[bot.x][bot.y] = (bot.direction==0)?'F':'B';
redrawAndSound(new Coord(c.x,c.y),MOVE_NULL_SOUND);
redrawAndSound(new Coord(bot.x,bot.y),MOVE_NULL_SOUND);
}
}
} // timer
// Check if amoeba is suffocated
boolean amoebaSuffocated = true;
for (int i=1; i<X_DIMEN-1 && amoebaSuffocated; i++) {
for (int u=1; u<Y_DIMEN-1 && amoebaSuffocated; u++) {
if (levelMap[i][u] == 'A') {
if ((levelMap[i][u-1] == '.') || (levelMap[i][u-1] == ':') ||
(levelMap[i-1][u] == '.') || (levelMap[i-1][u] == ':') ||
(levelMap[i][u+1] == '.') || (levelMap[i][u+1] == ':') ||
(levelMap[i+1][u] == '.') || (levelMap[i+1][u] == ':')) {
amoebaSuffocated = false;
}
}
}
}
// If the amoeba is suffocated, convert all amoeba into diamonds
if (amoebaSuffocated) {
for (int i=1; i<X_DIMEN-1; i++) {
for (int u=1; u<Y_DIMEN-1; u++) {
if (levelMap[i][u] == 'A') {
levelMap[i][u] = '+';
redrawAndSound(new Coord(i,u),MOVE_NULL_SOUND);
}
}
}
}
redrawCoords();
if (timer >= maxtimer) {
timer = 0;
}
timer++;
try {
Thread.sleep(150);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
} // while
} // run
public boolean action(Event evt, Object o) {
if ((evt.target == buttonPrev) && (level-1 > 0)) {
score = accumulatedScore;
setupLevel(--level);
} else
if (evt.target == buttonRestart) {
score = accumulatedScore;
setupLevel(level);
} else
if ((evt.target == buttonNext) && (level+1 < NUM_LEVELS+1)) {
score = accumulatedScore;
setupLevel(++level);
}
return true;
}
public boolean mouseDown(Event evt, int x, int y) {
if ((x > 0) && (x < X_DIMEN*SQUARE_DIMEN) && (y > Y_DIMEN*SQUARE_DIMEN+CON_DIMEN) && (y < Y_DIMEN*SQUARE_DIMEN+CON_DIMEN+SIG_DIMEN)) {
g.setFont(new Font("Helvetica",Font.BOLD|Font.ITALIC,14));
for (int i=0; i<3; i++) {
g.setColor(Color.magenta);
g.fillRect(0,Y_DIMEN*SQUARE_DIMEN+CON_DIMEN,X_DIMEN*SQUARE_DIMEN,SIG_DIMEN);
try { Thread.sleep(150); } catch (InterruptedException e) { e.printStackTrace(); }
g.setColor(Color.blue);
g.fillRect(0,Y_DIMEN*SQUARE_DIMEN+CON_DIMEN,X_DIMEN*SQUARE_DIMEN,SIG_DIMEN);
g.setColor(Color.yellow);
g.drawString("Click here to visit this applet's homepage!",45,(Y_DIMEN*SQUARE_DIMEN)+CON_DIMEN+16);
try { Thread.sleep(150); } catch (InterruptedException e) { e.printStackTrace(); }
}
try {
AppletContext ac = getAppletContext();
// PLEASE DO NOT CHANGE WHERE THIS URL POINTS!
ac.showDocument(new URL("http://www.ultranet.com/~dkoelle/Boulderdash"));
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
public boolean keyDown(Event evt, int keyCode) {
if (paused) {
return false;
}
Coord delta = new Coord();
Coord delta2 = new Coord();
Coord move = new Coord();
Coord move2 = new Coord();
boolean validMove = false;
// Interpret the keycode
if (keyCode == Event.UP) {
delta.x = 0; delta.y = -1;
} else
if (keyCode == Event.RIGHT) {
delta.x = 1; delta.y = 0;
} else
if (keyCode == Event.DOWN) {
delta.x = 0; delta.y = 1;
} else
if (keyCode == Event.LEFT) {
delta.x = -1; delta.y = 0;
} else
if (keyCode == Event.ESCAPE) {
playerAlmostDead = true;
levelMap[coordPlayer.x][coordPlayer.y] = '*';
redrawAndSound(coordPlayer,MOVE_PLAYERSMASH_SOUND);
}
// Make move relative to player
move.x = coordPlayer.x + delta.x;
move.y = coordPlayer.y + delta.y;
// Range checking on the move
if (move.x < 0) { move.x = 0; } else
if (move.x > X_DIMEN) { move.x = X_DIMEN; }
if (move.y < 0) { move.y = 0; } else
if (move.y > Y_DIMEN) { move.y = Y_DIMEN; }
// Create a "look-ahead" position
delta2.x = delta.x*2;
delta2.y = delta.y*2;
move2.x = coordPlayer.x + delta2.x;
move2.y = coordPlayer.y + delta2.y;
// Moving into an empty space? "I'll allow it!" -- Mills Lane
if ((levelMap[move.x][move.y] == '.') || (levelMap[move.x][move.y] == ':')) {
if (levelMap[move.x][move.y] == '.') {
redrawAndSound(move,MOVE_NULL_SOUND); // SPACE sound
} else {
redrawAndSound(move,MOVE_NULL_SOUND); // DIRT sound
}
validMove = true;
} else
// Pushing a rock?
if ((levelMap[move.x][move.y] == 'O') && (move2.y != move.y-1)) {
if (((move2.x >= 0) && (move2.x <= X_DIMEN)) &&
((move2.y >= 0) && (move2.y <= Y_DIMEN))) {
if (levelMap[move2.x][move2.y] == '.') {
// "look-ahead" is an empty space. Move the rock.
levelMap[move2.x][move2.y] = levelMap[move.x][move.y];
redrawAndSound(move2,MOVE_ROCK_SOUND);
redrawAndSound(move,MOVE_NULL_SOUND);
validMove = true;
}
}
} else
// Grabbing a Diamond?
// Add one to the Number of Fulfilled Diamonds,
// and see if the exit should be opened
if (levelMap[move.x][move.y] == '+') {
numFulfilledDiamonds++;
score += (numFulfilledDiamonds<numNeededDiamonds ? scorePerDiamond : scorePerExtraDiamond);
updateScore();
redrawAndSound(move,MOVE_DIAMOND_SOUND);
if (numFulfilledDiamonds == numNeededDiamonds) {
levelMap[coordExit.x][coordExit.y] = '[';
redrawAndSound(coordExit,MOVE_OPENEXIT_SOUND);
}
validMove = true;
} else
// If moving into an exit, make sure it's open!
if (levelMap[move.x][move.y] == '[') {
redrawAndSound(move,MOVE_NULL_SOUND);
validMove = true;
}
// If the player is holding the shift key, don't move the character
if (evt.shiftDown() && validMove) {
levelMap[move.x][move.y] = '.';
validMove = false;
}
if (validMove) {
levelMap[move.x][move.y] = 'P';
levelMap[coordPlayer.x][coordPlayer.y] = '.';
drawCoord(coordPlayer.x,coordPlayer.y);
coordPlayer.x = move.x;
coordPlayer.y = move.y;
}
// Redraw coordinates that need to be redrawn
redrawCoords();
// Is the level over?
if ((coordPlayer.x == coordExit.x) &&
(coordPlayer.y == coordExit.y)) {
paused = true;
levelDone = true;
if (level == NUM_LEVELS) {
niceBanner("All levels complete!");
/* moveSound[MOVE_COMPLETE_SOUND].play();*/
} else {
niceBanner("Congratulations!");
/* moveSound[MOVE_CONGRATULATIONS_SOUND].play();*/
}
// All done with the level! Give a little pause...
try {
Thread.sleep(SLEEP_DELAY);
} catch (Exception e) {
e.printStackTrace();
}
accumulatedScore = score;
paused = false;
if (level+1 < NUM_LEVELS+1) {
level++;
} else {
level = 1;
}
setupLevel(level);
}
return true;
}
public void paint(Graphics g) {
for (int x=0; x<X_DIMEN; x++) {
for (int y=0; y<Y_DIMEN; y++) {
drawCoord(x,y);
}
}
updateStatus();
}
public void updateStatus() {
g.setColor(Color.lightGray);
g.fillRect(0,Y_DIMEN*SQUARE_DIMEN,X_DIMEN*SQUARE_DIMEN,CON_DIMEN);
// Game info
g.setColor(Color.black);
g.setFont(new Font("Helvetica",Font.BOLD,14));
g.drawString(levelTitle,12,(Y_DIMEN*SQUARE_DIMEN)+12);
g.setFont(new Font("Helvetica",Font.ITALIC,12));
g.drawString("by "+levelAuthor,12,(Y_DIMEN*SQUARE_DIMEN)+26);
g.setFont(new Font("Helvetica",Font.BOLD,10));
g.drawString("Level "+level+" / "+NUM_LEVELS,12,(Y_DIMEN*SQUARE_DIMEN)+38);
// Second column of Game info
g.setFont(new Font("Helvetica",Font.PLAIN,12));
g.drawString("Score: "+score,200,(Y_DIMEN*SQUARE_DIMEN)+12);
g.drawString("Diamonds: "+numFulfilledDiamonds+" / "+(numFulfilledDiamonds<numNeededDiamonds ? numNeededDiamonds : 0),200,(Y_DIMEN*SQUARE_DIMEN)+24);
g.drawString("Diamond Points: "+(numFulfilledDiamonds<numNeededDiamonds ? scorePerDiamond : scorePerExtraDiamond),200,(Y_DIMEN*SQUARE_DIMEN)+36);
// "Click here for the applet homepage" (SIG)
g.setColor(Color.blue);
g.fillRect(0,Y_DIMEN*SQUARE_DIMEN+CON_DIMEN,X_DIMEN*SQUARE_DIMEN,SIG_DIMEN);
g.setColor(Color.yellow);
g.setFont(new Font("Helvetica",Font.BOLD + Font.ITALIC,14));
g.drawString("Click here to visit this applet's homepage!",45,(Y_DIMEN*SQUARE_DIMEN)+CON_DIMEN+16);
// Version info
g.setColor(Color.black);
g.fillRect(0,Y_DIMEN*SQUARE_DIMEN+CON_DIMEN+SIG_DIMEN,X_DIMEN*SQUARE_DIMEN,VER_DIMEN);
g.setColor(Color.white);
g.setFont(new Font("Helvetica",Font.BOLD,10));
g.drawString("Java Boulderdash - Version "+VERSION+" - ID "+ID,55,(Y_DIMEN*SQUARE_DIMEN)+CON_DIMEN+SIG_DIMEN+10);
}
public void updateScore() {
g.setColor(Color.lightGray);
g.fillRect(200,Y_DIMEN*SQUARE_DIMEN,X_DIMEN*SQUARE_DIMEN,CON_DIMEN);
g.setColor(Color.black);
g.setFont(new Font("Helvetica",Font.PLAIN,12));
g.drawString("Score: "+score,200,(Y_DIMEN*SQUARE_DIMEN)+12);
g.drawString("Diamonds: "+numFulfilledDiamonds+" / "+(numFulfilledDiamonds<numNeededDiamonds ? numNeededDiamonds : 0),200,(Y_DIMEN*SQUARE_DIMEN)+24);
g.drawString("Diamond Points: "+(numFulfilledDiamonds<numNeededDiamonds ? scorePerDiamond : scorePerExtraDiamond),200,(Y_DIMEN*SQUARE_DIMEN)+36);
}
public void drawCoord(int x, int y)
{
int index;
for (index=0; index<imgRep.length(); index++) {
if (imgRep.charAt(index) == levelMap[x][y]) {
g.drawImage(img[index],x*SQUARE_DIMEN,y*SQUARE_DIMEN,null);
}
}
}
public void redrawCoords() {
// Redraw any coordinates that should be redrawn
try {
for (int i=0; i<redraw.size(); i++) {
Coord display = (Coord)redraw.elementAt(i);
drawCoord(display.x,display.y);
/*
if (i<sounds.size()) {
((AudioClip)sounds.elementAt(i)).play();
}
*/
}
} catch (Exception e) {
e.printStackTrace();
}
redraw.removeAllElements();
sounds.removeAllElements();
}
public void redrawAndSound(Coord coord, int sound) {
redraw.addElement(coord);
/*
sounds.addElement(moveSound[sound]);
*/
}
public void niceBanner(String msg) {
g.setColor(Color.darkGray);
g.fillRect(SQUARE_DIMEN*2,SQUARE_DIMEN*4,SQUARE_DIMEN*16,SQUARE_DIMEN*5);
g.setColor(Color.blue);
g.fillRect(SQUARE_DIMEN*3,SQUARE_DIMEN*5,SQUARE_DIMEN*14,SQUARE_DIMEN*3);
g.setColor(Color.yellow);
g.setFont(new Font("Helvetica",Font.BOLD|Font.ITALIC,20));
g.drawString(msg,SQUARE_DIMEN*6-10,SQUARE_DIMEN*7-5);
}
public String getAppletInfo() {
return("Boulderdash Applet, (C) 2000 by David Koelle\nInspired by the game, \"Super Boulderdash\"\n\n" +
"Please visit http://www.ultranet.com/~dkoelle/Boulderdash to find out\nhow to use and modify this applet!");
}
public String[][] getParameterInfo() {
return(new String[][] {{"LEVELS","int","Number of levels included in this .html file"},
{"LEVEL_NAME_x","String","Name of Level x"},
{"LEVEL_AUTHOR_x","String","Author of Level x"},
{"LEVEL_DATA_x_r","String","Map for Level x. 'r' is 1 - 20, representing a row of the map"},
{"NEEDED_DIAMONDS_x","int","Number of diamonds needed to open the exit for Level x"},
{"POINTS_PER_DIAMOND_x","int","Number of points awarded for getting a diamond for Level x"},
{"POINTS_PER_EXTRA_DIAMOND_x","int","Number of points awarded for getting a diamond after all needed diamonds have been obtained for Level x"},
// {"MAXTIMER","int","Cycle timer for monsters"},
{"IMAGEFILE_x","String","Filename of the image file to use for Level x"}});
}
} // Boulderdash
Back to Boulderdash
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.
|