import java.applet.*;
import java.awt.*;
import java.util.*;
/*
*----------------------------------------------------------------------
* Gunfighter.java
*/
/**
This class is the main program that controls the game.
To make communication with other classes easier,
some methods and variables are made static.
This is no limitation since we only have one instance of this class.
Some important variables are made public just to show up in this page.
*/
/*
*----------------------------------------------------------------------
*/
public class Gunfighter extends Applet implements Runnable {
/**
actor[0] - The Good (blue)
actor[1] - The Bad and Ugly (red)
*/
public static Player actor[] = new Player[2];
/**
This variable controls the behavior of the game.
The states are: loading, intro, new game, new duel,
action, player dead, between level and game over.
*/
public static int gameState = -1;
/**
This array contains the obstacles for the current playfield.
*/
public static Obstacle obstacles[];
/**
Array of arrays containing tripples with object types and coordinates.
*/
public final short[][] scenes = { {0,270,204, 1,369,204, 2,268,326, 3,495,329,
3,45,232, 5,194,77, 6,460,39, 6,514,14,
6,574,57, 7,313,121, 9,70,319},
{0,157,90, 1,455,318, 2,530,348, 3,116,313,
4,439,150, 5,401,10, 6,515,18, 7,551,291,
8,165,0, 9,130,224, 9,208,266},
{0,471,109, 1,365,285, 2,201,279, 3,305,112,
4,463,28, 6,127,248, 6,403,107, 7,168,110,
7,288,259, 7,553,258},
{0,642,78, 1,13,362, 2,20,133, 2,24,269,
7,160,163, 3,151,262, 9,650,136,4,408,0,
7,605,189, 6,562,137},
{0,592,371, 1,81,73, 6,323,75, 6,377,64,
7,397,165, 2,213,194, 4,434,265, 8,79,324,
9,191,297, 5,621,10,3,655,176},
{0,165,218, 1,324,75, 3,556,10, 2,506,39,
5,538,97, 4,577,182, 7,526,137, 6,192,106,
6,249,125, 7,305,159, 4,150,314},
{0,139,203, 1,436,74, 3,312,281, 6,339,0,
6,281,33, 7,231,321, 8,550,170, 4,349,170,
9,82,261, 9,113,75} };
/**
Playfield height.
Currently set to 440 pixels.
*/
public static final int screenHeight = 440;
/**
Playfield width.
Currently set to 740 pixels.
*/
public static final int screenWidth = 740;
/**
The sound effects.
0 - shoot | 1 - ricochet | 2 - click | 3 - reload |
4 - scream | 5 - prepare | 6 - intro |
*/
public static AudioClip sfx[] = new AudioClip[7];
/**
All the obstacle's textures and the bullet texture.
0 - blue ammo box | 1 - red ammo box | 2 - big rock left | 3 - small rock right |
4 - big rock right | 5 - small rock left | 6 - spuce | 7 - triange bush |
8 - round tree light | 9 - round tree dark | 10 - bullet |
*/
public static Image textures[] = new Image[11];
/*
* xcoords and ycoords defines the corners of all the obstacles.
*/
private final int[][] xcoords = { {0,0,7,64,71,71,64,7,0},
{0,0,7,64,71,71,64,7,0},
{21,54,62,38,9,0,7,21},
{58,38,11,4,0,1,8,14,27,33,40,49,58,58},
{41,55,62,53,24,0,8,41},
{0,0,9,18,25,31,44,50,57,58,54,47,20,0},
{3,28,47,28,28,24,24,3},
{5,37,62,40,40,33,33,5},
{7,11,21,36,50,61,65,61,50,36,21,11,7},
{6,10,22,39,55,67,72,67,55,39,22,10,6}};
private final int[][] ycoords = { {24,5,0,0,6,21,28,28,24},
{24,5,0,0,6,21,28,28,24},
{0,15,49,51,51,47,14,0},
{32,34,34,33,30,22,13,13,0,1,6,6,21,32},
{0,14,47,51,51,49,15,0},
{32,21,6,6,1,0,13,13,22,30,33,34,34,32},
{66,3,65,65,70,70,66,66},
{44,3,44,44,50,50,44,44},
{33,47,58,62,58,47,33,18,8,4,8,18,33},
{38,54,66,71,66,54,38,21,9,5,9,21,38}};
/*
* Contains the polygons made of xcoords and ycoords from above.
*/
private Polygon polygons[] = new Polygon[10];
/*
* Values that makes the actors diffent:
* 0,1 - Start coordinate.
* 2 - Which frame to start with.
* 3-6 - Hit rectangle.
* 7 - How many pixels the hit rectangle moves when the actor turns around.
* 8-11 - Walk rectangle.
* 12 - How many pixels the walk rectangle moves when the actor turns around.
* 13 - How many pixels the actor should move when he turns around.
* 14-15 - Width and heigth.
* 16 - Vertical offset to where the bullets start.
* 17 - Animation rate.
*/
private final int[][] actorProps = { {0,0, 1, 20,12,17,51, 3,
7,94,55,10, 17, 26,
80, 104, 39, 4},
{669,291, 5, 38,19,17,51, -1,
21,88,42,20, 9, 23,
71, 109, 46, 3} };
/*
* The background image.
*/
private Image wallPaper;
/*
* Background with the current scene obstacles.
*/
private Image bg;
/*
* The double buffer image.
*/
private Image offscreen;
/*
* Graphic contexts for bg and offscreen.
*/
private Graphics bgGC, offGC;
/*
* Which scene are we playing and which was the previous.
* See also newScene().
*/
private int scene = -1, prevScene;
/*
* Used in newScene().
*/
private Random rand = new Random();
private int speed = 50;
private Thread game = null;
/*
* The score.
*/
static int kills[] = {0, 0};
/*
* Number of actor that won the last duel.
*/
static int latestPoint = 0;
private Image actor0Frames[] = new Image[6];
private Image actor1Frames[] = new Image[6];
/*
* Colors used in status row.
*/
private Color blue = new Color(132, 132, 255);
private Color red = new Color(198, 0, 0);
/*
* 16 grey colours, used in intro.
*/
private Color tone[] = new Color[16];
private Font small,big;
/*
* Help pointers when fading text in the intro.
*/
private int toneIn = 0;
private int toneOut = 4095;
/*
*----------------------------------------------------------------------
*
* handleEvent
*/
/**
Takes care of user input.
*/
/*
*----------------------------------------------------------------------
*/
public boolean handleEvent(Event e) {
if (e.id == Event.WINDOW_DESTROY) {
System.exit(0);
} else if (gameState == 3) {
if (e.id == Event.KEY_ACTION) {
if (e.key == Event.UP) {
actor[1].userInput(1, true); // actor[1] up.
} else if (e.key == Event.DOWN) {
actor[1].userInput(3, true); // actor[1] down.
} else if (e.key == Event.LEFT) {
actor[1].userInput(4, true); // actor[1] left.
} else if (e.key == Event.RIGHT) {
actor[1].userInput(2, true); // actor[1] right.
}
} else if (e.id == Event.KEY_ACTION_RELEASE) {
if (e.key == Event.UP) {
actor[1].userInput(1, false);
} else if (e.key == Event.DOWN) {
actor[1].userInput(3, false);
} else if (e.key == Event.LEFT) {
actor[1].userInput(4, false);
} else if (e.key == Event.RIGHT) {
actor[1].userInput(2, false);
}
} else if (e.id == Event.KEY_PRESS) {
if (e.key == 'w' || e.key == 'W') { // actor[0] up.
actor[0].userInput(1, true);
} else if (e.key == 's' || e.key == 'S') { // actor[0] down.
actor[0].userInput(3, true);
} else if (e.key == 'a' || e.key == 'A') { // actor[0] left.
actor[0].userInput(4, true);
} else if (e.key == 'd' || e.key == 'D') { // actor[0] right.
actor[0].userInput(2, true);
} else if (e.key == '\t') { // actor[0] shoot.
actor[0].userInput(5, true);
} else if (e.key == '\n') { // actor[1] shoot.
actor[1].userInput(5, true);
} else if (e.key == 'i' || e.key == 'I') {
actor[1].userInput(1, true); // actor[1] up.
} else if (e.key == 'k' || e.key == 'K') {
actor[1].userInput(3, true); // actor[1] down.
} else if (e.key == 'j' || e.key == 'J') {
actor[1].userInput(4, true); // actor[1] left.
} else if (e.key == 'l' || e.key == 'L') {
actor[1].userInput(2, true); // actor[1] right.
}
} else if (e.id == Event.KEY_RELEASE) {
if (e.key == 'w' || e.key == 'W') {
actor[0].userInput(1, false);
} else if (e.key == 's' || e.key == 'S') {
actor[0].userInput(3, false);
} else if (e.key == 'a' || e.key == 'A') {
actor[0].userInput(4, false);
} else if (e.key == 'd' || e.key == 'D') {
actor[0].userInput(2, false);
} else if (e.key == 'i' || e.key == 'I') {
actor[1].userInput(1, false);
} else if (e.key == 'k' || e.key == 'K') {
actor[1].userInput(3, false);
} else if (e.key == 'j' || e.key == 'J') {
actor[1].userInput(4, false);
} else if (e.key == 'l' || e.key == 'L') {
actor[1].userInput(2, false);
}
}
} else if (e.id == Event.KEY_PRESS && e.key == ' ' &&
gameState == 0 ) {
/*
* Somebody pressed space in the intro,
* start the action!
*/
gameState=1;
} else {
return super.handleEvent(e);
}
return true;
}
/*
*----------------------------------------------------------------------
*
* init
*/
/**
Sets up some drawing areas, fonts, colours and builds the
obstacle polygons.
*/
/*
*----------------------------------------------------------------------
*/
public void init() {
speed = 50;
offscreen = createImage(screenWidth, screenHeight);
offGC = offscreen.getGraphics();
bg = createImage(screenWidth, screenHeight);
bgGC = bg.getGraphics();
gameState = -1;
small = new Font("Dialog", Font.BOLD, 16);
big = new Font("Dialog", Font.BOLD, 48);
int col = 0x000000;
for (int n = 0; n < 16; n ++) {
tone[n] = new Color(col);
col += 0x101010;
}
for (int n = 0; n < polygons.length; n++) {
polygons[n] = new Polygon(xcoords[n], ycoords[n], xcoords[n].length);
}
}
/*
*----------------------------------------------------------------------
*
* paint
*/
/**
Draws the offscreen buffer into the screen.
*/
/*
*----------------------------------------------------------------------
*/
public void paint(Graphics g) {
g.drawImage(offscreen, 0, 0, this);
}
/*
*----------------------------------------------------------------------
*
* run
*/
/**
The main loop of the game.
Sleep, update game, repaint and loop.
*/
/*
*----------------------------------------------------------------------
*/
public void run() {
int counter = 0;
while (game !=null) {
try {
Thread.sleep(speed);
} catch (InterruptedException e) {
}
switch (gameState) {
case -1: // Initialising the game.
repaint();
loadData();
gameState = 0;
break;
case 0: // Intro, wait for user to start game.
speed = 50;
counter = intro(counter);
break;
case 1: // New game.
sfx[6].stop();
counter = 0;
toneIn = 0;
toneOut = 4095;
offGC.setFont(small);
kills[0] = kills[1] = 0;
gameState = 2;
break;
case 2: // New duel.
newScene();
gameState = 3;
break;
case 3: // Game in action!
speed = 37;
actor[0].update();
actor[1].update();
break;
case 4: // One player is dead.
speed = 50;
counter++;
if (counter == 37) {
sfx[4].stop();
sfx[5].play();
counter = 0;
if (kills[0] == 10 || kills[1] == 10) { // Sombody won?
gameState = 6;
} else {
gameState = 5;
}
}
break;
case 5: // Between level
speed = 50;
counter++;
if (counter > 75) {
sfx[5].stop();
gameState = 2;
counter = 0;
}
break;
case 6: // Game over.
counter++;
if (counter > 95) {
sfx[5].stop();
gameState = 0;
counter = 0;
}
break;
default:
break;
}
repaint();
}
}
/*
*----------------------------------------------------------------------
*
* start
*/
/**
Creates a new game thread.
*/
/*
*----------------------------------------------------------------------
*/
public void start() {
if (game == null) {
game = new Thread(this);
game.start();
}
}
/*
*----------------------------------------------------------------------
*
* stop
*/
/**
Stops the game and soundplaying.
*/
/*
*----------------------------------------------------------------------
*/
public void stop() {
if ( (game != null) && (game.isAlive()) ) {
game.stop();
}
for (int n = 0; n < 7; n++) {
sfx[n].stop();
}
gameState = 0;
game = null;
}
/*
*----------------------------------------------------------------------
*
* update
*/
/**
Draws into offscreen depending of gameState.
*/
/*
*----------------------------------------------------------------------
*/
public void update(Graphics g) {
switch (gameState) {
case -1:
offGC.setColor(tone[0]);
offGC.fillRect(0, 0, screenWidth, screenHeight);
offGC.setColor(tone[15]);
offGC.setFont(small);
offGC.drawString("Loading lot's of data...",270,180);
break;
case 3:
offGC.drawImage(bg, 0, 0, this);
actor[0].drawBullets(offGC);
actor[1].drawBullets(offGC);
if (actor[0].coord.y > actor[1].coord.y) {
actor[1].draw(offGC);
actor[0].draw(offGC);
} else {
actor[0].draw(offGC);
actor[1].draw(offGC);
}
drawStatus(offGC);
break;
case 4:
if (latestPoint == 1) {
offGC.setColor(blue);
} else {
offGC.setColor(red);
}
offGC.drawString("You're dead!",actor[actor[latestPoint].otherPlayer].coord.x,
actor[actor[latestPoint].otherPlayer].coord.y + 30);
drawStatus(offGC);
break;
case 5:
offGC.setColor(tone[0]);
offGC.fillRect(0, 0, screenWidth, screenHeight - 40);
if (latestPoint == 1) {
offGC.setColor(blue);
} else {
offGC.setColor(red);
}
/*
* The text is in the loser's colour.
*/
offGC.drawString("Prepare for next duel",270,180);
drawStatus(offGC);
break;
case 6:
offGC.setColor(tone[0]);
offGC.fillRect(0, 0, screenWidth, screenHeight - 40);
offGC.setColor(tone[15]);
offGC.drawString("GAME OVER!", 330, 160);
if (kills[0] > kills[1]) {
offGC.setColor(blue);
offGC.drawString("BLUE IS THE BEST GUNFIGHTER",240,200);
} else {
offGC.setColor(red);
offGC.drawString("RED IS THE BEST GUNFIGHTER",240,200);
}
drawStatus(offGC);
break;
default:
break;
}
paint(g);
}
/*
*----------------------------------------------------------------------
*
* loadData
*
* Loads soundeffects and graphics.
*
*----------------------------------------------------------------------
*/
private void loadData() {
MediaTracker tracker = new MediaTracker(this);
for (int n = 0; n < 7; n++) {
sfx[n] = getAudioClip(getCodeBase(), "multimedia/sfx" + n + ".au");
}
wallPaper = getImage(getCodeBase(), "multimedia/wallpaper.gif");
tracker.addImage(wallPaper, 1);
for (int n = 0; n < 6; n++) {
actor0Frames[n] = getImage(getCodeBase(), "multimedia/actor0_" + n + ".gif");
actor1Frames[n] = getImage(getCodeBase(), "multimedia/actor1_" + n + ".gif");
tracker.addImage(actor0Frames[n], 1);
tracker.addImage(actor1Frames[n], 1);
}
for (int n = 0; n < textures.length; n++) {
textures[n] = getImage(getCodeBase(), "multimedia/texture" + n + ".gif");
tracker.addImage(textures[n], 1);
}
try {
tracker.waitForID(1);
} catch (InterruptedException e) {
}
}
/*
*----------------------------------------------------------------------
*
* random
*
* Returns a random integer in [min, max].
*
*----------------------------------------------------------------------
*/
private int random(int min, int max) {
return Math.round(Math.abs(rand.nextFloat() * (max - min) + min));
}
/*
*----------------------------------------------------------------------
*
* newScene
*
* Makes a new scene.
*
*----------------------------------------------------------------------
*/
private void newScene() {
/*
* Pick a random scene, but not the previous one.
*/
prevScene = scene;
while ((scene = random(0,6)) == prevScene);
short[] ps = scenes[scene];
bgGC.drawImage(wallPaper, 0, 0, this);
obstacles = new Obstacle[ps.length / 3];
int ob = 0;
for (int n = 0; n < ps.length; n += 3, ob++) {
if (ps[n] < 6) { // Rocks and ammo boxes
obstacles[ob] = new Obstacle(textures[ps[n]], ps[n+1], ps[n+2],
polygons[ps[n]]);
} else if (ps[n] <= 9) { // Trees
obstacles[ob] = new Tree(textures[ps[n]], ps[n+1], ps[n+2],
polygons[ps[n]]);
}
obstacles[ob].draw(bgGC);
}
actor[0] = new Player(actor0Frames, actorProps[0], true);
actor[1] = new Player(actor1Frames, actorProps[1], false);
}
/*
*----------------------------------------------------------------------
*
* drawStatus
*
* Draws the score and ammo status for both actors.
*
*----------------------------------------------------------------------
*/
private void drawStatus(Graphics g) {
g.setColor(tone[0]);
g.fillRect(0, screenHeight - 40, screenWidth, 40);
g.setColor(blue);
g.drawString("Kills: " + kills[0], 50, 420);
g.drawString("Bullets: " + actor[0].getAmmo(), 150, 420);
g.setColor(red);
g.drawString("Kills: " + kills[1], 500, 420);
g.drawString("Bullets: " + actor[1].getAmmo(), 600, 420);
}
/*
*----------------------------------------------------------------------
*
* intro
*
* Introduces the game.
*
*----------------------------------------------------------------------
*/
private int intro(int counter) {
if (counter == 0) {
sfx[6].play();
offGC.setColor(tone[0]);
offGC.fillRect(0, 0, screenWidth, screenHeight);
offGC.drawImage(wallPaper,0,0, null);
offGC.setColor(tone[0]);
offGC.fillRect(200, 0, screenWidth - 400, screenHeight);
} else if (counter <= 32) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneIn++ % 16]);
offGC.setFont(small);
offGC.drawString("RepublikenSoft", 290,70);
}
} else if (counter <= 64) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneIn++ % 16]);
offGC.setFont(small);
offGC.drawString("proudly presents", 280, 90);
offGC.setColor(tone[toneOut-- % 16]);
offGC.drawString("RepublikenSoft", 290, 70);
}
} else if (counter <= 96) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneIn++ % 16]);
offGC.setFont(big);
offGC.drawString("Gunfighter",225,160);
offGC.setColor(tone[toneOut-- % 16]);
offGC.setFont(small);
offGC.drawString("proudly presents", 280, 90);
}
} else if (counter <= 128) {
offGC.setColor(blue);
offGC.setFont(big);
offGC.drawString("Gunfighter",225,160);
offGC.setColor(red);
offGC.setFont(small);
offGC.drawString("the same generation", 225, 190);
} else if (counter <= 160) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneIn++ % 16]);
offGC.setFont(small);
offGC.drawString("starring", 330,270);
}
} else if (counter <= 192) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneIn++ % 16]);
offGC.setFont(small);
offGC.drawString("The Good", 225, 290);
offGC.setColor(tone[toneOut-- % 16]);
offGC.drawString("starring", 330, 270);
if (counter == 192) {
offGC.drawImage(actor0Frames[0], 100, 190, null);
}
}
} else if (counter <= 224) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneIn++ % 16]);
offGC.setFont(small);
offGC.drawString("and", 330, 270);
offGC.setColor(tone[toneOut-- % 16]);
offGC.drawString("The Good", 225, 290);
}
} else if (counter <= 256) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneIn++ % 16]);
offGC.setFont(small);
offGC.drawString("The Bad and Ugly", 370, 290);
offGC.setColor(tone[toneOut-- % 16]);
offGC.drawString("and", 330, 270);
if (counter == 256) {
offGC.drawImage(actor1Frames[3], 569, 190, null);
}
}
} else if (counter <= 288) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneOut-- % 16]);
offGC.setFont(small);
offGC.drawString("The Bad and Ugly", 370, 290);
}
} else if (counter <= 320) {
if ((counter % 2) == 0) {
offGC.setColor(tone[toneIn++ % 16]);
offGC.setFont(small);
offGC.drawString("Press space to start", 280, 350);
}
} else if (counter <= 768) {
} else {
toneIn = 0;
toneOut = 4095;
counter = -1;
offGC.setFont(small);
}
return ++counter;
}
}