/** * @(#)Nelzan.java 1.0.1 98/12/04 *

* Copyright (C) 1998 David E. Wexler *

* This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. See * license.txt for a full copy of the GNU GPL. *

* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *

* You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. *

* To Contact the author send e-mail to vagabond@netdragon.com or send * snailmail to: 511 Bobcat Ct, Punta Gorda, FL 3398. */ import java.applet.Applet; import java.awt.*; import java.lang.*; import java.net.*; import java.io.*; import java.util.*; import EnemyShips; import HighScores; import MovingStars; import Player; import StoryLine; import ZipImageLoader; /** * Nelzan - A Space Arcade Game. *

* This Class handles state control, thread control, user input and * game level logic. *

* @author David Wexler (vagabond@netdragons.com) * @version 1.0.1 * @since Nelzan1.0.1 */ public class Nelzan extends Applet implements Runnable { // Variables to store state values so the code it more readable. private static final int GETSYSTEM = -4; private static final int GETPLAYER = -3; private static final int GETHIGH = - 2; private static final int GAMEOVER = -1; private static final int SHOWINST = 0; private static final int LEVELIMAGE = 1; private static final int LEVELDATA = 2; private static final int LEVELDELAY = 3; private static final int LEVELPLAY = 4; private static final int LEVELPLOT = 5; // What State do we Start in? private int state = GETSYSTEM; // How many calls to update() before we die? private int TTL = 50; Thread runner; MovingStars ms; ZipImageLoader zip; Player me; EnemyShips enemy; HighScores high; StoryLine plot; Font bigFont = new Font("Helvetica", Font.BOLD, 22); Font midFont = new Font("Helvetica", Font.BOLD, 16); FontMetrics midFontSize = getFontMetrics(midFont); Font smallFont = new Font("Helvetica", Font.PLAIN, 12); // For Offscreen Drawing Image offscreenI; Graphics offscreenG; /** * Use To Tell if the Game is pasued or not. */ public boolean pause = false; /** * Variable hold what level the player is currently on */ public int level = 5; Image system[]; Image ships[]; /** * Starts that Applets execution */ public void init() { setLayout(null); this.requestFocus(); zip = new ZipImageLoader(getCodeBase().toString(), this); high = new HighScores(this); ms = new MovingStars(610,375,7,0,50); } /** * Starts Thread execution */ public void start() { if (runner == null) { runner = new Thread(this); runner.start(); } } /** * Stops thread execution. */ public void stop() { if (runner != null) { runner.stop(); runner = null; } } /** * Checks to see when the mouse enters the Applet window. When it does, * the game is unpasued automatically. * * @param evt the mouse event * @param x x location mouse entered * @param y y location mouse entered * @return false always * * @since Nelzan1.0.1 */ public boolean mouseEnter(Event evt, int x, int y) { if (state == LEVELPLAY) { pause = false; this.requestFocus(); } return false; } /** * Checks to see when the mouse leaves the Applet window. When it does * the game is paused automatically. * * @param evt the mouse event * @param x x location mouse entered * @param y y location mouse entered * @return false always * * @since Nelzan1.0.1 */ public boolean mouseExit(Event evt, int x, int y) { if (state == LEVELPLAY) pause = true; return false; } /** * Handles Events when Keys are pressed. All KeyEvents are handled by this function, * to provide gameplay most like and arcade game tags inside of Player are * changed to true on keyDown. * * @param evt The Event ID * @param key The Key Pressed * @return false if state is not handling user Input * true otherwise * @see Player#setMoveTrue */ public boolean keyDown(Event evt, int key) { if (state < GAMEOVER) return false; else if (state == GAMEOVER || state == SHOWINST) startLevel(level); else if (state == LEVELPLOT && plot.canContinue()) startLevel(level); else if (state < LEVELPLAY) return false; else if (key == 'p' || key == 'P') pause = !pause; else if (key == 1004 || key == '8') me.setMoveTrue(0); else if (key == 1005 || key == '2') me.setMoveTrue(2); else if (key == 1006 || key == '4') me.setMoveTrue(3); else if (key == 1007 || key == '6') me.setMoveTrue(1); else if (key == 'a' || key == 'A') me.setActiveWeapon(0); else if (key == 's' || key == 'S') me.setActiveWeapon(1); else if (key == 'd' || key == 'D') me.setActiveWeapon(2); else if (key == 'f' || key == 'F') me.setActiveWeapon(3); else if (key == ' ') me.fire(); return true; } /** * Handles Events when Keys are let go. All KeyEvents are canceled by this * function, to provide gameplay most like an Arcade game tags inside of * Player are changed to false on keyUp. * * @param evt The Event ID * @param key The Key Pressed * @return true always. */ public boolean keyUp(Event evt, int key) { if (key == 1004 || key == '8') me.setMoveFalse(0); else if (key == 1005 || key == '2') me.setMoveFalse(2); else if (key == 1006 || key == '4') me.setMoveFalse(3); else if (key == 1007 || key == '6') me.setMoveFalse(1); return true; } /** * Handles Thread execution. If game is not paused both gameLogic() and * repaint() is called, otherwise just repaint() is called. */ public void run() { while(true) { if (!pause) gameLogic(); repaint(); sleep(50); } } /** * This fuction paints everything for the user to See. Depending on the * state, different fuctions are called from different classes. *

* * @param g Where to paint to; */ public synchronized void paint(Graphics g) { if (offscreenG == null) { offscreenI = createImage(this.size().width, this.size().height); offscreenG = offscreenI.getGraphics(); offscreenG.setFont(bigFont); } offscreenG.setColor(Color.black); offscreenG.fillRect(0, 0, 600, 390); if (state == LEVELPLAY) { ms.paint(offscreenG); me.paint(offscreenG); enemy.paint(offscreenG); paintPannel(offscreenG); if (pause) { offscreenG.setFont(bigFont); offscreenG.setColor(Color.blue); offscreenG.fillRect(200,150,200,40); offscreenG.setColor(Color.black); offscreenG.drawString("Game Paused", 227, 178); offscreenG.setColor(Color.white); offscreenG.drawString("Game Paused", 225, 177); } } else if (state == LEVELPLOT) { if (plot != null) plot.paint(offscreenG); } else if (state < LEVELIMAGE) { drawState(offscreenG); } else { drawLevelState(offscreenG); } g.drawImage(offscreenI,0,0,this); } /** * Override of update to prevent clearing of screen. */ public void update(Graphics g) { paint(g); } /** * Causes the thread to sleep. * * @param i How long to sleep for. */ private void sleep(int i) { try { runner.sleep(i); } catch (InterruptedException e) { } } /** * Handles all the game level Logic. Tells MovingStars, Player, * and EnemyShips to update. Also checks for level ending, and * Game Over. */ private void gameLogic() { if (state == LEVELPLOT) { if (plot != null) plot.update(); } else if (state != LEVELPLAY) handleState(); else { ms.update(); me.update(); enemy.update(); if (enemy.levelOver()) startLevel(level + 1); if (me.lives < 0) gameOver(); } } /** * This function handles the migration between state. */ private void handleState() { switch (state) { case -4: loadSystem(); state = GETPLAYER; break; case -3: loadPlayer(); state = GETHIGH; break; case -2: loadHighScores(); state = GAMEOVER; break; case -1: if (TTL-- < 0) { state = SHOWINST; TTL = 50; } else sleep(100); break; case 0: if (TTL-- < 0) { state = GAMEOVER; TTL = 50; } else sleep(100); break; case 1: loadShipImages(); state = LEVELDATA; break; case 2: loadLevelData(); state = LEVELDELAY; break; case 3: state = LEVELPLAY; sleep(1000); break; } } /** * Converts a String to an int; * * @param s The String to be converted * @return an int value from s */ private int toInt(String s) { Integer cToI = new Integer(s); return cToI.intValue(); } /** * Load System Images. */ private void loadSystem() { system = zip.getImages("system.zip"); } /** * Loads player Images and defines a new instance of Player. */ private void loadPlayer() { me = new Player(zip.getImages("ship.zip"), 10, 200, this); } /** * Sends High Score to CGI script and downloads new High Score Data. */ private void loadHighScores() { high.sendHigh(me.score); } /** * Loads Enemy Ship Images to be used in the upcoming Level. */ private void loadShipImages() { ships = zip.getImages("" + level + ".zip"); } /** * Creates a new instance of EnemyShips */ private void loadLevelData() { enemy = new EnemyShips(ships, level, this); } /** * Loads the Plot line. * * @param i The level to load a plot for. */ private void loadPlot(int i) { plot = null; state = LEVELPLOT; plot = new StoryLine(i, this); System.gc(); } /** * Starts new level. * * @param i The level to start. */ private void startLevel(int i) { if ((i%4) == 1 && plot == null) { loadPlot(i); } else { plot = null; if (i > toInt(this.getParameter("level"))) gameOver(); else state = LEVELIMAGE; } me.level = i; level = i; } /** * Sets state to Game Over, and handles other logic that deals with * the game ending. */ private void gameOver() { TTL = 50; high.sendHigh(me.score); level = 1; state = GAMEOVER; for (int i = 0; i < 10; i++) me.killMe(); me.score = 0; me.lives = 3; me.level = 1; } /** * Display User Feedback. Shows Shield Level, Weapon selected, Weapon Level, * Lives, and score. * * @param g The Graphics to paint to. */ private void paintPannel(Graphics g) { g.setFont(smallFont); g.setColor(Color.white); g.drawString("Shields",5,388); g.drawString("Score: " + me.score, 475, 388); g.drawString("Lives ", 250, 388); g.drawImage(system[me.getActiveWeapon()], 175, 378, this); g.drawString("Lvl: " + me.getWeaponLevel(), 190, 388); g.setColor(Color.cyan); g.drawString("Nelzan 1.0.1", 390, 388); for (int i = 0; i < me.lives; i++) { g.drawImage(system[4], 285 + (i*15),378, this); } for (int i = 0; i < me.shields && i < 100; i++) { g.setColor(me.shieldColor[i]); g.drawLine(50+i,378,50+i,388); } if (me.shields > 100) { g.drawLine(152, 383, 160, 383); g.drawLine(156, 379, 156, 387); } } /** * Display State Information. This shows what the system is doing like, * "Loading System . . ." and other relevent messages. It also display * High Scores and Instrutions when needed. * * @param g The Graphics to paint to. */ private void drawState(Graphics g) { String msg = ""; switch (state) { case -4: msg = "Loading System . . ."; break; case -3: msg = "Loading Player . . ."; break; case -2: msg = "Loading High Scores."; break; case -1: case 0: msg = "Hit Any Key to Start"; break; } g.setFont(bigFont); g.setColor(Color.blue); g.fillRect(200,75,200,200); g.setColor(Color.black); g.drawString("Nelzan", 262, 91); g.setColor(Color.white); offscreenG.drawString("Nelzan", 260, 90); g.setFont(midFont); g.setColor(Color.black); g.drawString(msg, 301 - midFontSize.stringWidth(msg)/2, 271); g.setColor(Color.white); g.drawString(msg, 300 - midFontSize.stringWidth(msg)/2, 270); if (state == -1) high.paint(g); else if (state == 0) high.paintInstructions(g); } /** * Display Level State Information. This shows what the level is doing like, * "Loading Images . . ." and other relevent messages. It also display * The Level number and if the level is a boss or not. * * @param g The Graphics to paint to. */ private void drawLevelState(Graphics g) { g.setFont(bigFont); g.setColor(Color.blue); g.fillRect(200,150,200,65); g.setColor(Color.black); if (level % 4 != 0) g.drawString("Level " + level, 212, 173); else g.drawString("Level " + level + " (Boss)", 212, 173); g.setColor(Color.white); if (level % 4 != 0) g.drawString("Level " + level, 210, 172); else g.drawString("Level " + level + " (Boss)", 210, 172); String msg = "Get Ready . . ." + (4-state); g.setFont(midFont); g.setColor(Color.black); g.drawString(msg, 301 - midFontSize.stringWidth(msg)/2, 191); g.setColor(Color.white); g.drawString(msg, 300 - midFontSize.stringWidth(msg)/2, 190); switch (state) { case 1: msg = "Loading Images . . ."; break; case 2: msg = "Loading Data . . ."; break; case 3: msg = "- Systems Nominal -"; break; } g.setColor(Color.black); g.drawString(msg, 301 - midFontSize.stringWidth(msg)/2, 211); g.setColor(Color.white); g.drawString(msg, 300 - midFontSize.stringWidth(msg)/2, 210); } }