first commit

This commit is contained in:
Jose Caban
2025-06-07 01:59:34 -04:00
commit 388ac241f0
3558 changed files with 9116289 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.event.ActionListener;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
/**
* This is the parent of all the game state classes. A game state is
* basically one screen - be it the splash screen, the main menu, or
* the game screen itself.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
*/
public abstract class AbstractGameState implements ActionListener {
/**
* The UI to display with this state.
*/
private LRootPane gui;
/**
* Creates a new <code>AbstractGameState</code> instance.
*
*/
protected AbstractGameState() {
gui = new LRootPane();
}
/**
* Returns the root pane with the UI associated with this state.
*
* @return a <code>LRootPane</code> value
*/
public final LRootPane getUI() {
return gui;
}
/**
* Sets a new value of the root pane.
*
* @param pane a <code>LRootPane</code> value
*/
protected final void setUI(LRootPane pane) {
gui = pane;
}
//@roseuid 40651ACF00AB
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return True if the initialization was successful, and false if
* it was not.
*
*/
public abstract boolean initialize();
//@roseuid 40651B230193
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return True if the clean-up was successful, and false if it failed.
*
*/
public abstract boolean cleanUp();
}

View File

@@ -0,0 +1,164 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobSettings;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobFactory;
import edu.gatech.cs2335.lemmings.gui.LComponent;
import edu.gatech.cs2335.lemmings.gui.LLeafComponent;
import edu.gatech.cs2335.lemmings.gui.IClickable;
import java.awt.Graphics;
import java.awt.Dimension;
//import java.awt.image.BufferedImage;
import java.util.List;
import java.awt.Point;
/**
* Class GamePlayState: where the actual game is displayed and played
*
*
*/
public class AttractState extends GamePlayState {
/**
* Allow more bashers?
*/
private boolean basher = true;
/**
* Allow more bashers?
*/
private boolean bridger = true;
/**
* Constructor
* @param l , Level
*/
public AttractState(Level l) {
super(l);
}
/**
* Constructor
* @param l , string of level name
*/
public AttractState(String l) {
super(l);
}
/**
* Handles the special update.
*/
protected void specialUpdate() {
Lemming currLem = null;
// BufferedImage map = getLevel().getMap().getMap();
List lemmings = getLevel().getActiveLemmings();
int x;
int y;
if (getLevel().getLemmingHeaven().size() == 20) {
GameEngine.getInstance().setCurrentState(new MainMenuState());
}
for (int i = 0; i < lemmings.size(); i++) {
currLem = (Lemming) lemmings.get(i);
x = currLem.getPosition().getX() + 5;
y = currLem.getPosition().getY() + 5;
// System.out.println("In special update x is " + x + "y is " + y);
//check for digger
if (x >= 156 && y < 122) {
currLem.setOccupation(
LemmingJobFactory.getInstance().makeJob(
LemmingJobSettings.JOB_ID_DIGGER));
}
//check for exploder
if (x >= 615 && x <= 620 && y >= 225 && y <= 230) {
currLem.setOccupation(
LemmingJobFactory.getInstance().makeJob(
LemmingJobSettings.JOB_ID_EXPLODER));
}
//check for basher
if (x >= 220 && x <= 250 && y >= 282 && y <= 288 && basher) {
basher = false;
currLem.setOccupation(
LemmingJobFactory.getInstance().makeJob(
LemmingJobSettings.JOB_ID_BASHER));
}
//check for climber
if (x >= 210 && x <= 220 && y >= 590 && y <= 595) {
currLem.setOccupation(
LemmingJobFactory.getInstance().makeJob(
LemmingJobSettings.JOB_ID_CLIMBER));
}
//check for floater
if (x >= 130 && x <= 180 && y >= 220 && y <= 235) {
currLem.setOccupation(
LemmingJobFactory.getInstance().makeJob(
LemmingJobSettings.JOB_ID_FLOATER));
}
//check for bridger
if (x >= 340 && x <= 345 && y >= 282 && y <= 288 && bridger) {
bridger = false;
currLem.setOccupation(
LemmingJobFactory.getInstance().makeJob(
LemmingJobSettings.JOB_ID_BRIDGER));
}
}
}
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*/
public void actionPerformed(ActionEvent ae) {
GameEngine.getInstance().setCurrentState(new MainMenuState());
}
/**
* Whatever else needs to be initialized.
*/
protected void specialInitialize() {
LComponent c = new LClickListener() {
public void registerMouseClick(int button, Point coords) {
GameEngine.getInstance().setCurrentState(new MainMenuState());
}
};
c.setPosition(new Point(0, 0));
c.setSize(new Dimension(800, 600));
c.setShown(true);
getUI().addChild(c);
}
/**
* Listens to keys and stuff.
*/
private abstract class LClickListener
extends LLeafComponent implements IClickable {
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
return true;
}
}
}

View File

@@ -0,0 +1,105 @@
package edu.gatech.cs2335.lemmings.engine;
import java.util.Random;
/**
* Class Particle: When a lemming blows up, he makes particles. Well,
* here they are. :)
*
* Description from Jose:
* Adds specific blood stuff
*
* Blood shouldn't bounce, rather it should hit the wall and die
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the BloodParticle class
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Caban</A>
* @version Version 1.0, Apr. 11, 2004
*/
public class BloodParticle extends Particle {
/**
* Used to randomize velocity.
*/
private static final Random GENERATOR = new Random(new java.util.Date()
.getTime());
/**
* Particles to create when death should occur
*/
public static final int PARTICLESWHENDIE = 75;
/**
* time to Live
*/
private int killMe;
/**
* curve
*/
private int curve;
/**
* Stores canDie
*/
private boolean canDie;
/**
* Creates a new <code>Particle</code> instance.
*/
public BloodParticle() {
super("blood");
getVelocity().setPolar(GENERATOR.nextInt(8), GENERATOR.nextInt(30));
canDie = false;
killMe = 0;
curve = 0;
}
/**
* Should return canDie
*
* @return <code>boolean</code> value
*/
public boolean getCanDie() {
return canDie;
}
/**
* Should return canDie
*
* @param a for candie <code>boolean</code> value
*/
public void setCanDie(boolean a) {
canDie = a;
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (killMe > 25) {
return true;
}
if (curve > 5) {
return true;
} else if (this.getVelocity().getJ() == 0) {
curve++;
}
killMe++;
return canDie;
}
}

View File

@@ -0,0 +1,63 @@
package edu.gatech.cs2335.lemmings.engine;
import java.util.Random;
/**
* Class Particle: When a lemming blows up, he makes particles. Well,
* here they are. :)
*
* Description from Andrew:
* Adds specific bone stuff
*
* bones reaches peak in curve three before disapearing
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the BoneParticle class
* </PRE>
*
* @author <A HREF="mailto:gtg284h@mail.gatech.edu">Andrew Knight</A>
* @version Version 1.0, Apr. 11, 2004
*/
public class BoneParticle extends Particle {
/**
* Used to randomize velocity.
*/
private static final Random GENERATOR = new Random(new java.util.Date()
.getTime());
/**
* Particles to create when death should occur
*/
public static final int PARTICLESWHENBONE = 3;
/**
* curve
*/
private int curve;
/**
* Creates a new <code>Particle</code> instance.
*/
public BoneParticle() {
super("bone");
getVelocity().setPolar(GENERATOR.nextInt(8), GENERATOR.nextInt(30));
curve = 0;
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (curve > 3) {
return true;
} else if (this.getVelocity().getJ() == 0) {
curve++;
}
return false;
}
}

View File

@@ -0,0 +1,94 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Point;
import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* Class CreditsState: Displays the credits screen, and when we are
* done - goes back to the main menu.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 15, 2004) - Created the CreditsState class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 15, 2004
*/
public class CreditsState extends AbstractGameState {
/**
* Creates a new <code>CreditsState</code> instance.
*/
public CreditsState() {
}
//@roseuid 4065D29C032F
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*
*/
public boolean initialize() {
LRootPane np = new LRootPane();
LButton button = new LButton();
button.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("credits"),
Direction.NO_DIRECTION, Looping.NONE));
button.addActionListener(this);
button.setShown(true);
button.setPosition(new Point(0, 0));
np.addChild(button);
setUI(np);
return true;
}
//@roseuid 4065D29C034D
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
return true;
}
//@roseuid 4065D29C037F
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ada
*
*/
public void actionPerformed(ActionEvent ae) {
//The splash screen has been clicked.
if (GameEngine.VERBOSE) {
System.out.println("CreditsState: Transferrring to Main Menu...");
System.out.flush();
}
//Transfer to Main Menu.
MainMenuState mms = new MainMenuState();
GameEngine.getInstance().setCurrentState(mms);
}
}

View File

@@ -0,0 +1,79 @@
package edu.gatech.cs2335.lemmings.engine;
import java.util.Random;
/**
* Class Particle: When a lemming blows up, he makes particles. Well,
* here they are. :)
*
* Description from Andrew:
* Adds specific dirt stuff
*
* Dirt reaches peak in curve twice before it disapears
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the DirtParticle class
* </PRE>
*
* @author <A HREF="mailto:gtg284h@mail.gatech.edu">Andrew Knight</A>
* @version Version 1.0, Apr. 11, 2004
*/
public class DirtParticle extends Particle {
/**
* Used to randomize velocity.
*/
private static final Random GENERATOR = new Random(new java.util.Date()
.getTime());
/**
* Particles to create when death should occur
*/
public static final int PARTICLESWHENDIG = 4;
/**
* curve
*/
private int curve;
/**
* Creates a new <code>Particle</code> instance.
*/
public DirtParticle() {
super("dirt");
getVelocity().setPolar(GENERATOR.nextInt(8), GENERATOR.nextInt(30));
curve = 0;
}
/**
* Creates a new <code>Particle</code> instance.
*@param i i
*@param j j
*/
public DirtParticle(int i, int j) {
super("dirt");
getVelocity().setI(-i + GENERATOR.nextInt(3));
getVelocity().setJ(-j + GENERATOR.nextInt(3));
curve = 0;
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (curve > 2) {
return true;
} else if (this.getVelocity().getJ() == 0) {
curve++;
}
return false;
}
}

View File

@@ -0,0 +1,247 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Font;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.file.LevelReader;
import edu.gatech.cs2335.lemmings.gui.LIcon;
//import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
import edu.gatech.cs2335.lemmings.networking.LemmingNetworking;
/**
* Class EstablishingConnectionState: This state will load a level,
* and then wait for a connection to be established. After both of the
* conditions are met, it will go on to the actual game.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 15, 2004) - Created the EstablishingConnectionState class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 15, 2004
*/
public class EstablishingConnectionState extends AbstractGameState {
/**
* The level that we will be loading here.
*/
private Level level;
/**
* Allows the user to continue on to play the level.
*/
// private LButton continueButton;
/**
* The label to display our message to the user in.
*/
private LFancyLabel message;
/**
* Describe variable <code>levelLoaded</code> here.
*
*/
private boolean islevelLoaded;
/**
* Contains true if the connection is established, and false if it
* is not yet.
*/
private boolean connectionEstablished;
/**
* Port.
*/
private int port;
/**
* Host.
*/
private String host;
/**
* Describe variable <code>lemmingCount</code> here.
*
*/
private int lemmingCount;
/**
* Creates a new <code>EstablishingConnectionState</code> instance.
*/
public EstablishingConnectionState() {
connectionEstablished = false;
islevelLoaded = false;
level = null;
lemmingCount = 0;
//Initialize background:
LIcon bg = new LIcon(TileSetManager.getInstance()
.getTileSet("bg_loading"));
bg.setShown(true);
bg.setPosition(new Point(0, 0));
//Initialize label:
message = new LFancyLabel();
message.setForeground(Color.black);
message.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
message.setText("Establishing Connection...");
message.setShown(true);
message.setPosition(new Point(300, 300));
//Initialize UI:
LRootPane np = new LRootPane();
np.addChild(bg);
np.addChild(message);
setUI(np);
}
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*/
public boolean initialize() {
return true;
}
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
level = null;
connectionEstablished = false;
islevelLoaded = false;
port = 0;
host = null;
lemmingCount = 0;
return true;
}
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*/
public void actionPerformed(ActionEvent ae) {
//The user can't click anything here, so...
}
/**
* Get the value of lemmingCount.
* @return value of lemmingCount.
*/
public int getLemmingCount() {
return lemmingCount;
}
/**
* Set the value of lemmingCount.
* @param v Value to assign to lemmingCount.
*/
public void setLemmingCount(int v) {
this.lemmingCount = v;
}
/**
* Describe <code>connectTo</code> method here.
*
* @param hostt a <code>String</code> value
* @param portt an <code>int</code> value
*/
public void connectTo(String hostt, int portt) {
host = hostt;
port = portt;
//We are the client:
if (LemmingNetworking.getInstance().connectTo(host, port)) {
connectionEstablished();
}
}
/**
* Describe <code>host</code> method here.
*
* @param portt an <code>int</code> value
*/
public void host(int portt) {
port = portt;
//We are the server:
if (LemmingNetworking.getInstance().listenForConnection(port)) {
connectionEstablished();
}
}
/**
* Loads the level with the specified id.
*
* @param id a <code>String</code> value
*/
public synchronized void loadLevel(final String id) {
level = LevelReader.getInstance().loadLevel(id);
levelLoaded();
}
/**
* This function will be done when the level is done loading.
*/
private synchronized void levelLoaded() {
islevelLoaded = true;
//Set the number of lemmings on the level to fourty plus whatever
//we have
int num = 40 + lemmingCount;
level.setNumberOfLemmings(num);
if (LemmingNetworking.getInstance().isConnected()) {
startLevel();
}
}
/**
* Will be called when the connection is established.
*/
private synchronized void connectionEstablished() {
if (islevelLoaded) {
startLevel();
}
}
/**
* Starts the level.
*/
private synchronized void startLevel() {
GameEngine.getInstance()
.setCurrentState(new MultiplayerGameplayState(level));
LemmingNetworking.getInstance().getConnection().startListening();
LemmingNetworking.getInstance().getConnection().startSending();
}
}

View File

@@ -0,0 +1,88 @@
package edu.gatech.cs2335.lemmings.engine;
import java.util.Random;
/**
* Class Particle: When a lemming blows up, he makes particles. Well,
* here they are. :)
*
* Description from Andrew:
* Adds specific flame stuff
*
* flames shoot downward and live for five process calls
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the FlameParticle class
* </PRE>
*
* @author <A HREF="mailto:gtg284h@mail.gatech.edu">Andrew Knight</A>
* @version Version 1.0, Apr. 11, 2004
*/
public class FlameParticle extends Particle {
/**
* Particles to create when death should occur
*/
public static final int PARTICLESWHENFLAME = 3;
/**
* Used to randomize velocity.
*/
private static final Random GENERATOR = new Random(new java.util.Date()
.getTime());
/**
* time to Live
*/
private int killMe;
/**
* Stores canDie
*/
private boolean canDie;
/**
* Creates a new <code>Particle</code> instance.
*/
public FlameParticle() {
super("flame");
killMe = 0;
getVelocity().setJ(2 + GENERATOR.nextInt(2));
getVelocity().setI((-1 * GENERATOR.nextInt(1)) * GENERATOR.nextInt(2));
}
/**
* Should return canDie
*
* @return <code>boolean</code> value
*/
public boolean getCanDie() {
return canDie;
}
/**
* Should return canDie
*
* @param a for candie <code>boolean</code> value
*/
public void setCanDie(boolean a) {
canDie = a;
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (killMe > 5 + GENERATOR.nextInt(3)) {
return true;
}
killMe++;
return canDie;
}
}

View File

@@ -0,0 +1,39 @@
package edu.gatech.cs2335.lemmings.engine;
import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
/**
* Class FlingTrap: when the lemming comes close to this one, it flings him.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 22, 2004) - Created the FlingTrap class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 22, 2004
*/
public class FlingTrap extends Portal {
/**
* Creates a new <code>FlingTrap</code> instance.
*/
public FlingTrap() {
super(PortalFactory.PORTAL_TYPES[2]);
}
/**
* This function will be called when a lemming enters this portal.
*
* @param l a <code>Lemming</code> value
*/
public void processLemmingEntry(Lemming l) {
int x = l.getVelocity().getI();
x /= Math.abs(x); //normalize
l.getVelocity().setI(x * PhysicsSettings.getInstance()
.getTerminalVelocity() / 2);
l.getVelocity().setJ(-PhysicsSettings.getInstance().getTerminalVelocity());
}
}

View File

@@ -0,0 +1,242 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Rectangle;
import edu.gatech.cs2335.lemmings.gui.LFader;
import edu.gatech.cs2335.lemmings.gui.LApplication;
import edu.gatech.cs2335.lemmings.networking.AbstractMessage;
import edu.gatech.cs2335.lemmings.networking.LemmingNetworking;
import edu.gatech.cs2335.lemmings.file.LevelReader;
/**
* This class will manage the states of the game and their
* transitions. It will know when to go to the splash screen or the
* menu, or the game itself, and when to quit.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
*/
public class GameEngine {
/**
* Display debug information?
*/
public static final boolean VERBOSE = false;
/**
* Display a lot of debug information?
*/
public static final boolean DEBUG = false;
/**
* Singleton implementation.
*/
private static GameEngine instance;
/**
* This is the state that the game is currently in.
*/
private AbstractGameState currentState;
/**
* This is the application that will be started when the game starts.
*/
private LApplication theApp;
/**
* Cache this guy so we don't have to create him on the fly.
*/
private LoadLevelState loadLevelState;
/**
* The cached d00d.
*/
private EstablishingConnectionState ecs;
/**
* Like, another chached d00d, and stuff.
*/
private NetworkErrorState nes;
/**
* Creates a new <code>GameEngine</code> instance.
*
*/
private GameEngine() {
if (VERBOSE) {
System.out.println("GameEngine: Initializing...");
System.out.flush();
}
loadLevelState = new LoadLevelState();
ecs = new EstablishingConnectionState();
nes = new NetworkErrorState();
theApp = new LApplication("CS2335 Lemmings", null, null);
theApp.startRenderer();
}
/**
* Singleton implementation.
*
* @return a <code>GameEngine</code> value
*/
public static synchronized GameEngine getInstance() {
if (instance == null) {
instance = new GameEngine();
}
return instance;
}
/**
* When a network error occurs, this function will be called.
*
* @param error a <code>String</code> value
*/
public synchronized void showNetworkError(String error) {
//Show error:
nes.setErrorMessage(error);
setCurrentState(nes);
if (VERBOSE) {
System.err.println("GameEngine: Showing network error:");
System.err.println(error);
System.err.flush();
}
//Shut down networking:
LemmingNetworking.getInstance().shutDownNetworking();
}
/**
* Starts a single-player game with the specified level.
*
* @param name a <code>String</code> value
*/
public void startSingleLevel(String name) {
setCurrentState(loadLevelState);
//Collect garbage now:
System.gc();
//Now that we've cleaned shit up...
loadLevelState.loadLevel(name);
}
/**
* Accessor for EstablishingConnectionState
* @return EstablishingConnectionState
*/
public EstablishingConnectionState getEcs() {
return ecs;
}
/**
* Describe <code>startMultiGame</code> method here.
*
*/
public void startMultiGame() {
setCurrentState(ecs);
//Collect garbage now:
System.gc();
ecs.loadLevel(LevelReader.getInstance().getLevelList()[0]);
}
/**
* stuff.
*
* @param count an <code>int</code> value
* @param lemmings an <code>int</code> value
*/
public void startMultiLevel(int count, int lemmings) {
setCurrentState(ecs);
//Collect garbage now:
System.gc();
ecs.setLemmingCount(lemmings);
ecs.loadLevel(LevelReader.getInstance().getLevelList()[count]);
}
/**
* Access method for the currentState property.
*
* @return the current value of the currentState property
*/
public AbstractGameState getCurrentState() {
return currentState;
}
/**
* Sets the value of the currentState property. If the value passed
* in is NULL, will throw a NullPointerException.
*
* @param aCurrentState the new value of the currentState property
*/
public void setCurrentState(final AbstractGameState aCurrentState) {
boolean shitHitTheFan = false;
boolean fade = false;
if (currentState != null) {
fade = true;
}
if (fade) {
LFader f = new LFader();
f.setBounds(new Rectangle(0, 0, 800, 600));
f.setShown(true);
theApp.getRootPane().addChild(f);
f.fadeToForeground(1000);
currentState.cleanUp();
currentState = aCurrentState;
currentState.initialize();
if (f.isFading()) {
try {
Thread.sleep(f.getTimeRemaining());
} catch (Exception e) {
//Too bad:
//Don't mess with this! This exception is thrown when the
//next screen loads faster than the fader fully fades to
//black (because Java is screwed in the head and doesn't
//support normal thread wake up.
shitHitTheFan = true;
// System.out.println("Exception in GameEngine\n");
}
}
} else {
currentState = aCurrentState;
currentState.initialize();
}
//Set up the new root pane:
theApp.setRootPane(currentState.getUI());
theApp.getRootPane().makeDirty();
}
/**
* Describe <code>receiveMessage</code> method here.
*
* @param msg an <code>AbstractMessage</code> value
*/
public synchronized void receiveMessage(AbstractMessage msg) {
if (msg == null) {
System.err.println("GameEngine: receiveMessage got bad message\n");
return;
}
//check if we're in the MultiplayerGameState
if (currentState instanceof MultiplayerGameplayState) {
((MultiplayerGameplayState) currentState).receiveMessage(msg);
}
}
}

View File

@@ -0,0 +1,755 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.Point;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Dimension;
//import java.util.List;
//import java.util.Vector;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobSettings;
//import edu.gatech.cs2335.lemmings.networking.LemmingNetworking;
//import edu.gatech.cs2335.lemmings.networking.MessageFactory;
//import edu.gatech.cs2335.lemmings.networking.MessageSettings;
//import edu.gatech.cs2335.lemmings.networking.AbstractMessage;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
import edu.gatech.cs2335.lemmings.gui.ITypable;
import edu.gatech.cs2335.lemmings.gui.LLeafComponent;
import edu.gatech.cs2335.lemmings.gui.LComponent;
import edu.gatech.cs2335.lemmings.gui.JobContainer;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LToggleButton;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
import edu.gatech.cs2335.lemmings.gui.GamePlayPanel;
import edu.gatech.cs2335.lemmings.graphics.Renderer;
import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
import edu.gatech.cs2335.lemmings.file.LevelReader;
import edu.gatech.cs2335.lemmings.gui.MiniMap;
/**
* The game state, where the actual game is displayed and played.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version 1.0
*/
public class GamePlayState extends AbstractGameState {
/**
* How many times per second should we update the lemmings?
*/
public static final float UPDATE_FREQUENCY = 10;
/**
* Delay between the updates.
*/
public static final long UPDATE_DELAY = (long) (1000.0f / UPDATE_FREQUENCY);
/**
* The level associated with this game. It will be what the player plays on.
*/
private Level level;
/**
* True if the game is paused. False if it is not.
*/
//private boolean paused;
/**
* Set to true when the state is killed.
*/
private boolean killed;
/**
* The delay between the updates.
*/
private long gameSpeed;
/**
* The label that shows the time...
*/
private LFancyLabel timeLabel;
/**
* The label showing the number of lemmings released.
*/
private LFancyLabel releasedLabel;
/**
* The label that will show how many lemmings have been saved.
*/
private LFancyLabel savedLabel;
/**
* This label will show the number of lemmings out in the field.
*/
private LFancyLabel outLabel;
/**
* Describe variable <code>jb</code> here.
*
*/
private JobContainer jobs;
/**
* Describe variable <code>pauseToggle</code> here.
*
*/
private LToggleButton pauseToggle;
/**
* Describe variable <code>speedToggle</code> here.
*
*/
private LToggleButton speedToggle;
/**
* Describe variable <code>nukeToggle</code> here.
*
*/
private LToggleButton nukeToggle;
/**
* Describe variable <code>gpp</code> here.
*
*/
private GamePlayPanel gpp;
/**
* The renderer used for this.
*/
private Renderer renderer;
/**
* Will display the current lemming flow.
*/
private LFancyLabel lblLemmingFlow;
/**
* time of last update
*/
private long lastUpdate;
/**
* Number of Lemmings
*/
private int numLemmings;
/**
* String representation of the number of lemmings
*/
private String strNumLemmings;
/**
* How many lemmings have to be saved
*/
private int lemmingsToSave;
/**
* String representation of the number of lemmings to save
*/
private String strLemmingsToSave;
/**
* Number of released lemmings
*/
private int numReleasedLemmings;
/**
* Number of lemmings out
*/
private int numLemmingsOut;
//@roseuid 406623D9022A
/**
* Creates a new GamePlayState instance.
*
* @param l The level that we would like to play on.
*
*/
public GamePlayState(Level l) {
level = l;
//paused = true;
killed = false;
gameSpeed = UPDATE_DELAY;
}
/**
* Loads the level with the id passed in and uses that to play.
*
* @param levelName a <code>String</code> value
*/
public GamePlayState(String levelName) {
this(LevelReader.getInstance().loadLevel(levelName));
}
/**
* Access method for the level property.
*
* @return the current value of the level property
*/
public Level getLevel() {
return level;
}
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*/
public boolean initialize() {
//Set up the game panel:
LRootPane np = new LRootPane();
Rectangle bounds = new Rectangle(0, 20, 800, 580);
renderer = new Renderer(level, new Rectangle(bounds));
gpp = new GamePlayPanel(renderer);
jobs = new JobContainer(level);
jobs.setShown(true);
jobs.setPosition(new Point(30, 570));
jobs.setArrangement(JobContainer.X_AXIS);
for (int i = 0; i < LemmingJobSettings.ALL_JOB_NAMES.length; i++) {
if (level.getJobsRemaining(LemmingJobSettings.ALL_JOB_NAMES[i]) > 0) {
jobs.addJobButton(LemmingJobSettings.ALL_JOB_NAMES[i]);
}
}
gpp.setBounds(new Rectangle(bounds));
gpp.setShown(true);
initializeLabels();
//Buttons:
initializeButtons();
LButton ib = new LButton();
ib.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("btn_increase"),
Direction.NO_DIRECTION,
Looping.NONE));
ib.addActionListener(this);
ib.setActionCommand("increase");
ib.setShown(true);
ib.setPosition(new Point(10, 290));
LButton db = new LButton();
db.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("btn_decrease"),
Direction.NO_DIRECTION,
Looping.NONE));
db.addActionListener(this);
db.setActionCommand("decrease");
db.setShown(true);
db.setPosition(new Point(10, 370));
np.addChild(gpp);
np.addChild(timeLabel);
np.addChild(releasedLabel);
np.addChild(savedLabel);
np.addChild(outLabel);
np.addChild(lblLemmingFlow);
np.addChild(jobs);
np.addChild(pauseToggle);
np.addChild(speedToggle);
np.addChild(nukeToggle);
np.addChild(ib);
np.addChild(db);
MiniMap mm = new MiniMap(level);
mm.setShown(true);
mm.setPosition(new Point(650, 450));
mm.setSize(new Dimension(120, 120));
np.addChild(mm);
setUI(np);
//Update physics engine
PhysicsEngine.getInstance().setCurrentMap(level.getMap().getMap());
specialInitialize();
np.addChild(new LKeyListener() {
public void processKeyTyped(char key, int modifiers) {
if (key == 'p') {
//Pause the game.
if (level.isPaused()) {
pauseToggle.setSelected(false);
handlePauseButton();
} else {
pauseToggle.setSelected(true);
handlePauseButton();
}
} else if (key == 'f') {
//Fast forward.
speedToggle.toggleSelected();
handleSpeedupButton();
} else if (key == '+') {
//Increase lemming flow:
handleIncreaseButton();
} else if (key == '-') {
//Decrease lemming flow:
handleDecreaseButton();
} else if (key == 27) { //ESC
try {
level.pause();
level.setLemmingsSaved(0);
endLevel();
} catch (NullPointerException npe) {
System.err.println("NullPointerException"); //tooo bad
}
}
}
});
//Set up update thread:
new Thread(new Runnable() {
public void run() {
lastUpdate = System.currentTimeMillis();
numLemmings = level.getPendingLemmings().size();
strNumLemmings = Integer.toString(numLemmings);
lemmingsToSave = level.getData().getPercentToSave();
lemmingsToSave = (numLemmings * lemmingsToSave) / 100;
strLemmingsToSave = Integer.toString(lemmingsToSave);
level.unpause();
while (!killed) {
//Update level:
level.updateLevel();
if (level.isLevelOver()) {
killed = true;
endLevel();
return;
}
numReleasedLemmings
= numLemmings - level.getPendingLemmings().size();
numLemmingsOut = level.getActiveLemmings().size();
//Update UI:
updateStuff();
//Sleep some:
lastUpdate += gameSpeed;
try {
Thread.sleep(Math
.max(0, lastUpdate - System.currentTimeMillis()));
} catch (Exception e) {
continue;
} finally {
lastUpdate = System.currentTimeMillis();
}
}
// level.pause();
}
}).start();
return true;
}
/**
* Initializes buttons.
*/
private void initializeButtons() {
pauseToggle = new LToggleButton();
pauseToggle.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("btn_pause"),
Direction.NO_DIRECTION,
Looping.NONE));
pauseToggle.addActionListener(this);
pauseToggle.setActionCommand("pause");
pauseToggle.setShown(true);
pauseToggle.setPosition(new Point(10, 110));
speedToggle = new LToggleButton();
speedToggle.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("btn_speedup"),
Direction.NO_DIRECTION,
Looping.NONE));
speedToggle.addActionListener(this);
speedToggle.setActionCommand("speedup");
speedToggle.setShown(true);
speedToggle.setPosition(new Point(10, 170));
nukeToggle = new LToggleButton();
nukeToggle.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("btn_nuke"),
Direction.NO_DIRECTION,
Looping.NONE));
nukeToggle.addActionListener(this);
nukeToggle.setActionCommand("nuke");
nukeToggle.setShown(true);
nukeToggle.setPosition(new Point(10, 230));
}
/**
* Initializes the labels.
*/
private void initializeLabels() {
timeLabel = new LFancyLabel();
timeLabel.setForeground(Color.yellow);
timeLabel.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 20.0f));
timeLabel.setText("");
timeLabel.setPosition(new Point(760, 50));
timeLabel.setShown(true);
releasedLabel = new LFancyLabel();
releasedLabel.setForeground(Color.yellow);
releasedLabel
.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 20.0f));
releasedLabel.setText("");
releasedLabel.setPosition(new Point(10, 50));
releasedLabel.setShown(true);
savedLabel = new LFancyLabel();
savedLabel.setForeground(Color.yellow);
savedLabel.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 20.0f));
savedLabel.setText("");
savedLabel.setPosition(new Point(10, 70));
savedLabel.setShown(true);
outLabel = new LFancyLabel();
outLabel.setForeground(Color.yellow);
outLabel.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 20.0f));
outLabel.setText("");
outLabel.setPosition(new Point(10, 90));
outLabel.setShown(true);
lblLemmingFlow = new LFancyLabel();
lblLemmingFlow.setForeground(Color.black);
lblLemmingFlow.setFont(LFancyLabel
.DEFAULT_FONT.deriveFont(Font.BOLD, 20.0f));
lblLemmingFlow.setText("");
lblLemmingFlow.setPosition(new Point(20, 360));
lblLemmingFlow.setShown(true);
}
/**
* Whatever else needs to be initialized.
*/
protected void specialInitialize() { }
/**
* Describe <code>updateStuff</code> method here.
*
*/
protected void updateStuff() {
//Update UI:
timeLabel.setText(Long.toString(level.getTimeRemaining() / 1000));
releasedLabel.setText("Released: "
+ Integer.toString(numReleasedLemmings)
+ "/" + strNumLemmings);
savedLabel.setText("Saved: "
+ Integer.toString(level.getLemmingsSaved())
+ "/" + strLemmingsToSave);
outLabel.setText("Out: "
+ Integer.toString(numLemmingsOut));
lblLemmingFlow.setText(Integer
.toString(101 - level.getLemmingFlow()));
jobs.updateButtons();
specialUpdate();
renderer.updateCursor();
}
/**
* Handles the special update.
*/
protected void specialUpdate() { }
//@roseuid 4066242E013C
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
killed = true;
level = null;
return true;
}
/**
*This method will set the gamespeed;
*@param speed for gamespeed
*/
public void setGameSpeed(long speed) {
gameSpeed = speed;
}
/**
* Ends the level and goes on to the next state.
*/
public void endLevel() {
//Go to the level results state:
level.pause();
GameEngine.getInstance().setCurrentState(new LevelResultsState(level));
}
/**
* Handles it when the pause button is pressed.
*/
protected void handlePauseButton() {
singlePauseButtonHandle();
specialPauseHandle();
}
/**
* Describe <code>singlePauseButtonHandle</code> method here.
*
*/
protected void singlePauseButtonHandle() {
boolean p = pauseToggle.isSelected();
if (p) {
//We have paused:
level.pause();
} else {
//We have unpaused:
level.unpause();
}
}
/**
* Describe <code>getPauseButton</code> method here.
*
* @return a <code>LToggleButton</code> value
*/
protected LToggleButton getPauseButton() {
return pauseToggle;
}
/**
* Whatever else needs to be done for pause.
*/
protected void specialPauseHandle() { }
/**
* Handles it when the speedup button is pressed.
*/
protected void handleSpeedupButton() {
singleSpeedupButtonHandle();
specialSpeedupHandle();
}
/**
* Describe <code>singleSpeedupButtonHandle</code> method here.
*
*/
protected void singleSpeedupButtonHandle() {
boolean s = speedToggle.isSelected();
if (s) {
//We speed up:
//NOTE here that the name of the variable "gameSpeed" is a
//little misleading. It's really the delay between
//updates. Hence, when we want to speed up, we decrease this
//gamespeed.
gameSpeed /= 10;
} else {
//We slow down:
gameSpeed *= 10;
}
//Update the Level's notion of time:
level.setTimeIncrement(gameSpeed);
}
/**
* Whatever else needs to be done for speedup.
*/
protected void specialSpeedupHandle() { }
/**
* Handles it when the nuke button is pressed.
*/
protected void handleNukeButton() {
singleNukeButtonHandle();
specialNukeHandle();
}
/**
* Describe <code>singleNukeButtonHandle</code> method here.
*
*/
protected void singleNukeButtonHandle() {
boolean n = nukeToggle.isSelected();
if (n) {
//Gotta Nuke:
level.nukeEmALL();
} else {
//We are already nukin'
nukeToggle.toggleSelected();
}
}
/**
* Whatever else needs to be done for nuke.
*/
protected void specialNukeHandle() { }
/**
* Handles it when the increase button is pressed.
*/
protected void handleIncreaseButton() {
singleIncreaseButtonHandle();
specialIncreaseHandle();
}
/**
* Describe <code>singleIncreaseButtonHandle</code> method here.
*
*/
protected void singleIncreaseButtonHandle() {
level.increaseLemmingFlow();
}
/**
* Whatever else needs to be done for increase.
*/
protected void specialIncreaseHandle() { }
/**
* Handles it when the decrease button is pressed.
*/
protected void handleDecreaseButton() {
singleDecreaseButtonHandle();
specialDecreaseHandle();
}
/**
* Describe <code>singleDecreaseButtonHandle</code> method here.
*
*/
protected void singleDecreaseButtonHandle() {
level.decreaseLemmingFlow();
}
/**
* Whatever else needs to be done for decrease.
*/
protected void specialDecreaseHandle() { }
//@roseuid 4066242E016E
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*/
public void actionPerformed(ActionEvent ae) {
String a = ae.getActionCommand();
if (a.equals("pause")) {
handlePauseButton();
} else if (a.equals("speedup")) {
handleSpeedupButton();
} else if (a.equals("nuke")) {
handleNukeButton();
} else if (a.equals("increase")) {
handleIncreaseButton();
} else if (a.equals("decrease")) {
handleDecreaseButton();
}
}
/**
* Describe <code>isPauseSelected</code> method here.
*
* @return a <code>boolean</code> value
*/
protected boolean isPauseSelected() {
return pauseToggle.isSelected();
}
/**
* Describe <code>getGameSpeed</code> method here.
*
* @return an <code>int</code> value
*/
protected int getGameSpeed() {
return (int) gameSpeed;
}
/**
* Describe <code>getLemmingFlow</code> method here.
*
* @return an <code>int</code> value
*/
protected int getLemmingFlow() {
return (int) level.getLemmingFlow();
}
/**
* Listens to keys and stuff.
*/
private abstract class LKeyListener
extends LLeafComponent implements ITypable {
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
return true;
}
}
/**
*accessor for gpp
*@return GamePlayPanel
*/
public GamePlayPanel getGamePlayPanel() {
return gpp;
}
}

View File

@@ -0,0 +1,502 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Font;
import java.awt.event.ActionEvent;
//import java.awt.Point;
import java.awt.Color;
//import java.awt.Rectangle;
//import java.awt.Graphics;
//import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobSettings;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
//import edu.gatech.cs2335.lemmings.gui.ITypable;
//import edu.gatech.cs2335.lemmings.gui.LLeafComponent;
//import edu.gatech.cs2335.lemmings.gui.LComponent;
//import edu.gatech.cs2335.lemmings.gui.JobContainer;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.gui.LButton;
//import edu.gatech.cs2335.lemmings.gui.LToggleButton;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
//import edu.gatech.cs2335.lemmings.gui.GamePlayPanel;
//import edu.gatech.cs2335.lemmings.graphics.Renderer;
//import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.file.LevelReader;
import java.awt.Point;
//import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.gui.LIcon;
//import edu.gatech.cs2335.lemmings.gui.LButton;
//import edu.gatech.cs2335.lemmings.gui.LRootPane;
//import edu.gatech.cs2335.lemmings.graphics.Looping;
//import edu.gatech.cs2335.lemmings.graphics.Direction;
//import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
//import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* Class HelpState: Help screen...
*
* <PRE>
* Revision History:
* v1.0 (Apr. 21, 2004) - Created the HelpState class
* </PRE>
*
* @author <A HREF="mailto:gtg284h@mail.gatech.edu">Andrew Knight</A>
* @version Version 1.0, Apr. 21, 2004
*/
public class HelpState extends AbstractGameState {
/**
* Display debug information?
*/
public static final boolean VERBOSE = false;
/**
* The label to display Help title.
*/
private LFancyLabel title;
/**
* The label to display Help subtitle.
*/
private LFancyLabel subTitle;
/**
* The label to display singleplayer info.
*/
private LFancyLabel single;
/**
* The label to display singleplayer info.
*/
private LFancyLabel singlet;
/**
* The label to display multiplayer info.
*/
private LFancyLabel multi;
/**
* The label to display multiplayer info.
*/
private LFancyLabel multit;
/**
* The label to display hostbox info.
*/
private LFancyLabel hostBox;
/**
* The label to display hostbox info.
*/
private LFancyLabel hostBoxt;
/**
* The label to display portbox info.
*/
private LFancyLabel portBox;
/**
* The label to display portbox info.
*/
private LFancyLabel portBoxt;
/**
* The label to display hostButton info.
*/
private LFancyLabel hostButton;
/**
* The label to display hostButton info.
*/
private LFancyLabel hostButtont;
/**
* The label to display connectButton info.
*/
private LFancyLabel connectButton;
/**
* The label to display connectButton info.
*/
private LFancyLabel connectButtont;
/**
* The label to display credits info.
*/
private LFancyLabel credits;
/**
* The label to display credits info.
*/
private LFancyLabel creditst;
/**
* The label to display help info.
*/
private LFancyLabel help;
/**
* The label to display help info.
*/
private LFancyLabel helpt;
/**
* The label to display quit info.
*/
private LFancyLabel quit;
/**
* The label to display quit info.
*/
private LFancyLabel quitt;
/**
* The label to display back info.
*/
private LFancyLabel back;
/**
* The label to display back info.
*/
private LFancyLabel backt;
/**
* The label to display mainmenu info.
*/
private LFancyLabel mainMenu;
/**
* The label to display mainmenu info.
*/
private LFancyLabel mainMenut;
/**
* The label to other gameplay info.
*/
private LFancyLabel gamePlay;
/**
* Creates a new <code>HelpState</code> instance.
*/
public HelpState() {
//Initialize background:
LIcon bg = new LIcon(TileSetManager.getInstance()
.getTileSet("background"));
bg.setShown(true);
bg.setPosition(new Point(0, 0));
title = new LFancyLabel();
title.setForeground(Color.black);
title.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 35.0f));
title.setShown(true);
subTitle = new LFancyLabel();
subTitle.setForeground(Color.black);
subTitle.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
subTitle.setShown(true);
single = new LFancyLabel();
single.setForeground(Color.black);
single.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
single.setShown(true);
singlet = new LFancyLabel();
singlet.setForeground(Color.black);
singlet.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 16.0f));
singlet.setShown(true);
multi = new LFancyLabel();
multi.setForeground(Color.black);
multi.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
multi.setShown(true);
multit = new LFancyLabel();
multit.setForeground(Color.black);
multit.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 16.0f));
multit.setShown(true);
hostBox = new LFancyLabel();
hostBox.setForeground(Color.black);
hostBox.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 14.0f));
hostBox.setShown(true);
hostBoxt = new LFancyLabel();
hostBoxt.setForeground(Color.black);
hostBoxt.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 12.0f));
hostBoxt.setShown(true);
portBox = new LFancyLabel();
portBox.setForeground(Color.black);
portBox.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 14.0f));
portBox.setShown(true);
portBoxt = new LFancyLabel();
portBoxt.setForeground(Color.black);
portBoxt.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 12.0f));
portBoxt.setShown(true);
hostButton = new LFancyLabel();
hostButton.setForeground(Color.black);
hostButton.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 14.0f));
hostButton.setShown(true);
hostButtont = new LFancyLabel();
hostButtont.setForeground(Color.black);
hostButtont.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 12.0f));
hostButtont.setShown(true);
connectButton = new LFancyLabel();
connectButton.setForeground(Color.black);
connectButton.setFont(
LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 14.0f));
connectButton.setShown(true);
connectButtont = new LFancyLabel();
connectButtont.setForeground(Color.black);
connectButtont.setFont(
LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 12.0f));
connectButtont.setShown(true);
credits = new LFancyLabel();
credits.setForeground(Color.black);
credits.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
credits.setShown(true);
creditst = new LFancyLabel();
creditst.setForeground(Color.black);
creditst.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 16.0f));
creditst.setShown(true);
help = new LFancyLabel();
help.setForeground(Color.black);
help.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
help.setShown(true);
helpt = new LFancyLabel();
helpt.setForeground(Color.black);
helpt.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 16.0f));
helpt.setShown(true);
quit = new LFancyLabel();
quit.setForeground(Color.black);
quit.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
quit.setShown(true);
quitt = new LFancyLabel();
quitt.setForeground(Color.black);
quitt.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 16.0f));
quitt.setShown(true);
back = new LFancyLabel();
back.setForeground(Color.black);
back.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
back.setShown(true);
backt = new LFancyLabel();
backt.setForeground(Color.black);
backt.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 16.0f));
backt.setShown(true);
mainMenu = new LFancyLabel();
mainMenu.setForeground(Color.black);
mainMenu.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
mainMenu.setShown(true);
mainMenut = new LFancyLabel();
mainMenut.setForeground(Color.black);
mainMenut.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 16.0f));
mainMenut.setShown(true);
gamePlay = new LFancyLabel();
gamePlay.setForeground(Color.black);
gamePlay.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.PLAIN, 18.0f));
gamePlay.setShown(true);
//Initialize button:
LButton goOn = new LButton();
goOn.setPosition(new Point(10, 590));
goOn.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mpb_back"),
Direction.NO_DIRECTION, Looping.NONE));
goOn.addActionListener(this);
goOn.setActionCommand("goOn");
goOn.setShown(true);
//Initialize UI:
LRootPane np = new LRootPane();
np.addChild(bg);
np.addChild(goOn);
np.addChild(title);
np.addChild(subTitle);
np.addChild(single);
np.addChild(singlet);
np.addChild(multi);
np.addChild(multit);
np.addChild(hostBox);
np.addChild(hostBoxt);
np.addChild(portBox);
np.addChild(portBoxt);
np.addChild(hostButton);
np.addChild(hostButtont);
np.addChild(connectButton);
np.addChild(connectButtont);
np.addChild(credits);
np.addChild(creditst);
np.addChild(help);
np.addChild(helpt);
np.addChild(quit);
np.addChild(quitt);
np.addChild(back);
np.addChild(backt);
np.addChild(mainMenu);
np.addChild(mainMenut);
np.addChild(gamePlay);
setUI(np);
}
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*/
public boolean initialize() {
title.setText("Help");
title.setPosition(new Point(30, 80));
subTitle.setText("Menu Navigation:");
subTitle.setPosition(new Point(65, 125));
single.setText("\"SinglePlayer\" Button:");
single.setPosition(new Point(100, 165));
singlet.setText("Starts a SinglePlayer game");
singlet.setPosition(new Point(302, 165));
multi.setText("\"MultiPlayer\" Button:");
multi.setPosition(new Point(100, 205));
multit.setText("Starts a MultiPlayer game");
multit.setPosition(new Point(290, 205));
hostBox.setText("\"Host\" TextField:");
hostBox.setPosition(new Point(135, 230));
hostBoxt.setText("IP address of host");
hostBoxt.setPosition(new Point(260, 230));
portBox.setText("\"Port\" TextField:");
portBox.setPosition(new Point(135, 255));
portBoxt.setText("Port on host");
portBoxt.setPosition(new Point(260, 255));
hostButton.setText("\"Host\" Button:");
hostButton.setPosition(new Point(135, 280));
hostButtont.setText("Hosts a multiplayer session");
hostButtont.setPosition(new Point(247, 280));
connectButton.setText("\"Connect\" Button:");
connectButton.setPosition(new Point(135, 302));
connectButtont.setText("Connects to a multiplayer session");
connectButtont.setPosition(new Point(270, 302));
credits.setText("\"Credits\" Button:");
credits.setPosition(new Point(100, 330));
creditst.setText("Displays information about the creators");
creditst.setPosition(new Point(255, 330));
help.setText("\"Help\" Button:");
help.setPosition(new Point(100, 365));
helpt.setText("Displays this screen");
helpt.setPosition(new Point(233, 365));
quit.setText("\"Quit\" Button:");
quit.setPosition(new Point(100, 405));
quitt.setText("Exits the game");
quitt.setPosition(new Point(229, 405));
back.setText("\"Back\" Button:");
back.setPosition(new Point(100, 445));
backt.setText("Returns to previous menu");
backt.setPosition(new Point(236, 445));
mainMenu.setText("\"MainMenu\" Button:");
mainMenu.setPosition(new Point(100, 482));
mainMenut.setText("Returns to the main menu");
mainMenut.setPosition(new Point(280, 482));
gamePlay.setText("For help on game-play, refer to the html user manual.");
gamePlay.setPosition(new Point(200, 540));
if (VERBOSE) {
System.err.println("HelpState: I am initialized!");
System.err.flush();
}
return true;
}
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
if (VERBOSE) {
System.err.println("HelprState: I am cleaned up!");
System.err.flush();
}
return true;
}
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*/
public void actionPerformed(ActionEvent ae) {
if (VERBOSE) {
System.err.println("HelpState: Got some action - "
+ ae.getActionCommand());
System.err.flush();
}
//Go back to the Multiplayer setup state:
GameEngine.getInstance().setCurrentState(new MainMenuState());
}
}

View File

@@ -0,0 +1,22 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* Interface ICleanable: The implementing objects will be able to be
* cleaned up.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 15, 2004) - Created the ICleanable interface
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 15, 2004
*/
public interface ICleanable {
/**
* Cleans up all references for the garbage collector.
*/
public void cleanUp();
}

View File

@@ -0,0 +1,133 @@
package edu.gatech.cs2335.lemmings.engine;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.physics.PhysicsObject;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJob;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobFactory;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobSettings;
/**
* Represents a generic lemming, that can move around, and do
* stuff. What exactly it does is decided by the lemming's occupation.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @see LemmingJob
*/
public final class Lemming extends PhysicsObject implements ICleanable {
/**
* What the lemming does.
*/
private LemmingJob occupation;
/**
* The physics object associated with the lemming.
*/
//private PhysicsObject physics;
/**
* Creates a new <code>Lemming</code> instance.
*
*/
public Lemming() {
//Set up physics:
super();
//physics = new PhysicsObject();
//Set Occupation:
setOccupation(LemmingJobSettings.JOB_ID_WALKER);
}
/**
* Describe <code>cleanUp</code> method here.
*
*/
public void cleanUp() {
occupation.cleanUp();
occupation = null;
}
/**
* Access method for the occupation property.
*
* @return the current value of the occupation property
*/
public LemmingJob getOccupation() {
return occupation;
}
/**
* Sets the value of the occupation property.
*
* @param id the id of the occupation to give this lemming.
*/
public void setOccupation(String id) {
occupation = LemmingJobFactory.getInstance().makeJob(id);
occupation.setOwner(this);
updateDirection();
}
/**
* Describe <code>setOccupation</code> method here.
*
* @param job a <code>LemmingJob</code> value
*/
public void setOccupation(LemmingJob job) {
job.setOwner(this);
occupation = job;
updateDirection();
}
/**
* Access method for the physics property.
*
* @return the current value of the physics property
*/
public PhysicsObject getPhysics() {
//Should deprecate this. :( Really should.
return this;
// return physics;
}
/**
* This method should be called whenever the lemming's direction
* changes. It will update the current animation with the new
* direction of movement.
*/
public void updateDirection() {
int xVelocity = getVelocity().getI();
Direction dir;
if (xVelocity == 0) {
dir = Direction.NO_DIRECTION;
} else if (xVelocity < 0) {
dir = Direction.LEFT;
} else {
dir = Direction.RIGHT;
}
if (occupation.getRegularAnimation().getMovementDirection() != dir) {
occupation.getRegularAnimation().setMovementDirection(dir);
}
if (occupation.getFallingAnimation().getMovementDirection() != dir) {
occupation.getFallingAnimation().setMovementDirection(dir);
}
}
/**
* Returns a string representation of a lemming.
*
* @return a <code>String</code> value
*/
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Lemming: (").append(occupation.getId()).append(")\n");
sb.append(toString()).append("\n");
return sb.toString();
}
}

View File

@@ -0,0 +1,47 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* Class LemmingDrown: This pretty sprite is created when the lemming
* drowns.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 14, 2004) - Created the LemmingDrown class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 14, 2004
*/
public class LemmingDrown extends PrettySprite {
/**
* Creates a new <code>LemmingDrown</code> instance.
*/
public LemmingDrown() {
super("drown");
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (getAnimation().getCurrentFrame() == 0) {
//The animation is through.
//Create an angel:
PrettySprite a = new LemmingsSoul();
a.getPosition().setPoint(getPosition().getX(),
getPosition().getY(),
getPosition().getZ());
getLevel().addPrettySprite(a);
//We are done:
return true;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,47 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* Class LemmingSplat: When the lemming falls too far and smashes into
* the ground, this blood-splattering animation will be played.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 12, 2004) - Created the LemmingSplat class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 12, 2004
*/
public class LemmingSplat extends PrettySprite {
/**
* Creates a new <code>LemmingSplat</code> instance.
*/
public LemmingSplat() {
super("splat");
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (getAnimation().getCurrentFrame() == 0) {
//The animation is through.
//Create an angel:
PrettySprite a = new LemmingsSoul();
a.getPosition().setPoint(getPosition().getX(),
getPosition().getY(),
getPosition().getZ());
getLevel().addPrettySprite(a);
//We are done:
return true;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,39 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* Class LemmingsSoul: You have failed, The lemming is DEAD, His soul
* goes to heaven.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the LemmingsSoul class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 11, 2004
*/
public class LemmingsSoul extends PrettySprite {
/**
* Creates a new <code>LemmingsSoul</code> instance.
*/
public LemmingsSoul() {
super("angel");
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (getPosition().getY() <= 5) {
//We are almost at the top of the screen... That's close enough.
return true;
}
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,386 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobSettings;
/**
* This class contains the data associated with a certain level. It is
* essentially your template to create playable levels.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
*/
public class LevelData {
/**
* The thumbnail to display for the level.
*/
private BufferedImage thumbnail;
/**
* The id of the level. This is essentially the name of the
* directory that the file was loaded from.
*/
private String id;
/**
* The name of the level.
*/
private String name;
/**
* The number of lemmings initially let out in the level.
*/
private int numLemmings;
/**
* The percentage of lemmings we need to save. Needs to be between 0 and 1.
*/
private int percentToSave;
/**
* The time limit for the level.
*/
private long timeLimit;
/**
* The position of the entrance point on the map.
*/
private Point entrancePoint;
/**
* The position of the exit point on the map.
*/
private Point exitPoint;
/**
* The list of all portals available in this level.
*/
private List portals;
/**
* This map will contain the number of jobs of each type available
* for the level.
*/
private Map jobMap;
/**
* The level of water on the level. Uhhh, yeah. -1 means there is no
* water on the level.
*/
private int waterLevel;
/**
* The lines of the briefing to display before the level.
*/
private String[] mapBriefing;
/**
* The lines of the win statement to display after the level.
*/
private String[] winStatement;
/**
* Creates a new <code>LevelData</code> instance.
*
*/
public LevelData() {
thumbnail = null;
entrancePoint = new Point(0, 0);
exitPoint = new Point(0, 0);
jobMap = new HashMap(LemmingJobSettings.ALL_JOB_NAMES.length);
portals = new Vector();
name = "";
numLemmings = 0;
percentToSave = 0;
timeLimit = 0;
waterLevel = -1;
mapBriefing = null;
winStatement = null;
}
/**
* Access method for the thumbnail property.
*
* @return the current value of the thumbnail property
*/
public BufferedImage getThumbnail() {
return thumbnail;
}
/**
* Sets the value of the thumbnail property.
*
* @param aThumbnail the new value of the thumbnail property
*/
public void setThumbnail(BufferedImage aThumbnail) {
thumbnail = aThumbnail;
}
/**
* Access method for the name property.
*
* @return the current value of the name property
*/
public String getName() {
return name;
}
/**
* Sets the value of the name property.
*
* @param aName the new value of the name property
*/
public void setName(String aName) {
name = aName;
}
/**
* Access method for the id property.
*
* @return the current value of the id property
*/
public String getId() {
return id;
}
/**
* Sets the value of the id property.
*
* @param aId the new value of the id property
*/
public void setId(String aId) {
id = aId;
}
/**
* Access method for the numLemmings property.
*
* @return the current value of the numLemmings property
*/
public int getNumLemmings() {
return numLemmings;
}
/**
* Sets the value of the numLemmings property.
*
* @param aNumLemmings the new value of the numLemmings property
*/
public void setNumLemmings(int aNumLemmings) {
numLemmings = aNumLemmings;
}
/**
* Access method for the percentToSave property.
*
* @return the current value of the percentToSave property
*/
public int getPercentToSave() {
return percentToSave;
}
/**
* Sets the value of the percentToSave property.
*
* @param aPercentToSave the new value of the percentToSave property
*/
public void setPercentToSave(int aPercentToSave) {
percentToSave = aPercentToSave;
}
/**
* Access method for the timeLimit property.
*
* @return the current value of the timeLimit property
*/
public long getTimeLimit() {
return timeLimit;
}
/**
* Sets the value of the timeLimit property.
*
* @param aTimeLimit the new value of the timeLimit property
*/
public void setTimeLimit(long aTimeLimit) {
timeLimit = aTimeLimit;
}
/**
* Get the value of entrancePoint.
* @return value of entrancePoint.
*/
public Point getEntrancePoint() {
return entrancePoint;
}
/**
* Set the value of entrancePoint.
* @param v Value to assign to entrancePoint.
*/
public void setEntrancePoint(Point v) {
this.entrancePoint = v;
}
/**
* Set the value of entrancePoint.
* @param x an <code>int</code> value
* @param y an <code>int</code> value
*/
public void setEntrancePoint(int x, int y) {
this.entrancePoint.x = x;
this.entrancePoint.y = y;
}
/**
* Get the value of exitPoint.
* @return value of exitPoint.
*/
public Point getExitPoint() {
return exitPoint;
}
/**
* Set the value of exitPoint.
* @param v Value to assign to exitPoint.
*/
public void setExitPoint(Point v) {
this.exitPoint = v;
}
/**
* Set the value of exitPoint.
* @param x an <code>int</code> value
* @param y an <code>int</code> value
*/
public void setExitPoint(int x, int y) {
this.exitPoint.x = x;
this.exitPoint.y = y;
}
/**
* Get the value of waterLevel.
* @return value of waterLevel.
*/
public int getWaterLevel() {
return waterLevel;
}
/**
* Set the value of waterLevel. Note that -1 is a magic value which
* means there is no water on the level. Other negative numbers will
* be ABSed before application.
* @param v Value to assign to waterLevel.
*/
public void setWaterLevel(int v) {
if (v < -1) {
this.waterLevel = -v;
} else {
this.waterLevel = v;
}
}
/**
* Adds the number of jobs for the appropriate job id for the level.
*
* @param idd a <code>String</code> value
* @param num an <code>int</code> value
*/
public void addJob(String idd, int num) {
jobMap.put(idd, new Integer(num));
}
/**
* Returns the number of jobs of the specified type for this level.
*
* @param idd a <code>String</code> value
* @return an <code>int</code> value
*/
public int getNumJobs(String idd) {
if (!jobMap.containsKey(idd)) { return 0; }
Integer i = (Integer) jobMap.get(idd);
return i.intValue();
}
/**
* Returns the job map.
*
* @return a <code>Map</code> value
*/
public Map getJobMap() {
return jobMap;
}
/**
* Sets the job map
* @param jm , map of jobs to set
*/
public void setJobMap(Map jm) {
jobMap = jm;
}
/**
* Describe <code>getPortals</code> method here.
*
* @return a <code>List</code> value
*/
public List getPortals() {
return portals;
}
/**
* Describe <code>addPortal</code> method here.
*
* @param type a <code>String</code> value
* @param value a <code>String</code> value
*/
public void addPortal(String type, String value) {
Portal p = PortalFactory.getInstance().makePortal(type, value);
if (p != null) {
portals.add(p);
if (type.equals(PortalFactory.PORTAL_TYPES[0])) {
entrancePoint.x = p.getPhysics().getPosition().getX();
entrancePoint.y = p.getPhysics().getPosition().getY();
} else if (type.equals(PortalFactory.PORTAL_TYPES[1])) {
exitPoint.x = p.getPhysics().getPosition().getX();
exitPoint.y = p.getPhysics().getPosition().getY();
}
}
}
/**
* Returns the string representation of the level.
*
* @return a <code>String</code> value
*/
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Level (").append(getId()).append(")\n");
sb.append("\tName: ").append(getName()).append("\n");
sb.append("\tLemmings: ").append(getNumLemmings()).append("\n");
sb.append("\tSave: ").append(getPercentToSave() * 100).append("%\n");
sb.append("\tTime Limit: ").append(getTimeLimit()).append(" sec\n");
sb.append("\tEntrance: (").append(getEntrancePoint().x)
.append(", ").append(getEntrancePoint().y).append(")\n");
sb.append("\tExit: (").append(getExitPoint().x)
.append(", ").append(getExitPoint().y).append(")\n");
return sb.toString();
}
}

View File

@@ -0,0 +1,33 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* Class LevelEntrance: This is where lemmings enter a level.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 28, 2004) - Created the LevelEntrance class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 28, 2004
*/
public class LevelEntrance extends Portal {
/**
* Creates a new <code>LevelEntrance</code> instance.
*/
public LevelEntrance() {
super(PortalFactory.PORTAL_TYPES[0]);
}
/**
* This function will be called when a lemming enters this portal.
*
* @param l a <code>Lemming</code> value
*/
public void processLemmingEntry(Lemming l) {
//This is an *entry* nothing to do with the lemming.
}
}

View File

@@ -0,0 +1,39 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* Class LevelExit: This is where lemmings exit a level.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 28, 2004) - Created the LevelExit class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 28, 2004
*/
public class LevelExit extends Portal {
/**
* Creates a new <code>LevelExit</code> instance.
*/
public LevelExit() {
super(PortalFactory.PORTAL_TYPES[1]);
}
/**
* This function will be called when a lemming enters this portal.
*
* @param l a <code>Lemming</code> value
*/
public void processLemmingEntry(Lemming l) {
//This lemming has been saved. Wohoo!
if (GameEngine.VERBOSE) {
System.out.println("Homie's home (aka a Lemming has been saved)");
System.out.flush();
}
getParent().killLemming(l, true);
}
}

View File

@@ -0,0 +1,263 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Font;
//import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
//import java.util.Random;
import edu.gatech.cs2335.lemmings.file.LevelReader;
import edu.gatech.cs2335.lemmings.gui.LIcon;
import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LLabel;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* Class LevelResultsState: Shows the results of a level.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 14, 2004) - Created the LevelResultsState class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 14, 2004
*/
public class LevelResultsState extends AbstractGameState {
/**
* Show debug output?
*/
private static final boolean VERBOSE = false;
/**
* The level, whose results we are showing.
*/
private Level level;
/**
* Contains the name of the level.
*/
private LFancyLabel lblLevelName;
/**
* Something to show when the user has won/lost.
*/
private LLabel[] lblSayings;
/**
* How many lemmings were saved.
*/
private LLabel lblLemmingsSaved;
/**
* How many lemmings were not saved.
*/
private LLabel lblLemmingsKilled;
/**
* How much time did it take?
*/
private LLabel lblTimeTaken;
/**
* Describe variable <code>lemmingsSaved</code> here.
*
*/
private int lemmingsSaved;
/**
* Describe variable <code>lemmingsToSave</code> here.
*
*/
private int lemmingsToSave;
/**
* Creates a new <code>LevelResultsState</code> instance.
*@param l len
*/
public LevelResultsState(Level l) {
level = l;
}
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*/
public boolean initialize() {
LRootPane np = new LRootPane();
LIcon bg = new LIcon(TileSetManager.getInstance().getTileSet("bg_result"));
bg.setShown(true);
bg.setPosition(new Point(0, 0));
//Buttons:
LButton back = new LButton();
back
.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("rb_back"),
Direction.NO_DIRECTION,
Looping.NONE));
back.setPosition(new Point(10, 590));
back.setShown(true);
back.setActionCommand("back");
back.addActionListener(this);
LButton play = new LButton();
play
.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("rb_play"),
Direction.NO_DIRECTION,
Looping.NONE));
play.setPosition(new Point(680, 590));
play.setShown(true);
play.setActionCommand("play");
play.addActionListener(this);
//Labels:
lblLevelName = new LFancyLabel();
lblLevelName.setFont(LFancyLabel.DEFAULT_FONT
.deriveFont(Font.BOLD, 30.0f));
lblLevelName.setText(level.getData().getName());
lblLevelName.setPosition(new Point(250, 180));
lblLevelName.setShown(true);
//TODOXC: Set this to whatever it needs to be eventually.
lblSayings = null;
lemmingsSaved = level.getLemmingsSaved();
lemmingsToSave = level.getData().getPercentToSave();
lemmingsToSave *= level.getData().getNumLemmings();
lemmingsToSave /= 100;
lblLemmingsSaved = new LLabel();
lblLemmingsSaved.setText("Lemmings Saved: " + lemmingsSaved
+ "/" + lemmingsToSave);
lblLemmingsSaved.setPosition(new Point(300, 220));
lblLemmingsSaved.setShown(true);
lblLemmingsKilled = new LLabel();
lblLemmingsKilled
.setText("Lemmings Dead: "
+ Math.max(level.getData().getNumLemmings()
- level.getLemmingsSaved(), 0));
lblLemmingsKilled.setPosition(new Point(300, 240));
lblLemmingsKilled.setShown(true);
lblTimeTaken = new LLabel();
lblTimeTaken
.setText("Time Taken: "
+ (level.getData().getTimeLimit()
- (level.getTimeRemaining() / 1000)));
lblTimeTaken.setPosition(new Point(300, 260));
lblTimeTaken.setShown(true);
np.addChild(bg);
np.addChild(lblLevelName);
np.addChild(lblLemmingsSaved);
np.addChild(lblLemmingsKilled);
np.addChild(lblTimeTaken);
np.addChild(back);
np.addChild(play);
setUI(np);
return true;
}
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
level.cleanUp();
level = null;
return true;
}
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*/
public void actionPerformed(ActionEvent ae) {
String a = ae.getActionCommand();
if (a.equals("back")) {
//Go back to the main menu:
GameEngine.getInstance().setCurrentState(new MainMenuState());
} else if (a.equals("play")) {
//Load next level if this one was passed, and this level, if it
//was failed.
String name = level.getData().getId();
if (VERBOSE) {
System.out.println("LevelResultsState: Want to play again...");
System.out.println("\tLemmings Saved: " + lemmingsSaved);
System.out.println("\tRequired to Advance: " + lemmingsToSave);
System.out.flush();
}
if (lemmingsSaved >= lemmingsToSave) {
//The player saved enough lemmings.
String nextLevel = LevelReader.getInstance().findNextLevel(name);
if (nextLevel == null) {
//Went through all levels...
//Show some kind of "You Win!" screen????? Would be
//good. But it's too late now.
GameEngine.getInstance().setCurrentState(new MainMenuState());
} else {
//Go to the next level:
GameEngine.getInstance().startSingleLevel(nextLevel);
}
} else {
//Go to same level:
GameEngine.getInstance().startSingleLevel(name);
}
}
}
/**
* Describe <code>getLevel</code> method here.
*
* @return a <code>Level</code> value
*/
public Level getLevel() {
return level;
}
/**
* Describe <code>setLevel</code> method here.
*
* @param l a <code>Level</code> value
*/
public void setLevel(Level l) {
level = l;
}
}

View File

@@ -0,0 +1,143 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.gui.LIcon;
import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.gui.LevelContainer;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
import edu.gatech.cs2335.lemmings.file.LevelReader;
/**
* This is the main menu state. It displays the main menu stuff, which
* allows the player to select to play the single player game, the
* multiplayer game, or quit.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
*/
public class LevelSelectionState extends AbstractGameState {
/**
* The container with the levels.
*/
private LevelContainer container;
/**
* Creates a new <code>LevelSelectionState</code> instance.
*
*/
public LevelSelectionState() {
//Make sure that the resources we want are loaded:
TileSetManager.getInstance().getTileSet("background");
}
//@roseuid 4065D29D005F
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*
*/
public boolean initialize() {
//Initialize background:
LIcon bg = new LIcon(TileSetManager.getInstance()
.getTileSet("background"));
bg.setShown(true);
bg.setPosition(new Point(0, 0));
//Initialize Levels:
container = new LevelContainer();
String[] levels = LevelReader.getInstance().getLevelList();
container.setShown(true);
container.setPosition(new Point(20, 40));
container.setSize(new Dimension(770, 500));
for (int i = 0; i < levels.length; i++) {
container.addLevelButton(levels[i]);
}
//Initialize buttons:
LButton back = new LButton();
back
.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("lb_back"),
Direction.NO_DIRECTION,
Looping.NONE));
back.setPosition(new Point(10, 590));
back.setShown(true);
back.setActionCommand("back");
back.addActionListener(this);
LButton play = new LButton();
play
.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("lb_play"),
Direction.NO_DIRECTION,
Looping.NONE));
play.setPosition(new Point(680, 590));
play.setShown(true);
play.setActionCommand("play");
play.addActionListener(this);
//Initialize UI:
LRootPane np = new LRootPane();
np.addChild(bg);
np.addChild(container);
np.addChild(back);
np.addChild(play);
setUI(np);
return true;
}
//@roseuid 4065D29D0073
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
return true;
}
//@roseuid 4065D29D0091
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*
*/
public void actionPerformed(ActionEvent ae) {
String a = ae.getActionCommand();
if (a.equals("back")) {
//Go back to the main menu:
GameEngine.getInstance().setCurrentState(new MainMenuState());
} else if (a.equals("play")) {
//Play!
GameEngine.getInstance().startSingleLevel(container.getSelectedLevel());
}
/*
//The button has been clicked. Start the level:
//Transfer to game now:
GamePlayState gps = new GamePlayState("test_level");
GameEngine.getInstance().setCurrentState(gps);
*/
}
}

View File

@@ -0,0 +1,179 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Font;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.util.Random;
import edu.gatech.cs2335.lemmings.file.LevelReader;
import edu.gatech.cs2335.lemmings.gui.LIcon;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* Class LoadLevelState: The display to show random humorous messages
* while the user is waiting for the level to load.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 01, 2004) - Created the LoadLevelState class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 01, 2004
*/
public class LoadLevelState extends AbstractGameState {
/**
* The level that we will be loading here.
*/
private Level level;
/**
* Number of millis we want to pass between the sayings.
*/
// private final long SAYING_DELAY = 4000;
/**
* List of things to say to the user.
*/
private final String[] sayings = {
"Drawing graffiti..",
"Circlin' da 'hood...",
"Intimidating nubs...",
"Chillin'...",
"Doing stuff...",
"Bling Blingin'...",
"You should be playin' Doom now...",
"Makin' Waves...",
"Writin' da JUnit tests...",
"Tracin' rays...",
"Studyin' Physics...",
"Renderin' Sprites...",
"Drawing pretty graphics...",
"Checkstylin'...",
"Floatin'...",
"Makin' final preparations...",
"Updatin' the repository...",
"Writing clever sayings...",
"Isn't it annoying to wait like this?",
"Blockin' unnecessary movement...",
"Givin' out the drills...",
"Assessing the llama population...",
};
/**
* The label to display our message to the user in.
*/
private LFancyLabel message;
/**
* Random number generator.
*/
private Random generator;
/**
* Creates a new <code>LoadLevelState</code> instance.
*/
public LoadLevelState() {
level = null;
generator = new Random(System.currentTimeMillis());
//Initialize background:
LIcon bg = new LIcon(TileSetManager.getInstance()
.getTileSet("bg_loading"));
bg.setShown(true);
bg.setPosition(new Point(0, 0));
//Initialize label:
message = new LFancyLabel();
message.setForeground(Color.black);
message.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
message.setText("");
message.setShown(true);
message.setPosition(new Point(300, 300));
//Initialize UI:
LRootPane np = new LRootPane();
np.addChild(bg);
np.addChild(message);
setUI(np);
}
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*/
public boolean initialize() {
updateText();
return true;
}
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
level = null;
return true;
}
//@roseuid 4066242E016E
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*/
public void actionPerformed(ActionEvent ae) {
//The user can't click anything here, so...
}
/**
* Loads the level with the specified id.
*
* @param id a <code>String</code> value
*/
public synchronized void loadLevel(final String id) {
new Thread(new Runnable() {
public void run() {
level = LevelReader.getInstance().loadLevel(id);
levelLoaded();
}
}).start();
}
/**
* This function will be done when the level is done loading.
*/
private void levelLoaded() {
//Transfer control to Game Play State:
GameEngine.getInstance()
.setCurrentState(new GamePlayState(level));
}
/**
* Updates the text in the label.
*/
private void updateText() {
message.setText(sayings[generator.nextInt(sayings.length)]);
}
}

View File

@@ -0,0 +1,208 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Point;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.gui.LIcon;
import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.gui.LLeafComponent;
import edu.gatech.cs2335.lemmings.gui.LComponent;
import edu.gatech.cs2335.lemmings.gui.ITypable;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* This is the main menu state. It displays the main menu stuff, which
* allows the player to select to play the single player game, the
* multiplayer game, or quit.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
*/
public class MainMenuState extends AbstractGameState {
/**
* Creates a new <code>MainMenuState</code> instance.
*
*/
public MainMenuState() {
//Make sure that the resources we want are loaded:
TileSetManager.getInstance().getTileSet("bg_main");
TileSetManager.getInstance().getTileSet("mmb_single");
TileSetManager.getInstance().getTileSet("mmb_multi");
TileSetManager.getInstance().getTileSet("mmb_quit");
}
//@roseuid 4065D29D005F
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*
*/
public boolean initialize() {
//Initialize background:
LIcon bg = new LIcon(TileSetManager.getInstance().getTileSet("bg_main"));
bg.setShown(true);
bg.setPosition(new Point(0, 0));
//Initialize Buttons:
Point pos = new Point(63, 278);
LButton single = new LButton();
single.setPosition(new Point(pos));
single.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mmb_single"),
Direction.NO_DIRECTION, Looping.NONE));
single.addActionListener(this);
single.setActionCommand("single");
single.setShown(true);
pos.x = 575;
pos.y = 292;
LButton multi = new LButton();
multi.setPosition(new Point(pos));
multi.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mmb_multi"),
Direction.NO_DIRECTION, Looping.NONE));
multi.addActionListener(this);
multi.setActionCommand("multi");
multi.setShown(true);
pos.x = 315;
pos.y = 278;
LButton cred = new LButton();
cred.setPosition(new Point(pos));
cred.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mmb_credits"),
Direction.NO_DIRECTION, Looping.NONE));
cred.addActionListener(this);
cred.setActionCommand("cred");
cred.setShown(true);
pos.x = 15;
pos.y = 530;
LButton help = new LButton();
help.setPosition(new Point(pos));
help.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mmb_help"),
Direction.NO_DIRECTION, Looping.NONE));
help.addActionListener(this);
help.setActionCommand("help");
help.setShown(true);
pos.x = 690;
pos.y = 515;
LButton quit = new LButton();
quit.setPosition(new Point(pos));
quit.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mmb_quit"),
Direction.NO_DIRECTION, Looping.NONE));
quit.addActionListener(this);
quit.setActionCommand("quit");
quit.setShown(true);
//Initialize UI:
LRootPane np = new LRootPane();
np.addChild(bg);
np.addChild(single);
np.addChild(multi);
np.addChild(cred);
np.addChild(help);
np.addChild(quit);
np.addChild(new LKeyListener() {
public void processKeyTyped(char key, int modifiers) {
if (key == 'a') {
GameEngine.getInstance()
.setCurrentState(new AttractState("Attract"));
}
}
});
setUI(np);
return true;
}
//@roseuid 4065D29D0073
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
return true;
}
//@roseuid 4065D29D0091
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*
*/
public void actionPerformed(ActionEvent ae) {
String ac = ae.getActionCommand();
if (GameEngine.VERBOSE) {
System.out.println("MainMenuState: Button clicked - " + ac);
System.out.flush();
}
if ("single".equals(ac)) {
//Go to level selection:
GameEngine.getInstance().setCurrentState(new LevelSelectionState());
} else if ("multi".equals(ac)) {
//Go to multiplayer setup screen:
GameEngine.getInstance().setCurrentState(new MultiplayerSetupState());
} else if ("cred".equals(ac)) {
//Go to the credits screen:
GameEngine.getInstance().setCurrentState(new CreditsState());
} else if ("help".equals(ac)) {
GameEngine.getInstance().setCurrentState(new HelpState());
} else if ("quit".equals(ac)) {
System.exit(0);
}
}
/**
* Listens to keys and stuff.
*/
private abstract class LKeyListener
extends LLeafComponent implements ITypable {
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
return true;
}
}
}

View File

@@ -0,0 +1,473 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
//import java.awt.Rectangle;
import java.awt.AlphaComposite;
import edu.gatech.cs2335.lemmings.graphics.TileSet;
import edu.gatech.cs2335.lemmings.graphics.ImageLoader;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
/**
* The map on which stuff will be happening.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Caban</A>
*/
public class Map {
/**
* The background image to display in this map.
*/
private BufferedImage background;
/**
* The texture image to use for the ground.
*/
private BufferedImage texture;
/**
* The unbreakable texture used for ground
*/
private BufferedImage unbreakableTexture;
/**
* This is the map itself. It is not technically a "map", but rather
* a color key for the map.
*/
private BufferedImage map;
/**
* Unbreakable map
*/
private BufferedImage unbreakableMap;
/**
* This is the actual displayed map
*/
private BufferedImage gameMap;
/**
* This is the indestructable game Map
*/
private BufferedImage indestructGameMap;
/**
* The water animation displayed on the map.
*/
private AnimatedSprite waterAnimation;
//////////////////
// Constructors //
//////////////////
/**
* Creates a new <code>Map</code> instance.
*
*/
public Map() {
background = null;
map = null;
}
///////////////////////////
// Accessors / Modifiers //
///////////////////////////
/**
* Access method for the background property.
*
* @return the current value of the background property
*/
public BufferedImage getBackground() {
return background;
}
/**
* Sets the value of the background property.
*
* @param aBackground the new value of the background property
*/
public void setBackground(BufferedImage aBackground) {
background = aBackground;
}
/**
* Access method for the texture property.
*
* @return the current value of the texture property
*/
public BufferedImage getTexture() {
return texture;
}
/**
* Sets the value of the texture property.
*
* @param aTexture the new value of the texture property
*/
public void setTexture(BufferedImage aTexture) {
texture = aTexture;
}
/**
* Method setUnbreakableTexture
*
*
* @param unbreakableTexture ub
*
*/
public void setUnbreakableTexture(BufferedImage unbreakableTexture) {
this.unbreakableTexture = unbreakableTexture;
}
/**
* Method getUnbreakableTexture
*
*
* @return BufferedImage
*
*/
public BufferedImage getUnbreakableTexture() {
return (this.unbreakableTexture);
}
/**
* Access method for the map property.
*
* @return the current value of the map property
*/
public BufferedImage getMap() {
return map;
}
/**
* Sets the value of the map property.
*
* @param aMap the new value of the map property
*/
public void setMap(BufferedImage aMap) {
map = aMap;
}
/**
* Describe <code>getGameMap</code> method here.
*
* @return a <code>BufferedImage</code> value
*/
public BufferedImage getGameMap() {
return (this.gameMap);
}
/**
* Access method for the size property.
*
* @return the current value of the size property
*/
public Dimension getSize() {
return new Dimension(map.getWidth(), map.getHeight());
}
/**
* Returns the water animation.
*
* @return an <code>AnimatedSprite</code> value
*/
public AnimatedSprite getWaterAnimation() {
return waterAnimation;
}
/**
* Describe <code>setWaterAnimation</code> method here.
*
* @param set a <code>TileSet</code> value
*
*/
public void setWaterAnimation(TileSet set) {
waterAnimation = new AnimatedSprite(set,
Direction.NO_DIRECTION,
Looping.INFINITE);
}
/////////////
// Methods //
/////////////
/**
* This will create a Game Map for use in drawing to screen.
* Parses the Mask and Texture File to spit out a map.
*
* @return the new gameMap to draw to screen.
*/
public BufferedImage createGameMap() {
//ensure that the map and textures are loaded
if (map == null || texture == null) {
return null;
}
//Set up variables
int mapWidth = map.getWidth() ,
mapHeight = map.getHeight() ,
texWidth = texture.getWidth() ,
texHeight = texture.getHeight();
int x, y, i, j, i2, j2; //x,y -> map, i,j -> texture
//initialize the game Map
this.gameMap =
ImageLoader.getInstance()
.createBlankImage(map.getWidth(), map.getHeight());
this.indestructGameMap = ImageLoader.getInstance()
.createBlankImage(map.getWidth(), map.getHeight());
this.unbreakableMap = ImageLoader.getInstance()
.createBlankImage(map.getWidth(), map.getHeight());
// Graphics2D g = gameMap.createGraphics();
//Go through the map and stick everything where its supposed to be
i = 0;
j = 0;
i2 = 0;
j2 = 0;
for (x = 0; x < mapWidth; x++) {
for (y = 0; y < mapHeight; y++) {
if (map.getRGB(x, y)
== MapSettings.getInstance().getUnbreakableColor()) {
gameMap.setRGB(x, y, unbreakableTexture.getRGB(i2, j2));
indestructGameMap.setRGB(x, y, unbreakableTexture.getRGB(i2, j2));
unbreakableMap.setRGB(x, y, MapSettings.getUnbreakableColor());
} else if (map.getRGB(x, y) != -16777216) {
gameMap.setRGB(x, y, texture.getRGB(i, j));
}
if (++j >= texHeight) {
j = 0;
}
if (++j2 >= unbreakableTexture.getHeight()) {
j2 = 0;
}
}
if (++i >= texWidth) {
i = 0;
}
if (++i2 >= unbreakableTexture.getWidth()) {
i2 = 0;
}
}
return gameMap;
}
//@roseuid 40660B3403D2
/**
* When someone creates something on the map, the thing will be
* added to the map. Use this method.
*
* @param shape The shape to add to the map.
* @param color The color of the shape
*
*/
public void addShape(Shape shape, Color color) {
Graphics2D g = (Graphics2D) PhysicsEngine.getInstance()
.getCurrentMap().getGraphics();
Graphics2D g2 = (Graphics2D) this.gameMap.getGraphics();
g.setColor(color);
g.fill(shape);
g2.setColor(color);
g2.fill(shape);
}
//@roseuid 40660B8C017F
/**
* When something explodes, digs, or mines, some part of the map can
* be destroyed. This is where this function is called. The shape
* passed in is what will be removed from the map. Note that only
* "diggable" elements will be removed. The undiggable ones will
* still persist.
*
* @param shape The shape to remove from the map.
*
*/
public void subtractShape(Shape shape) {
Graphics2D g = (Graphics2D) PhysicsEngine.getInstance()
.getCurrentMap().getGraphics();
Graphics2D g2 = (Graphics2D) this.gameMap.getGraphics();
g.setColor(Color.black);
g.fill(shape);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
g2.fill(shape);
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
//javax.swing.JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
final javax.swing.JFrame frame
= new javax.swing
.JFrame("Map Test",
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration());
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setSize(800, 600);
frame.setVisible(true);
//Initialize double buffering:
//frame.createBufferStrategy(2);
//Create and set up the content pane.
final Level l = edu.gatech.cs2335.lemmings.file.LevelReader.getInstance()
.loadLevel("test_level");
javax.swing.JPanel newContentPane
= new javax.swing.JPanel(new java.awt.BorderLayout());
//newContentPane.add(tst, java.awt.BorderLayout.CENTER);
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Initialize double buffering:
frame.createBufferStrategy(2);
final java.awt.image.BufferStrategy strategy = frame.getBufferStrategy();
new Thread(new Runnable() {
public void run() {
long lastFrame = System.currentTimeMillis();
BufferedImage im = l.getMap().createGameMap();
System.out.println("\n\n\n\n\n\n\n");
System.out.println("Image: \n" + im);
System.out.println("\n\n\n\n\n\n\n");
System.out.flush();
while (true) {
int width = frame.getWidth();
int height = frame.getHeight();
//Get the graphics context from buffer strategy:
java.awt.Graphics2D g =
(java.awt.Graphics2D) strategy.getDrawGraphics();
//Do the drawing:
g.setColor(java.awt.Color.gray);
g.fillRect(0, 0, width, height);
width /= 2;
height /= 2;
g.drawImage(im, null, 0, 0);
//Flip buffer:
strategy.show();
//Cap the frame rate:
lastFrame += 20; //Frame rate
try {
Thread.sleep(Math.max(0,
lastFrame - System.currentTimeMillis()));
} catch (Exception e) {
continue;
}
}
}
}
).start();
}
/**
* Main
* @param args args
*/
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

View File

@@ -0,0 +1,75 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Color;
/**
* Map Settings, thanks to PMD, a Singleton class
*
* Holds Physics Constants
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Caban</A>
*/
public class MapSettings {
/**
* The unbreakable color
*/
private static final int UNBREAKABLECOLOR = -8421505;
/**
* The passable color
*/
private static final int PASSABLECOLOR = -16777216;
/**
* TRANSPARENT Color
*/
public static final Color TRANSPARENT = new Color(255, 255, 255, 100);
/**
* The Singleton Map Settings
*/
private static MapSettings instance = new MapSettings();
/**
* Creates a new <code>MapSettings</code> instance.
*
*/
private MapSettings() { }
/**
* Method getUnbreakableColor
*
*
* @return int
*
*/
public static int getUnbreakableColor() {
return (MapSettings.UNBREAKABLECOLOR);
}
/**
* Method getPassableColor
*
*
* @return int
*
*/
public static int getPassableColor() {
return (MapSettings.PASSABLECOLOR);
}
/**
* Method getInstance
*
*
* @return mapsettings
*
*/
public static MapSettings getInstance() {
return (MapSettings.instance);
}
}

View File

@@ -0,0 +1,227 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Font;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
//import java.util.Random;
import edu.gatech.cs2335.lemmings.file.LevelReader;
//import edu.gatech.cs2335.lemmings.gui.LIcon;
//import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LLabel;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
//import edu.gatech.cs2335.lemmings.graphics.Looping;
//import edu.gatech.cs2335.lemmings.graphics.Direction;
//import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
//import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* Class MultiLevelResultsState: this is the multilevel result state.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 16, 2004) - Created the MultiLevelResultsState class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 16, 2004
*/
public final class MultiLevelResultsState extends LevelResultsState {
/**
* Describe variable <code>numCarryOver</code> here.
*
*/
private int numCarryOver;
/**
* Describe variable <code>otherCarryOver</code> here.
*
*/
private int otherCarryOver;
/**
*
*/
private LLabel lblCarryOver;
/**
*
*/
private int n;
/**
* Describe variable <code>lblOtherPlayer</code> here.
*
*/
private LLabel lblOtherPlayer;
/**
*
*/
private AbstractGameState target;
/**
* Creates a new <code>MultiLevelResultsState</code> instance.
*@param l l
*@param num num
*@param otherNum on
*/
public MultiLevelResultsState(Level l, int num, int otherNum) {
super(l);
setNumCarryOver(num);
setOtherCarryOver(otherNum);
}
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*/
public boolean initialize() {
super.initialize();
lblCarryOver = new LLabel();
lblCarryOver
.setText("Your Carry Over Lemmings: " + numCarryOver);
lblCarryOver.setPosition(new Point(300, 280));
lblCarryOver.setShown(true);
lblOtherPlayer = new LLabel();
lblOtherPlayer
.setText("Your Opponent's Carry Over Lemmings: " + otherCarryOver);
lblOtherPlayer.setPosition(new Point(300, 300));
lblOtherPlayer.setShown(true);
LRootPane u = getUI();
u.addChild(lblCarryOver);
u.addChild(lblOtherPlayer);
if (numCarryOver < 1) {
LFancyLabel resultLabel = new LFancyLabel();
resultLabel.setForeground(Color.red);
resultLabel.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD,
20.0f));
resultLabel.setText("You Lose!");
resultLabel.setPosition(new Point(300, 400));
resultLabel.setShown(true);
u.addChild(resultLabel);
target = new MainMenuState();
} else if (otherCarryOver < 1) {
LFancyLabel resultLabel = new LFancyLabel();
resultLabel.setForeground(Color.red);
resultLabel.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD,
20.0f));
resultLabel.setText("You Win!");
resultLabel.setPosition(new Point(300, 400));
resultLabel.setShown(true);
target = new MainMenuState();
u.addChild(resultLabel);
}
//The player saved enough lemmings.
String name = getLevel().getData().getId();
String[] levels = LevelReader.getInstance().getLevelList();
boolean found = false;
n = 0;
for (int i = 0; i < levels.length; i++) {
if (name.equals(levels[i])) {
if (i == levels.length - 1) {
LFancyLabel resultLabel = new LFancyLabel();
resultLabel.setForeground(Color.red);
resultLabel.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD,
20.0f));
resultLabel.setPosition(new Point(300, 400));
resultLabel.setShown(true);
if (numCarryOver > otherCarryOver) {
resultLabel.setText("You Win!");
} else if (numCarryOver < otherCarryOver) {
resultLabel.setText("You Lose!");
} else {
resultLabel.setText("It's a Tie...");
}
target = new MainMenuState();
u.addChild(resultLabel);
} else {
n = i + 1;
found = true;
break;
}
}
}
return true;
}
/**
* Get the value of otherCarryOver.
* @return value of otherCarryOver.
*/
public int getOtherCarryOver() {
return otherCarryOver;
}
/**
* Set the value of otherCarryOver.
* @param v Value to assign to otherCarryOver.
*/
public void setOtherCarryOver(int v) {
this.otherCarryOver = v;
}
/**
* Get the value of numCarryOver.
* @return value of numCarryOver.
*/
public int getNumCarryOver() {
return numCarryOver;
}
/**
* Set the value of numCarryOver.
* @param v Value to assign to numCarryOver.
*/
public void setNumCarryOver(int v) {
this.numCarryOver = v;
}
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*/
public void actionPerformed(ActionEvent ae) {
String a = ae.getActionCommand();
if (a.equals("back")) {
//Go back to the main menu:
GameEngine.getInstance().setCurrentState(new MainMenuState());
} else if (a.equals("play")) {
if (target != null) {
GameEngine.getInstance().setCurrentState(target);
} else {
//Go to the next level:
GameEngine.getInstance().startMultiLevel(n, numCarryOver);
}
}
}
}

View File

@@ -0,0 +1,397 @@
package edu.gatech.cs2335.lemmings.engine;
import edu.gatech.cs2335.lemmings.networking.PauseMessage;
import edu.gatech.cs2335.lemmings.networking.UpdateMessage;
import edu.gatech.cs2335.lemmings.networking.ChangeSpeedMessage;
import edu.gatech.cs2335.lemmings.networking.NukeRequestMessage;
import edu.gatech.cs2335.lemmings.networking.ChangeLemmingFlowMessage;
import edu.gatech.cs2335.lemmings.networking.IdentificationMessage;
import edu.gatech.cs2335.lemmings.networking.LemmingNetworking;
import edu.gatech.cs2335.lemmings.networking.MessageFactory;
import edu.gatech.cs2335.lemmings.networking.AbstractMessage;
import edu.gatech.cs2335.lemmings.networking.StatsMessage;
import edu.gatech.cs2335.lemmings.networking.LevelFinishMessage;
import java.awt.Font;
import java.awt.Color;
import java.awt.Point;
//import java.awt.Rectangle;
//import java.awt.event.ActionEvent;
//import java.awt.Point;
//import java.awt.Color;
//import java.awt.Rectangle;
//import java.awt.Graphics;
//import java.awt.Dimension;
//import java.awt.event.ActionEvent;
//import java.awt.event.ActionListener;
import java.util.List;
import java.util.Vector;
//import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobSettings;
//import edu.gatech.cs2335.lemmings.graphics.Looping;
//import edu.gatech.cs2335.lemmings.graphics.Direction;
//import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
//import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
//import edu.gatech.cs2335.lemmings.gui.ITypable;
//import edu.gatech.cs2335.lemmings.gui.LLeafComponent;
//import edu.gatech.cs2335.lemmings.gui.LComponent;
//import edu.gatech.cs2335.lemmings.gui.JobContainer;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
//import edu.gatech.cs2335.lemmings.gui.LButton;
//import edu.gatech.cs2335.lemmings.gui.LToggleButton;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
//import edu.gatech.cs2335.lemmings.gui.GamePlayPanel;
//import edu.gatech.cs2335.lemmings.graphics.Renderer;
//import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.file.LevelReader;
//import edu.gatech.cs2335.lemmings.networking.LemmingNetworking;
import edu.gatech.cs2335.lemmings.networking.MessageSettings;
//import edu.gatech.cs2335.lemmings.networking.MessageFactory;
//import edu.gatech.cs2335.lemmings.networking.AbstractMessage;
//import java.util.Vector;
//import java.util.List;
/**
* The mutiplayer game state, where the actual game is displayed and played.
*
* Koala Update - fixed
*
* @author <A href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version 1.0
*/
public class MultiplayerGameplayState extends GamePlayState {
/**
* bDebug is for turning debugging output on and off
*/
//private final boolean bDebug = false;
/**
* Boolean for VERBOSE output
*/
private static final boolean VERBOSE = true;
/**
* True if other player has requested a NUKE
*/
private boolean otherPlayerRequestedNuke = false;
/**
* True if this player has requested a NUKE
*/
private boolean playerRequestedNuke = false;
/**
* Holds the Level information for the other player
*/
private Level otherPlayerLevel;
/**
* The label showing the number of lemmings released.
*/
private LFancyLabel releasedLabel;
/**
* The label that will show how many lemmings have been saved.
*/
private LFancyLabel savedLabel;
/**
* This label will show the number of lemmings out in the field.
*/
private LFancyLabel outLabel;
/**
*dead
*/
private int deadLemmings;
/**
*saved
*/
private int savedLemmings;
/**
*released
*/
private int releasedLemmings;
/**
*total
*/
private int totalLemmings;
/**
* Has one player done the thing?
*/
private boolean flag;
/**
* constructor
*@param l level
*
*/
public MultiplayerGameplayState (Level l) {
super(l);
this.otherPlayerLevel = l.duplicate();
}
/**
* Describe <code>specialInitialize</code> method here.
*
*/
protected synchronized void specialInitialize() {
releasedLabel = new LFancyLabel();
releasedLabel.setForeground(Color.green);
releasedLabel
.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 20.0f));
releasedLabel.setText("");
releasedLabel.setPosition(new Point(600, 50));
releasedLabel.setShown(true);
savedLabel = new LFancyLabel();
savedLabel.setForeground(Color.green);
savedLabel.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 20.0f));
savedLabel.setText("");
savedLabel.setPosition(new Point(600, 70));
savedLabel.setShown(true);
outLabel = new LFancyLabel();
outLabel.setForeground(Color.green);
outLabel.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 20.0f));
outLabel.setText("");
outLabel.setPosition(new Point(600, 90));
outLabel.setShown(true);
LRootPane u = getUI();
u.addChild(releasedLabel);
u.addChild(savedLabel);
u.addChild(outLabel);
}
/**
* Whatever else needs to be done for pause.
*/
protected void specialPauseHandle() {
List data = new Vector();
data.add("MrPause");
data.add(Boolean.valueOf(isPauseSelected()));
AbstractMessage am = MessageFactory.getInstance().
createMessage(MessageSettings.TYPE_PAUSE, data);
LemmingNetworking.getInstance().sendMessage(am);
}
/**
* Whatever else needs to be done for speedup.
*/
protected void specialSpeedupHandle() {
List data = new Vector();
data.add("MrSpeed");
data.add(new Integer(getGameSpeed()));
AbstractMessage am = MessageFactory.getInstance().
createMessage(MessageSettings.TYPE_SPEED, data);
LemmingNetworking.getInstance().sendMessage(am);
}
/**
* Whatever else needs to be done for nuke.
*/
protected void specialNukeHandle() {
requestNuke();
}
/**
* Whatever else needs to be done for increase.
*/
protected void specialIncreaseHandle() {
List data = new Vector();
data.add("MrRate");
data.add(new Integer(getLemmingFlow()));
AbstractMessage am = MessageFactory.getInstance().
createMessage(MessageSettings.TYPE_FLOW, data);
LemmingNetworking.getInstance().sendMessage(am);
}
/**
* Whatever else needs to be done for decrease.
*/
protected void specialDecreaseHandle() {
List data = new Vector();
data.add("MrRate");
data.add(new Integer(getLemmingFlow()));
AbstractMessage am = MessageFactory.getInstance().
createMessage(MessageSettings.TYPE_FLOW, data);
LemmingNetworking.getInstance().sendMessage(am);
}
/**
* Describe <code>specialUpdate</code> method here.
*
*/
protected void specialUpdate() {
releasedLabel.setText("Released: "
+ Integer.toString(releasedLemmings)
+ "/" + Integer.toString(totalLemmings));
savedLabel.setText("Saved: "
+ Integer.toString(savedLemmings));
outLabel.setText("Out: "
+ Integer.toString(deadLemmings));
}
/**
* Updates both players levels based on msg data
* @param msg , network message holding game state updates
*/
public void receiveMessage(AbstractMessage msg) {
if (msg instanceof StatsMessage) {
deadLemmings = ((StatsMessage) msg).getLemmingsDead();
savedLemmings = ((StatsMessage) msg).getLemmingsSaved();
releasedLemmings = ((StatsMessage) msg).getLemmingsReleased();
totalLemmings = ((StatsMessage) msg).getLemmingsTotal();
} else if (msg instanceof NukeRequestMessage) {
//Seeing if we have to nuke and nuking if so
otherPlayerRequestedNuke = true;
if (playerRequestedNuke) {
//Send Nuke Message
List data = new Vector();
data.add(new String("NukeMessage"));
//Nuke them all
getLevel().nukeEmALL();
}
} else if (msg instanceof LevelFinishMessage) {
otherPlayerEnded();
} else if (msg instanceof UpdateMessage) {
getOtherPlayerLevel().updateLevel(msg);
} else if (msg instanceof PauseMessage) {
getPauseButton().setSelected(((PauseMessage) msg).getPause());
singlePauseButtonHandle();
if (((PauseMessage) msg).getPause()) {
getOtherPlayerLevel().pause();
} else {
getOtherPlayerLevel().unpause();
}
} else if (msg instanceof ChangeLemmingFlowMessage) {
this.getOtherPlayerLevel().setLemmingFlow(
((ChangeLemmingFlowMessage) msg).getLemmingFlow());
} else if (msg instanceof ChangeSpeedMessage) {
//Do we really want the other player controlling speed?
// this.setGameSpeed(((ChangeSpeedMessage) msg).getSpeed());
int stuff = 0;
stuff++;
} else if (msg instanceof IdentificationMessage) {
if (VERBOSE) {
System.out.println("IdentificationMessage received during play");
}
} else {
//This would be bad
System.err.println("MultiplayerGameplayState:receiveMessage: "
+ "Msg was not correct type");
}
}
/**
* Ends the level and goes on to the next state.
*/
public synchronized void endLevel() {
//Go to the level results state:
getLevel().pause();
//Send out the endmessage
AbstractMessage am = MessageFactory.getInstance()
.makeBlankMessage(MessageSettings.TYPE_LEVELEND, "nUb");
LemmingNetworking.getInstance().sendMessage(am);
if (flag) {
int lemmingsSaved = getLevel().getLemmingsSaved();
GameEngine.getInstance()
.setCurrentState(new
MultiLevelResultsState(getLevel(),
lemmingsSaved, //My lemmings
savedLemmings)); //Opponent's
//lemmings
} else {
flag = true;
}
}
/**
* Konchil on.
*/
public synchronized void otherPlayerEnded() {
if (flag) {
int lemmingsSaved = getLevel().getLemmingsSaved();
GameEngine.getInstance()
.setCurrentState(new
MultiLevelResultsState(getLevel(),
lemmingsSaved, //My lemmings
savedLemmings)); //Opponent's
//lemmings
} else {
flag = true;
}
}
/**
* Accessor for otherPlayerlevel
* @return Level , Level of remote player
*/
public Level getOtherPlayerLevel() {
return otherPlayerLevel;
}
/**
* Determines if the little guys should be nuked and nukes.
*/
public void requestNuke() {
playerRequestedNuke = true;
List data = new Vector();
data.add(new String("NukeMessage"));
AbstractMessage nukem =
MessageFactory.getInstance().createMessage("NUKE", data);
LemmingNetworking.getInstance().sendMessage(nukem);
if (otherPlayerRequestedNuke) {
//Nuke them all
getLevel().nukeEmALL();
getOtherPlayerLevel().nukeEmALL();
}
}
}

View File

@@ -0,0 +1,204 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Point;
import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.gui.LIcon;
import edu.gatech.cs2335.lemmings.gui.LLabel;
import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.gui.LTextField;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
//import edu.gatech.cs2335.lemmings.networking.LemmingNetworking;
//import edu.gatech.cs2335.lemmings.file.LevelReader;
/**
* Class MultiplayerSetupState: This is where the player can either
* start up the server, or join an existing game.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 14, 2004) - Created the MultiplayerSetupState class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 14, 2004
*/
public class MultiplayerSetupState extends AbstractGameState {
/**
* The text field that will contain the IP of the host to connect to.
*/
private LTextField ipField;
/**
* Port.
*/
private LTextField portField;
/**
* Creates a new <code>MultiplayerSetupState</code> instance.
*/
public MultiplayerSetupState() {
}
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*
*/
public boolean initialize() {
//Initialize background:
LIcon bg = new LIcon(TileSetManager.getInstance().getTileSet("bg_mp"));
bg.setShown(true);
bg.setPosition(new Point(0, 0));
//Initialize labels:
LLabel lblHost = new LLabel();
lblHost.setText("Host: ");
lblHost.setShown(true);
lblHost.setPosition(new Point(300, 150));
LLabel lblPort = new LLabel();
lblPort.setText("Port: ");
lblPort.setShown(true);
lblPort.setPosition(new Point(300, 200));
ipField = new LTextField("localhost");
ipField.setShown(true);
ipField.setPosition(new Point(350, 150));
ipField.end();
portField = new LTextField("3333");
portField.setShown(true);
portField.setPosition(new Point(350, 200));
portField.end();
//Initialize Buttons:
LButton back = new LButton();
back
.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mpb_back"),
Direction.NO_DIRECTION,
Looping.NONE));
back.setPosition(new Point(10, 590));
back.setShown(true);
back.setActionCommand("back");
back.addActionListener(this);
LButton host = new LButton();
host
.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mpb_host"),
Direction.NO_DIRECTION,
Looping.NONE));
host.setPosition(new Point(345, 590));
host.setShown(true);
host.setActionCommand("host");
host.addActionListener(this);
LButton connect = new LButton();
connect
.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mpb_connect"),
Direction.NO_DIRECTION,
Looping.NONE));
connect.setPosition(new Point(680, 590));
connect.setShown(true);
connect.setActionCommand("connect");
connect.addActionListener(this);
//Initialize UI:
LRootPane np = new LRootPane();
np.addChild(bg);
np.addChild(lblHost);
np.addChild(lblPort);
np.addChild(back);
np.addChild(host);
np.addChild(connect);
np.addChild(portField);
np.addChild(ipField);
setUI(np);
return true;
}
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
return true;
}
/**
* Describe <code>getHost</code> method here.
*
* @return a <code>String</code> value
*/
private String getHost() {
return ipField.getText();
}
/**
* Describe <code>getPort</code> method here.
*
* @return an <code>int</code> value
*/
private int getPort() {
try {
return Integer.parseInt(portField.getText());
} catch (NumberFormatException nfe) {
return 3333;
}
}
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*
*/
public void actionPerformed(ActionEvent ae) {
String a = ae.getActionCommand();
if ("back".equals(a)) {
GameEngine.getInstance().setCurrentState(new MainMenuState());
} else if ("host".equals(a)) {
//Host an mp game:
int port = getPort();
GameEngine.getInstance().startMultiGame();
GameEngine.getInstance().getEcs().host(port);
} else if ("connect".equals(a)) {
//Connect to an existing game:
String host = getHost();
int port = getPort();
GameEngine.getInstance().startMultiGame();
GameEngine.getInstance().getEcs().connectTo(host, port);
}
}
}

View File

@@ -0,0 +1,175 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.Point;
import java.awt.Color;
//import java.awt.Rectangle;
//import java.awt.Graphics;
//import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobSettings;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
//import edu.gatech.cs2335.lemmings.gui.ITypable;
//import edu.gatech.cs2335.lemmings.gui.LLeafComponent;
//import edu.gatech.cs2335.lemmings.gui.LComponent;
//import edu.gatech.cs2335.lemmings.gui.JobContainer;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.gui.LButton;
//import edu.gatech.cs2335.lemmings.gui.LToggleButton;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
//import edu.gatech.cs2335.lemmings.gui.GamePlayPanel;
//import edu.gatech.cs2335.lemmings.graphics.Renderer;
//import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.file.LevelReader;
//import java.awt.Point;
//import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.gui.LIcon;
//import edu.gatech.cs2335.lemmings.gui.LButton;
//import edu.gatech.cs2335.lemmings.gui.LRootPane;
//import edu.gatech.cs2335.lemmings.graphics.Looping;
//import edu.gatech.cs2335.lemmings.graphics.Direction;
//import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
//import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* Class NetworkErrorState: Displays a network error...
*
* <PRE>
* Revision History:
* v1.0 (Apr. 21, 2004) - Created the NetworkErrorState class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 21, 2004
*/
public class NetworkErrorState extends AbstractGameState {
/**
* Display debug information?
*/
public static final boolean VERBOSE = false;
/**
* The label to display our message to the user in.
*/
private LFancyLabel message;
/**
* Creates a new <code>NetworkErrorState</code> instance.
*/
public NetworkErrorState() {
//Initialize background:
LIcon bg = new LIcon(TileSetManager.getInstance()
.getTileSet("bg_ne"));
bg.setShown(true);
bg.setPosition(new Point(0, 0));
//Initialize label:
message = new LFancyLabel();
message.setForeground(Color.black);
message.setFont(LFancyLabel.DEFAULT_FONT.deriveFont(Font.BOLD, 18.0f));
message.setText("");
message.setShown(true);
message.setPosition(new Point(300, 300));
//Initialize button:
LButton goOn = new LButton();
goOn.setPosition(new Point(10, 590));
goOn.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("mpb_back"),
Direction.NO_DIRECTION, Looping.NONE));
goOn.addActionListener(this);
goOn.setActionCommand("goOn");
goOn.setShown(true);
//Initialize UI:
LRootPane np = new LRootPane();
np.addChild(bg);
np.addChild(message);
np.addChild(goOn);
setUI(np);
}
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*/
public boolean initialize() {
if (VERBOSE) {
System.err.println("NetworkErrorState: I am initialized!");
System.err.flush();
}
return true;
}
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
setErrorMessage("");
if (VERBOSE) {
System.err.println("NetworkErrorState: I am cleaned up!");
System.err.flush();
}
return true;
}
/**
* Sets up the error message.
*
* @param msg a <code>String</code> value
*/
public void setErrorMessage(String msg) {
message.setText(new String(msg));
}
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*/
public void actionPerformed(ActionEvent ae) {
if (VERBOSE) {
System.err.println("NetworkErrorState: Got some action - "
+ ae.getActionCommand());
System.err.flush();
}
//Go back to the Multiplayer setup state:
GameEngine.getInstance().setCurrentState(new MultiplayerSetupState());
}
}

View File

@@ -0,0 +1,40 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* Class Particle: When a lemming blows up, he makes particles. Well,
* here they are. :)
*
* Description from Jose:
* The particle class, used for ! particle effects !
*
* Nothing special to do here, the Physics Engine will handle the
* actual effects. The single function is used to test whether or
* not the particle should be killed.
*
* <PRE>
* Revision History:
* v2.0 (2004 - 4 - 20) - Made abstract
* v1.0 (Apr. 11, 2004) - Created the Particle class
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Caban</A>
* @version Version 1.0, Apr. 11, 2004
*/
public abstract class Particle extends PrettySprite {
/**
* Creates a new <code>Particle</code> instance.
*@param s s
*/
public Particle(String s) {
super(s);
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected abstract boolean canKillSelf();
}

View File

@@ -0,0 +1,144 @@
package edu.gatech.cs2335.lemmings.engine;
//import java.awt.Dimension;
import java.awt.Rectangle;
import edu.gatech.cs2335.lemmings.graphics.TileSet;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
import edu.gatech.cs2335.lemmings.physics.PhysicsObject;
/**
* Class Portal: Represents a portal in the map. It can be either an
* entrance, an exit, or a portal from one part of the map to another.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 28, 2004) - Created the Portal class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 28, 2004
*/
public abstract class Portal implements ICleanable {
/**
* The physics object associated with the portal.
*/
private PhysicsObject physics;
/**
* The animation associated with the portal.
*/
private AnimatedSprite animation;
/**
* The level that the portal is associated with.
*/
private Level parent;
/**
* The extent of the portal. If the lemming is within the
* rectangle, then it will be processed.
*/
private Rectangle extent;
/**
* Creates a new <code>Portal</code> instance.
*
* @param anim The name of the animation tileset to use.
*/
public Portal(String anim) {
//Set up physics:
physics = new PhysicsObject();
animation = new AnimatedSprite(TileSetManager.getInstance()
.getTileSet(anim),
Direction.NO_DIRECTION,
Looping.INFINITE);
setAnimation(anim); //Hack... Setting animation twice?? :/
parent = null;
}
/**
* Cleans up all references for the garbage collector.
*/
public void cleanUp() {
parent = null;
}
/**
* Returns the animation for this portal.
*
* @return an <code>AnimatedSprite</code> value
*/
public AnimatedSprite getAnimation() {
return animation;
}
/**
* Sets the animation with the specified name.
*
* @param name a <code>String</code> value
*/
protected final synchronized void setAnimation(String name) {
TileSet set = TileSetManager.getInstance().getTileSet(name);
Rectangle rect = set.getExtent(0);
setExtent(new Rectangle(rect));
animation.setAnimation(set);
}
/**
* Returns the physics object associated with this portal.
*
* @return a <code>PhysicsObject</code> value
*/
public PhysicsObject getPhysics() {
return physics;
}
/**
* Returns the physics object associated with this portal.
*
* @return a <code>PhysicsObject</code> value
*/
public Rectangle getExtent() {
return extent;
}
/**
* Returns the physics object associated with this portal.
*
* @param ext a <code>PhysicsObject</code> value
*/
public final synchronized void setExtent(Rectangle ext) {
extent = ext;
}
/**
* Returns the physics object associated with this portal.
*
* @return a <code>PhysicsObject</code> value
*/
public Level getParent() {
return parent;
}
/**
* Returns the physics object associated with this portal.
*
* @param l a <code>PhysicsObject</code> value
*/
public void setParent(Level l) {
parent = l;
}
/**
* This function will be called when a lemming enters this portal.
*
* @param l a <code>Lemming</code> value
*/
public abstract void processLemmingEntry(Lemming l);
}

View File

@@ -0,0 +1,102 @@
package edu.gatech.cs2335.lemmings.engine;
import java.util.StringTokenizer;
/**
* Class PortalFactory: Creates different portals.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 22, 2004) - Created the PortalFactory class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 22, 2004
*/
public class PortalFactory {
/**
* All the different kinds of portals.
*/
public static final String[] PORTAL_TYPES = {
"entrance",
"exit",
"fling_trap"
};
/**
* Describe constant <code>INSTANCE</code> here.
*
*/
private static final PortalFactory INSTANCE = new PortalFactory();
/**
* Creates a new <code>PortalFactory</code> INSTANCE.
*/
private PortalFactory() {
}
/**
* Describe <code>getInstance</code> method here.
*
* @return a <code>PortalFactory</code> value
*/
public static PortalFactory getInstance() {
return INSTANCE;
}
/**
* Describe <code>makePortal</code> method here.
*@param type type
*@param value value
* @return a <code>Portal</code> value
*/
public Portal makePortal(String type, String value) {
//Try to parse the coordinates out of the value first:
StringTokenizer st = new StringTokenizer(value);
int x;
int y;
if (!st.hasMoreTokens()) {
return null;
}
try {
x = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
if (!st.hasMoreTokens()) {
return null;
}
try {
y = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
//Got coordinates of the portal:
Portal result = null;
if (PORTAL_TYPES[0].equals(type)) {
result = new LevelEntrance();
} else if (PORTAL_TYPES[1].equals(type)) {
result = new LevelExit();
} else if (PORTAL_TYPES[2].equals(type)) {
result = new FlingTrap();
}
if (result == null) {
return null;
}
result.getPhysics().getPosition().setX(x);
result.getPhysics().getPosition().setY(y);
return result;
}
}

View File

@@ -0,0 +1,125 @@
package edu.gatech.cs2335.lemmings.engine;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.physics.PhysicsObject;
import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
/**
* Class PrettySprite: This is basically an eye-candy sprite. These
* will be things that happen to lemmings, for instance, or maybe some
* other effects, like a random bird flying by.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the PrettySprite class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 11, 2004
*/
public abstract class PrettySprite extends PhysicsObject
implements ICleanable {
/**
* The animation associated with this sprite.
*/
private AnimatedSprite animation;
/**
* The level that the sprite is associated with.
*/
private Level level;
/**
* Creates a new <code>PrettySprite</code> instance.
*@param animType anime
*/
public PrettySprite(String animType) {
level = null;
animation = new AnimatedSprite(TileSetManager.getInstance()
.getTileSet(animType),
Direction.NO_DIRECTION,
Looping.INFINITE);
}
/**
* Access method for the animation property.
*
* @return the current value of the animation property
*/
public AnimatedSprite getAnimation() {
return animation;
}
/**
* Describe <code>setAnimation</code> method here.
*
* @param type a <code>String</code> value
*/
public final void setAnimation(String type) {
animation.setAnimation(TileSetManager.getInstance().getTileSet(type));
}
/**
* Should be called at every frame to update the sprite.
*/
public final void updateSprite() {
//Process my own special:
//specialUpdate();
//Update position:
try {
PhysicsEngine.getInstance().calculateNextPoint(this);
} catch (Exception e) {
return;
}
//Update animation:
animation.nextFrameNumber();
//Check if we are ready to die:
if (canKillSelf()) {
//Commit suicide:
level.killPrettySprite(this);
}
}
/**
* Describe <code>getLevel</code> method here.
*
* @return a <code>Level</code> value
*/
public final Level getLevel() {
return level;
}
/**
* Describe <code>setLevel</code> method here.
*
* @param l a <code>Level</code> value
*/
public final void setLevel(Level l) {
level = l;
}
/**
* Cleans up all references for the garbage collector.
*/
public void cleanUp() {
level = null;
animation = null;
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected abstract boolean canKillSelf();
}

View File

@@ -0,0 +1,49 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* This class will essentially run the Lemmings game. It is the driver.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version 1.0
*/
public class ProgramRunner {
//@roseuid 406519600375
/**
* Creates a new instance of the runner.
*
*/
private ProgramRunner() { }
//@roseuid 406515D202EA
/**
* Runs the game.
*
*/
public void execute() {
if (GameEngine.VERBOSE) {
System.out.println("The program is running...");
System.out.flush();
}
//Create Splash Screen State:
AbstractGameState gs = new SplashScreenState();
//gs.initialize();
//Start up the game:
GameEngine.getInstance().setCurrentState(gs);
}
/**
* Main method.
*
* @param args a <code>String[]</code> value
*/
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ProgramRunner().execute();
}
});
}
}

View File

@@ -0,0 +1,38 @@
package edu.gatech.cs2335.lemmings.engine;
/**
* Class SavedLemming: This thing will basically show the animation of
* the lemming going upstairs into the exit.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the SavedLemming class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 11, 2004
*/
public class SavedLemming extends PrettySprite {
/**
* Creates a new <code>SavedLemming</code> instance.
*/
public SavedLemming() {
super("saved_lemming");
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (getAnimation().getCurrentFrame() == 0) {
//The animation is through. We are done:
return true;
}
return false;
}
}

View File

@@ -0,0 +1,91 @@
package edu.gatech.cs2335.lemmings.engine;
import java.awt.Point;
import java.awt.event.ActionEvent;
import edu.gatech.cs2335.lemmings.gui.LButton;
import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* This state basically represents the splash screen. It's just a
* fancy graphics, which when the player clicks, disappears.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
*/
public class SplashScreenState extends AbstractGameState {
/**
* Creates a new <code>SplashScreenState</code> instance.
*
*/
public SplashScreenState() {
//Make sure that the resources we want are loaded:
TileSetManager.getInstance().getTileSet("splash");
}
//@roseuid 4065D29C032F
/**
* This method will initialize the state that it belongs to. It will
* then return true upon success, and false upon failure.
*
* @return boolean
*
*/
public boolean initialize() {
LRootPane np = new LRootPane();
LButton button = new LButton();
button.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("splash"),
Direction.NO_DIRECTION, Looping.NONE));
button.addActionListener(this);
button.setShown(true);
button.setPosition(new Point(0, 0));
np.addChild(button);
setUI(np);
return true;
}
//@roseuid 4065D29C034D
/**
* This method will perform all of the necessary clean up operations
* once the object is no longer needed.
*
* @return boolean
*
*/
public boolean cleanUp() {
return true;
}
//@roseuid 4065D29C037F
/**
* This function essentially processes the action event that may
* have been thrown by the UI.
*
* @param ae ae
*
*
*/
public void actionPerformed(ActionEvent ae) {
//The splash screen has been clicked.
if (GameEngine.VERBOSE) {
System.out.println("SplashScreenState: Transferrring to Main Menu...");
System.out.flush();
}
//Transfer to Main Menu.
MainMenuState mms = new MainMenuState();
// mms.initialize();
GameEngine.getInstance().setCurrentState(mms);
}
}

View File

@@ -0,0 +1,105 @@
package edu.gatech.cs2335.lemmings.engine;
import java.util.Random;
/**
* Class Particle: When a lemming blows up, he makes particles. Well,
* here they are. :)
*
* Description from Jose:
* Adds specific blood stuff
*
* Blood shouldn't bounce, rather it should hit the wall and die
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the BloodParticle class
* </PRE>
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Caban</A>
* @version Version 1.0, Apr. 11, 2004
*/
public class WaterParticle extends Particle {
/**
* Used to randomize velocity.
*/
private static final Random GENERATOR = new Random(new java.util.Date()
.getTime());
/**
* Particles to create when death should occur
*/
public static final int PARTICLESWHENDIE = 50;
/**
* time to Live
*/
private int killMe;
/**
* curve
*/
private int curve;
/**
* Stores canDie
*/
private boolean canDie;
/**
* Creates a new <code>Particle</code> instance.
*/
public WaterParticle() {
super("water");
getVelocity().setPolar(GENERATOR.nextInt(8), GENERATOR.nextInt(30));
canDie = false;
killMe = 0;
curve = 0;
}
/**
* Should return canDie
*
* @return <code>boolean</code> value
*/
public boolean getCanDie() {
return canDie;
}
/**
* Should return canDie
*
* @param a for candie <code>boolean</code> value
*/
public void setCanDie(boolean a) {
canDie = a;
}
/**
* Should return true when the sprite is ready to kill self.
*
* @return a <code>boolean</code> value
*/
protected boolean canKillSelf() {
if (killMe > 25) {
return true;
}
if (curve > 1) {
return true;
} else if (this.getVelocity().getJ() == 0) {
curve++;
}
killMe++;
return canDie;
}
}

View File

@@ -0,0 +1,149 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
import edu.gatech.cs2335.lemmings.physics.PhysicsVector;
import edu.gatech.cs2335.lemmings.physics.Point;
import edu.gatech.cs2335.lemmings.engine.Map;
import edu.gatech.cs2335.lemmings.engine.MapSettings;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
import edu.gatech.cs2335.lemmings.engine.DirtParticle;
import edu.gatech.cs2335.lemmings.engine.PrettySprite;
//import edu.gatech.cs2335.lemmings.engine.Level;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* v1.5 - Jose Caban - added process() handling
* TOnotDO: correct problem with non-breakable areas
* getting nicked slightly
*
*
* @author <a href="mailto:gtg184g@mail.gatech.edu">Jose Caban</a>
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.5
*/
public class BasherJob extends LemmingJob {
/**
* The Rectangle used to break the gameMap
*/
private Rectangle rectangle;
/**
* Creates a new <code>BasherJob</code> instance.
*
*/
public BasherJob() {
super(LemmingJobSettings.JOB_ID_BASHER);
rectangle = new Rectangle(5, 40);
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*
*/
public void process() {
BufferedImage currentMap = PhysicsEngine.getInstance().getCurrentMap();
Map map = ((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel().getMap();
Lemming lem = this.getOwner();
PhysicsVector currentVelocity = lem.getVelocity();
Point currentPoint = lem.getPosition();
//going to check whether or not this lemming can still be a basher
int rayI = currentPoint.getX();
// int max = LemmingJobSettings.BASHDISTANCE + rayI;
if (LemmingJobSettings.BDEBUG) {
System.out.println("Calculating Basher Stuff...");
System.out.println("rayI1..." + rayI);
System.out.println("currentVelocity..." + currentVelocity.getI());
}
if (currentVelocity.getI() > 0) {
currentVelocity.setI(1);
for (int i = 0; i < LemmingJobSettings.BASHDISTANCE; rayI++, i++) {
if (currentMap.getRGB(rayI, currentPoint.getY())
== MapSettings.getInstance().getUnbreakableColor()) {
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
currentVelocity.setI(PhysicsSettings.getInitialX());
return;
} else if (currentMap.getRGB(rayI, currentPoint.getY())
!= MapSettings.getInstance().getPassableColor()) {
if (LemmingJobSettings.BDEBUG) {
System.out.println("rayI..." + rayI);
}
rectangle.x = currentPoint.getX();
rectangle.y = currentPoint.getY() - 39;
map.subtractShape(rectangle);
//Make the debris
PrettySprite ps;
for (int j = 0; j < DirtParticle.PARTICLESWHENDIG; j++) {
ps =
new DirtParticle(currentVelocity.getI(),
currentVelocity.getJ());
ps.getPosition().setPoint(getOwner().getPosition().getX(),
getOwner().getPosition().getY() - 20,
getOwner().getPosition().getZ());
((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel().addPrettySprite(ps);
}
return;
}
}
} else {
currentVelocity.setI(-1);
for (int i = LemmingJobSettings.BASHDISTANCE; i > 0; rayI--, i--) {
if (currentMap.getRGB(rayI, currentPoint.getY())
== MapSettings.getInstance().getUnbreakableColor()) {
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
currentVelocity.setI(-PhysicsSettings.getInitialX());
return;
} else if (currentMap.getRGB(rayI, currentPoint.getY())
!= MapSettings.getInstance().getPassableColor()) {
if (LemmingJobSettings.BDEBUG) {
System.out.println("rayI2..." + rayI);
}
rectangle.x = currentPoint.getX() - rectangle.width;
rectangle.y = currentPoint.getY() - 39;
map.subtractShape(rectangle);
//Make the debris
PrettySprite ps;
for (int j = 0; j < DirtParticle.PARTICLESWHENDIG; j++) {
ps = new DirtParticle(currentVelocity.getI(), currentVelocity.getJ());
ps.getPosition().setPoint(getOwner().getPosition().getX(),
getOwner().getPosition().getY(),
getOwner().getPosition().getZ());
((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel().addPrettySprite(ps);
}
return;
}
}
}
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
}
}

View File

@@ -0,0 +1,83 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
//import java.awt.geom.Ellipse2D;
//import java.awt.geom.Ellipse2D.Float;
//import java.awt.image.BufferedImage;
import java.util.List;
//import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
//import edu.gatech.cs2335.lemmings.physics.PhysicsVector;
import edu.gatech.cs2335.lemmings.physics.Point;
//import edu.gatech.cs2335.lemmings.engine.Map;
//import edu.gatech.cs2335.lemmings.engine.MapSettings;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
import edu.gatech.cs2335.lemmings.engine.Level;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* v1.5 Jose - Completed Blocker
*
* @author <a href="mailto:gtg184g@mail.gatech.edu">Jose Caban</a>
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.5
*/
public class BlockerJob extends LemmingJob {
/**
* Creates a new <code>BlockerJob</code> instance.
*
*/
public BlockerJob() {
super(LemmingJobSettings.JOB_ID_BLOCKER);
}
/**
* This function will be called whenever the owner is set and it is
* not NULL.
*/
protected void onSetOwner() {
getOwner().getVelocity().setI(0);
getOwner().updateDirection();
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
Level level
= ((GamePlayState) GameEngine.getInstance().getCurrentState()).getLevel();
Lemming lem = this.getOwner();
Point blockerPoint = lem.getPosition();
List list = level.getActiveLemmings();
Lemming currLem;
Point currentPoint;
for (int i = 0; i < list.size(); i++) {
currLem = (Lemming) list.get(i);
currentPoint = currLem.getPosition();
if (Math.abs(blockerPoint.getX() - currentPoint.getX())
< LemmingJobSettings.BLOCKERTOLERANCE
&& Math.abs(blockerPoint.getY() - currentPoint.getY())
< LemmingJobSettings.BLOCKERTOLERANCE) {
if (Math.abs(currentPoint.getX() + currLem.getVelocity().getI()
- blockerPoint.getX())
> Math.abs(currentPoint.getX() - blockerPoint.getX())) {
return;
}
currLem.getVelocity().setI(-1 * currLem.getVelocity().getI());
currLem.updateDirection();
}
}
}
}

View File

@@ -0,0 +1,133 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
import java.awt.Rectangle;
//import java.awt.image.BufferedImage;
import java.awt.Color;
//import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
import edu.gatech.cs2335.lemmings.physics.PhysicsVector;
import edu.gatech.cs2335.lemmings.physics.Point;
import edu.gatech.cs2335.lemmings.engine.Map;
//import edu.gatech.cs2335.lemmings.engine.MapSettings;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
//import edu.gatech.cs2335.lemmings.engine.Level;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* v1.4 - Turn around stop bridging
*
* v1.2 - Basic Bridging
*
* @author <a href="mailto:gtg184g@mail.gatech.edu">Jose Caban</a>
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.4
*/
public class BridgerJob extends LemmingJob {
/**
* Stores the current number of bricks laid down
*/
private int bricks;
/**
* Stores the current wait time
*/
private int waitTime;
/**
* The rectangle brick
*/
private Rectangle rectangle;
/**
* The direction
*/
private int direction;
/**
* Creates a new <code>BridgerJob</code> instance.
*
*/
public BridgerJob() {
super(LemmingJobSettings.JOB_ID_BRIDGER);
rectangle = new Rectangle(10, 2);
bricks = 0;
direction = 0;
waitTime = 0;
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
// BufferedImage currentMap = PhysicsEngine.
//getInstance().getCurrentMap();
Map map = ((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel().getMap();
Lemming lem = this.getOwner();
PhysicsVector currentVelocity = lem.getVelocity();
Point currentPoint = lem.getPosition();
if (direction == 0) {
direction = (currentVelocity.getI() > 0) ? 1 : -1;
} else if (currentVelocity.getI() > 0 && direction != 1) {
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
return;
} else if (currentVelocity.getI() < 0 && direction != -1) {
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
return;
}
if (bricks == 72) {
currentVelocity.setI(0);
if (waitTime >= LemmingJobSettings.BRIDGERDONETIME) {
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
}
if (LemmingJobSettings.BDEBUG) {
System.out.println("waitTime: " + waitTime);
}
waitTime++;
return;
}
if (currentVelocity.getI() > 0) {
if (bricks % 3 == 0) {
rectangle.x = currentPoint.getX() + rectangle.width;
rectangle.y = currentPoint.getY();
map.addShape(rectangle, Color.PINK);
}
bricks++;
} else {
if (bricks % 3 == 0) {
rectangle.x = currentPoint.getX()
- (rectangle.width * 2);
rectangle.y = currentPoint.getY();
map.addShape(rectangle, Color.PINK);
}
bricks++;
}
}
/**
*accessort
*@return int
*/
public int getDirection() {
return (this.direction);
}
}

View File

@@ -0,0 +1,96 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
//import java.awt.image.BufferedImage;
//import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
//import edu.gatech.cs2335.lemmings.physics.PhysicsVector;
//import edu.gatech.cs2335.lemmings.physics.Point;
//import edu.gatech.cs2335.lemmings.engine.Map;
//import edu.gatech.cs2335.lemmings.engine.MapSettings;
import edu.gatech.cs2335.lemmings.engine.Lemming;
//import edu.gatech.cs2335.lemmings.engine.GameEngine;
//import edu.gatech.cs2335.lemmings.engine.GamePlayState;
//import edu.gatech.cs2335.lemmings.engine.Level;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public class ClimberJob extends LemmingJob {
/**
*
*/
private boolean isClimbing;
/**
* the lemming direction
*/
private int direction;
/**
* Creates a new <code>ClimberJob</code> instance.
*
*/
public ClimberJob() {
super(LemmingJobSettings.JOB_ID_CLIMBER,
LemmingJobSettings.JOB_ID_CLIMBER + "_"
+ LemmingJobSettings.REGULAR_FALLER);
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
if (((Lemming) this.getOwner()).getVelocity().getI() > 0) {
direction = 1;
} else if (((Lemming) this.getOwner()).getVelocity().getI() < 0) {
direction = -1;
}
}
/**
* Method setIsClimbing
*
*
* @param isClimbing boolean
*
*/
public void setIsClimbing(boolean isClimbing) {
this.isClimbing = isClimbing;
}
/**
* Method getIsClimbing
*
*
* @return boolean , whether or not the lemming is climbing
*
*/
public boolean isClimbing() {
return (this.isClimbing);
}
/**
*mutator
*@param direction direction
*/
public void setDirection(int direction) {
this.direction = direction;
}
/**
*accessor
*@return int
*/
public int getDirection() {
return (this.direction);
}
}

View File

@@ -0,0 +1,111 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
import java.awt.geom.Ellipse2D;
//import java.awt.geom.Ellipse2D.Float;
import java.awt.image.BufferedImage;
import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
import edu.gatech.cs2335.lemmings.physics.PhysicsVector;
import edu.gatech.cs2335.lemmings.physics.Point;
import edu.gatech.cs2335.lemmings.engine.Map;
import edu.gatech.cs2335.lemmings.engine.MapSettings;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
import edu.gatech.cs2335.lemmings.engine.DirtParticle;
import edu.gatech.cs2335.lemmings.engine.PrettySprite;
//import edu.gatech.cs2335.lemmings.engine.Level;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* v1.5 - Jose Caban - added process() handling
* TOnotDO: correct problem with non-breakable areas
* getting nicked slightly
*
*
* @author <a href="mailto:gtg184g@mail.gatech.edu">Jose Caban</a>
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.5
*/
public class DiggerJob extends LemmingJob {
/**
* Dig a Circle!
*/
private Ellipse2D.Float circle;
/**
* Creates a new <code>DiggerJob</code> instance.
*
*/
public DiggerJob() {
super(LemmingJobSettings.JOB_ID_DIGGER);
circle = new Ellipse2D.Float(0, 0, 20, 10);
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
BufferedImage currentMap = PhysicsEngine.getInstance().getCurrentMap();
Map map = ((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel().getMap();
Lemming lem = this.getOwner();
PhysicsVector currentVelocity = lem.getVelocity();
Point currentPoint = lem.getPosition();
//going to check whether or not this lemming can still be a basher
int rayJ = currentPoint.getY();
if (LemmingJobSettings.BDEBUG) {
System.out.println("Calculating Basher Stuff...");
System.out.println("rayJ1..." + rayJ);
System.out.println("currentVelocity..." + currentVelocity.getJ());
}
for (int i = 0; i < LemmingJobSettings.DIGDISTANCE; rayJ++, i++) {
if (currentMap.getRGB(currentPoint.getX(), rayJ) == MapSettings
.getInstance().getUnbreakableColor()) {
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
return;
} else if (currentMap.getRGB(currentPoint.getX(), rayJ)
!= MapSettings.getInstance().getPassableColor()) {
if (LemmingJobSettings.BDEBUG) {
System.out.println("rayJ..." + rayJ);
}
circle.x = currentPoint.getX() - (circle.width) / 2;
circle.y = currentPoint.getY() - (circle.height) / 2;
map.subtractShape(circle);
//Make the debris
PrettySprite ps;
for (int j = 0; j < DirtParticle.PARTICLESWHENDIG; j++) {
ps = new DirtParticle(currentVelocity.getI(), currentVelocity.getJ());
ps.getPosition().setPoint(getOwner().getPosition().getX(),
getOwner().getPosition().getY(),
getOwner().getPosition().getZ());
((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel().addPrettySprite(ps);
}
return;
}
}
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
}
}

View File

@@ -0,0 +1,91 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
import java.awt.geom.Ellipse2D;
//import java.awt.geom.Ellipse2D.Float;
//import java.awt.image.BufferedImage;
//import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
//import edu.gatech.cs2335.lemmings.physics.PhysicsVector;
import edu.gatech.cs2335.lemmings.physics.Point;
//import edu.gatech.cs2335.lemmings.engine.Map;
//import edu.gatech.cs2335.lemmings.engine.MapSettings;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
import edu.gatech.cs2335.lemmings.engine.Level;
//import edu.gatech.cs2335.lemmings.graphics.TileSet;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* v1.2 - Basic Exploding
*
* @author <a href="mailto:gtg184g@mail.gatech.edu">Jose Caban</a>
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.2
*/
public class ExploderJob extends LemmingJob {
/**
* Time to blow
*/
private int timeLeftOnEarth;
/**
* Explosion thing
*/
private Ellipse2D.Float circle;
/**
* The tileset with the numbers.
*/
// private TileSet numbers;
/**
* Creates a new <code>ExploderJob</code> instance.
*
*/
public ExploderJob() {
super(LemmingJobSettings.JOB_ID_EXPLODER);
timeLeftOnEarth = LemmingJobSettings.BLOWUPTIME;
circle = new Ellipse2D.Float(0, 0,
LemmingJobSettings.EXPLOSIONDIAMETER,
LemmingJobSettings.EXPLOSIONDIAMETER);
}
/**
* Describe <code>getTimeLeftOnEarth</code> method here.
*
* @return an <code>int</code> value
*/
public int getTimeLeftOnEarth() {
return timeLeftOnEarth;
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
if (timeLeftOnEarth == 0) {
Level level = ((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel();
Lemming lem = this.getOwner();
Point currentPoint = lem.getPosition();
circle.x = currentPoint.getX() - (circle.width / 2);
circle.y = currentPoint.getY() - (circle.width / 2) - 6;
level.getMap().subtractShape(circle);
level.killLemming(lem, false);
}
timeLeftOnEarth--;
}
}

View File

@@ -0,0 +1,29 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public class FloaterJob extends LemmingJob {
/**
* Creates a new <code>FloaterJob</code> instance.
*
*/
public FloaterJob() {
super(LemmingJobSettings.JOB_ID_FLOATER,
LemmingJobSettings.JOB_ID_FLOATER + "_"
+ LemmingJobSettings.REGULAR_FALLER);
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
///yeah...
}
}

View File

@@ -0,0 +1,73 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
//import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
import edu.gatech.cs2335.lemmings.engine.PrettySprite;
import edu.gatech.cs2335.lemmings.engine.FlameParticle;
//import edu.gatech.cs2335.lemmings.engine.*;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* @author <A HREF="mailto:gtg184g@mail.gatech.edu">Jose Caban</A>
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public class HandGliderJob extends LemmingJob {
/**
* Used to keep track of when to reset out little AE major
*/
private boolean hasFlown;
/**
* Creates a new <code>HandGliderJob</code> instance.
*
*/
public HandGliderJob() {
super(LemmingJobSettings.JOB_ID_HAND_GLIDER,
LemmingJobSettings.JOB_ID_HAND_GLIDER + "_"
+ LemmingJobSettings.REGULAR_FALLER);
hasFlown = false;
}
/**
* This function will be called whenever the owner is set and it is
* not NULL.
*/
protected void onSetOwner() {
}
/**
*@return boolean
*/
public boolean hasFlown() {
return hasFlown;
}
/**
*@param b boolean
*/
public void setFlown(boolean b) {
this.hasFlown = b;
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
//No need for this, Physics handles it
PrettySprite ps;
for (int i = 0; i < FlameParticle.PARTICLESWHENFLAME; i++) {
ps = new FlameParticle();
ps.getPosition().setPoint(getOwner().getPosition().getX(),
getOwner().getPosition().getY(),
getOwner().getPosition().getZ());
((GamePlayState)
GameEngine.getInstance().getCurrentState()).getLevel().addPrettySprite(ps);
}
}
}

View File

@@ -0,0 +1,37 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
import edu.gatech.cs2335.lemmings.engine.Lemming;
//import edu.gatech.cs2335.lemmings.physics.PhysicsVector;
import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public class JumperJob extends LemmingJob {
/**
* Creates a new <code>JumperJob</code> instance.
*
*/
public JumperJob() {
super(LemmingJobSettings.JOB_ID_JUMPER);
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
//Jose is a nUb.
Lemming lem = getOwner();
if (lem.getVelocity().getJ() == 0) {
lem.getVelocity().setJ(PhysicsSettings.getInstance().getLaunchSpeed());
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
}
}
}

View File

@@ -0,0 +1,183 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public abstract class LemmingJob {
/**
* The string id of the lemming job.
*/
private String id;
/**
* The lemming, with which this job is associated.
*/
private Lemming owner;
/**
* The regular animation associated with the job.
*/
private AnimatedSprite regularAnimation;
/**
* The falling animation associated with the job.
*/
private AnimatedSprite fallingAnimation;
/**
* Creates a new <code>LemmingJob</code> instance.
*/
protected LemmingJob() {
this(LemmingJobSettings.JOB_ID_WALKER);
}
/**
* Creates a new <code>LemmingJob</code> instance.
*
* @param jobId a <code>String</code> value
*/
protected LemmingJob(String jobId) {
this(jobId, LemmingJobSettings.REGULAR_FALLER);
}
/**
* Creates a new <code>LemmingJob</code> instance.
*
* @param regularId a <code>String</code> value
* @param fallingId a <code>String</code> value
*/
protected LemmingJob(String regularId, String fallingId) {
//Set up Job Id:
id = regularId;
//Set up Owner:
owner = null;
//Set up animations:
regularAnimation
= new AnimatedSprite(TileSetManager.getInstance()
.getTileSet(regularId),
Direction.RIGHT,
Looping.INFINITE);
fallingAnimation = new AnimatedSprite(TileSetManager.getInstance()
.getTileSet(fallingId),
Direction.NO_DIRECTION,
Looping.INFINITE);
}
/**
* Describe <code>cleanUp</code> method here.
*
*/
public void cleanUp() {
id = null;
owner = null;
regularAnimation = null;
fallingAnimation = null;
}
/**
* Access method for the id property.
*
* @return the current value of the id property
*/
public String getId() {
return id;
}
/**
* Access method for the owner property.
*
* @return the current value of the owner property
*/
public Lemming getOwner() {
return owner;
}
/**
* Sets the value of the owner property.
*
* @param aOwner the new value of the owner property
*/
public void setOwner(Lemming aOwner) {
owner = aOwner;
if (owner != null) {
onSetOwner();
}
}
/**
* Access method for the animation property.
*
* @return the current value of the animation property
*/
public AnimatedSprite getAnimation() {
if (owner.getPhysics().isFalling()) {
return fallingAnimation;
} else {
return regularAnimation;
}
}
/**
* Set the value of the regular animation for this lemming.
*
* @param animation an <code>AnimatedSprite</code> value
*/
protected void setRegularAnimation(AnimatedSprite animation) {
regularAnimation.setAnimation(animation.getAnimation());
}
/**
* Describe <code>getRegularAnimation</code> method here.
*
* @return an <code>AnimatedSprite</code> value
*/
public AnimatedSprite getRegularAnimation() {
return regularAnimation;
}
/**
* Describe <code>getFallingAnimation</code> method here.
*
* @return an <code>AnimatedSprite</code> value
*/
public AnimatedSprite getFallingAnimation() {
return fallingAnimation;
}
/**
* Set the value of the falling animation for this lemming.
*
* @param animation an <code>AnimatedSprite</code> value
*/
protected void setFallingAnimation(AnimatedSprite animation) {
fallingAnimation.setAnimation(animation.getAnimation());
}
//@roseuid 4065FCD300AE
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*
*/
public abstract void process();
/**
* This function will be called whenever the owner is set and it is
* not NULL.
*/
protected void onSetOwner() { }
}

View File

@@ -0,0 +1,85 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
/**
* Creates lemming jobs.
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version 1.0
*/
public class LemmingJobFactory {
/**
* Singleton Implementation.
*/
private static LemmingJobFactory instance;
/**
* Creates a new <code>LemmingJobFactory</code> instance.
*
*/
private LemmingJobFactory() { }
/**
* Access method for the instance property.
*
* @return the current value of the instance property.
*/
public static LemmingJobFactory getInstance() {
if (instance == null) {
instance = new LemmingJobFactory();
}
return instance;
}
//@roseuid 406602CE0394
/**
* Creates a lemming job with the specified id and initializes it
* appropriately.
*
* @param id The id of the job type to create.
* @return The new lemming job.
*
*/
public LemmingJob makeJob(String id) {
LemmingJob result = null;
if (LemmingJobSettings.JOB_ID_BASHER.equals(id)) {
result = new BasherJob();
} else if (LemmingJobSettings.JOB_ID_BLOCKER.equals(id)) {
result = new BlockerJob();
} else if (LemmingJobSettings.JOB_ID_BRIDGER.equals(id)) {
result = new BridgerJob();
} else if (LemmingJobSettings.JOB_ID_CLIMBER.equals(id)) {
result = new ClimberJob();
} else if (LemmingJobSettings.JOB_ID_DIGGER.equals(id)) {
result = new DiggerJob();
} else if (LemmingJobSettings.JOB_ID_EXPLODER.equals(id)) {
result = new ExploderJob();
} else if (LemmingJobSettings.JOB_ID_FLOATER.equals(id)) {
result = new FloaterJob();
} else if (LemmingJobSettings.JOB_ID_HAND_GLIDER.equals(id)) {
result = new HandGliderJob();
} else if (LemmingJobSettings.JOB_ID_JUMPER.equals(id)) {
result = new JumperJob();
} else if (LemmingJobSettings.JOB_ID_MINER.equals(id)) {
result = new MinerJob();
} else if (LemmingJobSettings.JOB_ID_WALKER.equals(id)) {
result = new WalkerJob();
} else {
throw new IllegalArgumentException("Bad job id");
}
return result;
}
}

View File

@@ -0,0 +1,137 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
/**
* Contains some settings for the lemming jobs.
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
*/
public final class LemmingJobSettings {
/**
*
*/
public static final boolean BDEBUG = false;
/**
* The id the basher job type.
*/
public static final String JOB_ID_BASHER = "basher";
/**
* The id the blocker job type.
*/
public static final String JOB_ID_BLOCKER = "blocker";
/**
* The id the bridger job type.
*/
public static final String JOB_ID_BRIDGER = "bridger";
/**
* The id the climber job type.
*/
public static final String JOB_ID_CLIMBER = "climber";
/**
* The id the digger job type.
*/
public static final String JOB_ID_DIGGER = "digger";
/**
* The id the exploder job type.
*/
public static final String JOB_ID_EXPLODER = "exploder";
/**
* The id the floater job type.
*/
public static final String JOB_ID_FLOATER = "floater";
/**
* The id the hand_glider job type.
*/
public static final String JOB_ID_HAND_GLIDER = "hand_glider";
/**
* The id the jumper job type.
*/
public static final String JOB_ID_JUMPER = "jumper";
/**
* The id the launcher job type.
*/
public static final String JOB_ID_LAUNCHER = "launcher";
/**
* The id the miner job type.
*/
public static final String JOB_ID_MINER = "miner";
/**
* The id the walker job type.
*/
public static final String JOB_ID_WALKER = "walker";
/**
* The id of the regular faller animation.
*/
public static final String REGULAR_FALLER = "faller";
/**
* Contains all of the job names in a nice little array.
*/
public static final String[] ALL_JOB_NAMES = {
JOB_ID_BASHER,
JOB_ID_BLOCKER,
JOB_ID_BRIDGER,
JOB_ID_CLIMBER,
JOB_ID_DIGGER,
JOB_ID_EXPLODER,
JOB_ID_FLOATER,
JOB_ID_HAND_GLIDER,
JOB_ID_JUMPER,
JOB_ID_LAUNCHER,
JOB_ID_MINER,
JOB_ID_WALKER,
};
/**
* The max distance between a basher and a wall to bash
*/
public static final int BASHDISTANCE = 75;
/**
* The distance to the point to dig
*/
public static final int DIGDISTANCE = 8;
/**
* The distance to the point to mine
*/
public static final int MINEDISTANCE = 20;
/**
* Time to blow
*/
public static final int BLOWUPTIME = 30;
/**
* Explosion Diameter
*/
public static final int EXPLOSIONDIAMETER = 42;
/**
* Blocker Tolerance (distance to change direction)
*/
public static final int BLOCKERTOLERANCE = 10;
/**
* Bridger wait time
*/
public static final int BRIDGERDONETIME = 50;
/**
* Creates a new <code>LemmingJobSettings</code> instance.
*/
private LemmingJobSettings() { }
}

View File

@@ -0,0 +1,142 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
import java.awt.geom.Ellipse2D;
//import java.awt.geom.Ellipse2D.Float;
import java.awt.image.BufferedImage;
import edu.gatech.cs2335.lemmings.physics.PhysicsEngine;
//import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
import edu.gatech.cs2335.lemmings.physics.PhysicsVector;
import edu.gatech.cs2335.lemmings.physics.Point;
import edu.gatech.cs2335.lemmings.engine.Map;
import edu.gatech.cs2335.lemmings.engine.MapSettings;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
import edu.gatech.cs2335.lemmings.engine.DirtParticle;
import edu.gatech.cs2335.lemmings.engine.PrettySprite;
//import edu.gatech.cs2335.lemmings.engine.Level;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* v1.5 - Jose Caban - added process() handling
* TOnotDO: correct problem with non-breakable areas
* getting nicked slightly
*
*
* @author <a href="mailto:gtg184g@mail.gatech.edu">Jose Caban</a>
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.5
*/
public class MinerJob extends LemmingJob {
/**
* Dig a Circle!
*/
private Ellipse2D.Float circle;
/**
* Creates a new <code>MinerJob</code> instance.
*
*/
public MinerJob() {
super(LemmingJobSettings.JOB_ID_MINER);
circle = new Ellipse2D.Float(0, 0, 20, 10);
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
BufferedImage currentMap = PhysicsEngine.getInstance().getCurrentMap();
Map map = ((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel().getMap();
Lemming lem = this.getOwner();
PhysicsVector currentVelocity = lem.getVelocity();
Point currentPoint = lem.getPosition();
//going to check whether or not this lemming can still be a basher
int rayI = currentPoint.getX();
// int max = LemmingJobSettings.MINEDISTANCE + rayI;
if (LemmingJobSettings.BDEBUG) {
System.out.println("Calculating Miner Stuff...");
System.out.println("rayI1..." + rayI);
System.out.println("currentVelocity..." + currentVelocity.getI());
}
boolean bFoundDirt = false;
if (currentVelocity.getI() > 0) {
for (int i = 0; i < LemmingJobSettings.MINEDISTANCE; rayI++, i++) {
if (currentMap.getRGB(rayI, currentPoint.getY() + i)
== MapSettings.getInstance().getUnbreakableColor()) {
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
return;
} else if (currentMap.getRGB(rayI, currentPoint.getY() + i)
!= MapSettings.getInstance().getPassableColor()) {
bFoundDirt = true;
if (LemmingJobSettings.BDEBUG) {
System.out.println("rayI..." + rayI);
}
}
if (LemmingJobSettings.BDEBUG) {
System.out.println("Currently at " + rayI
+ (currentPoint.getY() + i));
}
}
if (bFoundDirt) {
circle.x = currentPoint.getX();
circle.y = currentPoint.getY();
map.subtractShape(circle);
//Make the debris
PrettySprite ps;
for (int j = 0; j < DirtParticle.PARTICLESWHENDIG; j++) {
ps = new DirtParticle(currentVelocity.getI(), currentVelocity.getJ());
ps.getPosition().setPoint(getOwner().getPosition().getX(),
getOwner().getPosition().getY(),
getOwner().getPosition().getZ());
((GamePlayState) GameEngine.getInstance()
.getCurrentState()).getLevel().addPrettySprite(ps);
}
return;
}
} else {
for (int i = 0; i < LemmingJobSettings.MINEDISTANCE; rayI--, i++) {
if (currentMap.getRGB(rayI, currentPoint.getY() + i)
== MapSettings.getInstance().getUnbreakableColor()) {
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
return;
} else if (currentMap.getRGB(rayI, currentPoint.getY() + i)
!= MapSettings.getInstance().getPassableColor()) {
bFoundDirt = true;
}
if (LemmingJobSettings.BDEBUG) {
System.out.println("Currently at " + rayI
+ (currentPoint.getY() + i));
}
}
if (bFoundDirt) {
circle.x = currentPoint.getX() - 20;
circle.y = currentPoint.getY();
map.subtractShape(circle);
return;
}
}
lem.setOccupation(LemmingJobSettings.JOB_ID_WALKER);
}
}

View File

@@ -0,0 +1,44 @@
package edu.gatech.cs2335.lemmings.engine.lemmingjob;
import edu.gatech.cs2335.lemmings.physics.PhysicsSettings;
/**
* This class represents a lemming job. Essentially, it defines what
* the lemming does in the world, what kind of effect it has, etc.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public class WalkerJob extends LemmingJob {
/**
* Creates a new <code>WalkerJob</code> instance.
*
*/
public WalkerJob() {
super();
}
/**
* This function will be called whenever the owner is set and it is
* not NULL.
*/
protected void onSetOwner() {
if (getOwner().getVelocity().getI() >= 0) {
getOwner().getVelocity().setI(PhysicsSettings.getInstance()
.getInitialX());
} else {
getOwner().getVelocity().setI(-PhysicsSettings.getInstance()
.getInitialX());
}
}
/**
* Processes the effect of the lemming on the world, as well as the
* lemming's position in the world.
*/
public void process() {
//Since this is a plain walker lemming, there is really nothing to
//do here.
}
}

View File

@@ -0,0 +1,574 @@
package edu.gatech.cs2335.lemmings.file;
import java.util.Map;
//import java.util.List;
//import java.util.Vector;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
//import java.awt.image.BufferedImage;
import edu.gatech.cs2335.lemmings.engine.Level;
import edu.gatech.cs2335.lemmings.engine.LevelData;
import edu.gatech.cs2335.lemmings.engine.PortalFactory;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobSettings;
import edu.gatech.cs2335.lemmings.graphics.ImageLoader;
import edu.gatech.cs2335.lemmings.graphics.TileSet;
import edu.gatech.cs2335.lemmings.graphics.ImprovedTileSet;
/**
* Allows us to read level data, maps and other nice stuff. The string
* passed in to any of the "load" methods is basically the id of the
* level. The loader then tests if there is a directory with the same
* name in the levels list. If there is one, then it loads the level
* from it. If there is no such directory, then is appends a .lmf
* extension to it and checks wether there is a file with that
* name. If there is, it extracts the file and then loads the level
* from that. If there is no such file, then the loading fails and
* null is returned.
*
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version 1.0
*/
public class LevelReader {
/**
* Display debug stuff?
*/
private static final boolean VERBOSE = false;
/**
* Singleton Implementation.
*/
private static final LevelReader INSTANCE = new LevelReader();
/**
* The level data is not going to change over the course of the
* game. Thus, if we've loaded it once, we are not going to load it
* again. It will be stored here, instead.
*/
private Map data;
/**
* A map of maps... :/
*/
private Map maps;
/**
* Creates a new <code>LevelReader</code> INSTANCE.
*
*/
private LevelReader() {
data = new HashMap();
maps = new HashMap();
}
/**
* Access method for the INSTANCE property.
*
* @return the current value of the INSTANCE property
*/
public static LevelReader getInstance() {
return INSTANCE;
}
//@roseuid 4066356A0190
/**
* Returns the list of all levels available. The elements in the
* list returned are String ids of the available levels.
* @return The array of ids of the levels.
*
*
*/
public String[] getLevelList() /*throws Exception*/ {
String path = "levels";
File f = new File(path);
if (!f.exists() || !f.isDirectory()) {
if (VERBOSE) {
System.out.println("Could not find the levels directory.");
}
//throw new Exception("Could not find the levels directory.");
}
String[] levels = f.list(new LevelNameFilter());
for (int i = 0; i < levels.length; i++) {
int index = levels[i].lastIndexOf(".");
if (index > 0) {
levels[i] = levels[i].substring(0, index);
}
}
if (VERBOSE) {
System.out.println("LevelReader: List of available levels:");
for (int i = 0; i < levels.length; i++) {
System.out.println("\t" + levels[i]);
}
System.out.flush();
}
return levels;
}
/**
* Given the id of a level, finds the next level. Basically, if the
* level id cannot be found in the list, returns the first element
* of the list. If the level id is the last level, returns null.
*
* @param id a <code>String</code> value
* @return a <code>String</code> value
*/
public String findNextLevel(String id) {
String[] levels = getLevelList();
int index = -1;
if (VERBOSE) {
System.err.println("LevelReader.findNextLevel - Current level:");
System.err.println("\t\"" + id + "\"");
System.err.flush();
}
if (id == null || id.equals("")) {
return levels[0];
}
for (int i = 0; i < levels.length; i++) {
if (id.equals(levels[i])) {
index = i;
break;
}
}
if (index < 0) {
//Did not find a level with this name:
return levels[0];
}
if (index == levels.length - 1) {
//Got last level:
return null;
}
if (VERBOSE) {
System.err.println("LevelReader.findNextLevel - next level:");
System.err.println("\t\"" + levels[index + 1] + "\"");
System.err.flush();
}
return levels[index + 1];
}
//@roseuid 40663BFF008F
/**
* Loads the level with the name specified if possible. Otherwise,
* returns null.
*
* @param name The name of the level to load.
* @return Level
*
*/
public Level loadLevel(String name) {
Level result = null;
result = new Level();
result.setMap(loadMap(name));
result.setData(loadLevelData(name));
return result;
}
/**
* Loads the map for the level with the specified name.
*
* @param name a <code>String</code> value
* @return a <code>Map</code> value
*/
public edu.gatech.cs2335.lemmings.engine.Map loadMap(String name) {
//Note here we have to use this ugly qualified name with the
//package, because otherwise, it will conflict with java.util.Map
//that we are also using here.
edu.gatech.cs2335.lemmings.engine.Map m = null;
File f = null;
if (maps.containsKey(name)) {
return (edu.gatech.cs2335.lemmings.engine.Map) maps.get(name);
}
m = new edu.gatech.cs2335.lemmings.engine.Map();
f = getFile(name);
if (f == null) { return null; }
//Set up files:
File bgFile = new File(f, "background.png");
File mapFile = new File(f, "terrain.png");
File txFile = new File(f, "texture.png");
File utxFile = new File(f, "unbreakableTexture.png");
File waterFile = new File(f, "water.png");
if (VERBOSE) {
System.out.println("\nLoading Map:");
System.out.println("\tBG: " + bgFile.getAbsolutePath());
System.out.println("\t\tExists? " + bgFile.exists());
System.out.println("\tMP: " + mapFile.getAbsolutePath());
System.out.println("\t\tExists? " + mapFile.exists());
System.out.println("\tTX: " + txFile.getAbsolutePath());
System.out.println("\t\tExists? " + txFile.exists());
System.out.println("\tWT: " + waterFile.getAbsolutePath());
System.out.println("\t\tExists? " + waterFile.exists());
System.out.flush();
}
if (VERBOSE) {
System.out.println("\n\nLoading Water tileset:");
System.out.flush();
}
TileSet ts = new ImprovedTileSet();
ts.loadTileset(waterFile.getAbsolutePath());
if (VERBOSE) {
System.out.println("\nDone... Loaded " + ts.getTileCount() + " frames.");
System.out.flush();
}
m.setBackground(ImageLoader.getInstance().loadImage(bgFile));
m.setMap(ImageLoader.getInstance().loadImage(mapFile));
m.setTexture(ImageLoader.getInstance().loadImage(txFile));
m.setUnbreakableTexture(ImageLoader.getInstance().loadImage(utxFile));
m.setWaterAnimation(ts);
return m;
}
//@roseuid 40663C5300B7
/**
* Loads the data for the level with the specified name.
*
* @param name The name of the level to load the data for.
* @return LevelData
*
*/
public LevelData loadLevelData(String name) {
File f = null;
LevelData result = null;
if (data.containsKey(name)) {
//The level data has already been loaded. Just return it:
return (LevelData) data.get(name);
}
f = getFile(name);
result = new LevelData();
if (f == null) {
throw new IllegalArgumentException("Could not find level " + name);
}
if (f.isDirectory()) {
//Load from the directory:
//Load the properties first:
FileInputStream fis = null;
Properties p = new Properties();
File pf = new File(f.getAbsolutePath(), "data.dat");
//Open up the resource properties file:
try {
fis = new FileInputStream(pf);
} catch (IOException ioe) {
System.err.println("LevelReader: Could not open data file...");
System.err.println("\tIOException: " + ioe.getMessage());
System.err.flush();
return null;
}
try {
p.load(fis);
} catch (IOException ioe) {
System.err.println("LevelReader: Could not "
+ "read the data file...");
System.err.println("\tIOException: " + ioe.getMessage());
System.err.flush();
return null;
}
if (VERBOSE) {
System.out.println("LevelReader: Loaded the following properties");
System.out.println(p);
System.out.flush();
}
//ID
result.setId(name);
//Fill the data with properties:
loadLevelData(result, p);
//Load the thumbnail:
result.setThumbnail(ImageLoader.getInstance()
.loadImage(new File(f.getAbsolutePath(),
"thumb.png")));
}
//Would have been nice to actually implement loading from archive...
if (result != null) {
data.put(name, result);
}
return result;
}
/**
* After the properties have been read from a file, this function
* will load the level data with them.
*
* @param result a <code>LevelData</code> value
* @param p a <code>Properties</code> value
*/
private void loadLevelData(LevelData result, Properties p) {
//Name
result.setName(p.getProperty("name", "Default Level"));
//Number of Lemmings:
try {
result.setNumLemmings(Integer.parseInt(p.getProperty("num_lemmings",
"10")));
} catch (NumberFormatException nfe) {
result.setNumLemmings(10);
}
//Percent to Save:
try {
result.setPercentToSave(Integer.parseInt(p.getProperty("save_percent",
"50")));
} catch (NumberFormatException nfe) {
result.setPercentToSave(50);
}
//Time Limit:
try {
result.setTimeLimit(Long.parseLong(p.getProperty("time_limit",
"600")));
} catch (NumberFormatException nfe) {
result.setTimeLimit(600);
}
//Portals:
for (int i = 0; i < PortalFactory.PORTAL_TYPES.length; i++) {
String s = p.getProperty(PortalFactory.PORTAL_TYPES[i]);
if (s == null) {
//No portals of this type in the level.
continue;
}
//Different portals of the same type delimited by commas:
StringTokenizer st = new StringTokenizer(s, ",");
while (st.hasMoreTokens()) {
result.addPortal(PortalFactory.PORTAL_TYPES[i], st.nextToken());
}
}
// //Entrance:
// try {
// int x = 0;
// int y = 0;
// x = Integer.parseInt(p.getProperty("entrance_x", "0"));
// y = Integer.parseInt(p.getProperty("entrance_y", "0"));
// result.setEntrancePoint(x, y);
// } catch (NumberFormatException nfe) {
// result.setEntrancePoint(0, 0);
// }
// //Exit:
// try {
// int x = 0;
// int y = 0;
// x = Integer.parseInt(p.getProperty("exit_x", "0"));
// y = Integer.parseInt(p.getProperty("exit_y", "0"));
// result.setExitPoint(x, y);
// } catch (NumberFormatException nfe) {
// result.setExitPoint(0, 0);
// }
//Load the jobs:
for (int i = 0; i < LemmingJobSettings.ALL_JOB_NAMES.length; i++) {
int num = 0;
try {
num = Integer
.parseInt(p.getProperty(LemmingJobSettings.ALL_JOB_NAMES[i], "0"));
} catch (NumberFormatException nfe) {
num = 0;
}
if (num > 0) {
//Set the number of appropriate jobs in the level data.
if (VERBOSE) {
System.out.print("LevelReader: Got " + num + " jobs of type ");
System.out.println("\""
+ LemmingJobSettings.ALL_JOB_NAMES[i] + "\"");
System.out.flush();
}
result.addJob(LemmingJobSettings.ALL_JOB_NAMES[i], num);
}
}
//Load water level:
try {
result.setWaterLevel(Integer.parseInt(p.getProperty("water_level",
"-1")));
} catch (NumberFormatException nfe) {
result.setWaterLevel(-1);
}
}
/**
* Given a name of the level, returns a file for where the thing is
* located. Or null, if it is not present.
*
* @param name a <code>String</code> value
* @return a <code>File</code> value
*/
private File getFile(String name) {
String path = "levels/" + name;
File f = new File(path);
if (VERBOSE) {
System.out.println("LevelReader: Got file - " + f.getAbsolutePath());
System.out.println(" File Exists? " + f.exists());
System.out.flush();
}
if (f.exists()) {
return f;
} else { //Do NOT make this recursive
path += ".lmf";
f = new File(path);
if (VERBOSE) {
System.out.println("LevelReader: Got file - " + f.getAbsolutePath());
System.out.println(" File Exists? " + f.exists());
System.out.flush();
}
if (f.exists()) {
return f;
}
}
return null;
}
/**
*
*@param args args
*/
public static void main(String[] args) {
LevelReader lr = new LevelReader();
System.out.println("Loaded data:");
System.out.println(lr.loadLevelData("test_level"));
System.out.flush();
try {
lr.getLevelList();
} catch (Exception e) {
//sux
System.out.println(e.getMessage());
System.out.flush();
System.exit(1);
}
}
/**
* Filters out the non-level files from the file dir.
*/
private class LevelNameFilter implements java.io.FilenameFilter {
/**
* Tests if a specified file should be included in a file list.
*
* @param dir the directory in which the file was found.
* @param name the name of the file.
* @return true if and only if the name should be included in the
* file list; false otherwise.
*/
public boolean accept(File dir, String name) {
File testFile = new File(dir, name);
if (!testFile.exists()) { return false; }
if (testFile.isDirectory()) {
//Check that the dir contains all the stuff we need:
File bgFile = new File(testFile, "background.png");
if (!bgFile.exists()) { return false; }
File datFile = new File(testFile, "data.dat");
if (!datFile.exists()) {
return false;
}
File terrainFile = new File(testFile, "terrain.png");
if (!terrainFile.exists()) {
return false;
}
File textureFile = new File(testFile, "texture.png");
if (!textureFile.exists()) {
return false;
}
File ubTextureFile = new File(testFile, "unbreakableTexture.png");
if (!ubTextureFile.exists()) {
return false;
}
File thumbFile = new File(testFile, "thumb.png");
if (!thumbFile.exists()) {
return false;
}
File waterFile = new File(testFile, "water.png");
if (!waterFile.exists()) {
return false;
}
return true;
} else {
//Check that the file has the correct extension:
String extn;
int index = name.lastIndexOf('.');
if (index <= 0) {
//The file doesn't have *any* extension
return false;
}
extn = name.substring(index);
if (extn.equalsIgnoreCase(".lmf")) {
return true;
}
return false;
}
}
}
}

View File

@@ -0,0 +1,268 @@
package edu.gatech.cs2335.lemmings.graphics;
import java.awt.Point;
import java.awt.Graphics;
/**
* Class AnimatedSprite: This animated sprite will be used to render
* all things animated onto the screen. It will be created using a
* TileSet, and will treat the frames as follows: it will keep track
* of which direction the sprite is moving in. If we are moving in a
* certain direction, we will only use half of the frames - which half
* depends on where we are moving - from left to right means using the
* first half of the frames, and from right to left - the second
* half. If the sprite is not moving in any direction, all frames will
* be used in the animation.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 12, 2004) - Created the AnimatedSprite class
* </PRE>
*
* @see TileSet
* @see TileSetManager
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 12, 2004
*/
public final class AnimatedSprite {
/**
* The direction in which the sprite is currently moving.
*/
private Direction movementDirection;
/**
* The looping mode for the animation.
*/
private Looping loopingMode;
/**
* The tileset holding the frames of animation.
*/
private TileSet animation;
/**
* The frame to be displayed.
*/
private int currentFrame;
/**
* Creates a new <code>AnimatedSprite</code> instance.
*/
public AnimatedSprite() {
this(null);
}
/**
* Creates a new <code>AnimatedSprite</code> instance.
*
* @param anim a <code>TileSet</code> value
*/
public AnimatedSprite(TileSet anim) {
this(anim, Direction.NO_DIRECTION, Looping.NONE);
}
/**
* Creates a new <code>AnimatedSprite</code> instance.
*
* @param anim a <code>TileSet</code> value
* @param dir a <code>Direction</code> value
* @param loop a <code>Looping</code> value
*/
public AnimatedSprite(TileSet anim, Direction dir, Looping loop) {
setAnimation(anim);
setMovementDirection(dir);
setLoopingMode(loop);
resetFrameNumber();
}
/**
* Returns true if the sprite is initialized with animation already,
* and false f it is not yet.
*
* @return a <code>boolean</code> value
*/
public synchronized boolean isInitialized() {
return animation != null;
}
/**
* Returns the number of frames in a complete loop of animation.
*
* @return an <code>int</code> value
*/
protected synchronized int getNumFrames() {
if (!isInitialized()) {
return -1;
}
if ((Direction.LEFT == movementDirection)
|| (Direction.RIGHT == movementDirection)) {
return animation.getTileCount() / 2;
} else if (Direction.NO_DIRECTION == movementDirection) {
return animation.getTileCount();
} else {
System.err.println("AnimatedSprite: Got bad value for the direction.");
System.err.flush();
return -1;
}
}
/**
* Returns the direction in which the sprite is moving.
*
* @return a <code>Direction</code> value
*/
public synchronized Direction getMovementDirection() {
return (this.movementDirection);
}
/**
* Sets a direction in which the sprite is moving. If the value
* passed in is NULL, an exception will be thrown.
*
* @param v a <code>Direction</code> value
*/
public synchronized void setMovementDirection(Direction v) {
if (v == null) {
throw new NullPointerException();
}
this.movementDirection = v;
resetFrameNumber();
}
/**
* Returns the looping mode.
*
* @return a <code>Looping</code> value
*/
public synchronized Looping getLoopingMode() {
return (this.loopingMode);
}
/**
* Sets a new looping mode. If the value passed in is NULL, an
* exception will be thrown.
*
* @param v a <code>Looping</code> value
*/
public synchronized void setLoopingMode(Looping v) {
if (v == null) {
throw new NullPointerException();
}
this.loopingMode = v;
resetFrameNumber();
}
/**
* Returns the tileset associated with the sprite.
*
* @return a <code>TileSet</code> value
*/
public synchronized TileSet getAnimation() {
return (this.animation);
}
/**
* Sets the new animation for the sprite.
*
* @param v a <code>TileSet</code> value
*/
public synchronized void setAnimation(TileSet v) {
this.animation = v;
resetFrameNumber();
}
/**
* Returns the current frame number.
*
* @return an <code>int</code> value.
*/
public synchronized int getCurrentFrame() {
return currentFrame;
}
/**
* Resets the current frame number.
*/
private synchronized void resetFrameNumber() {
if (isInitialized()) {
if (getMovementDirection() == Direction.LEFT) {
currentFrame = getNumFrames();
} else {
currentFrame = 0;
}
}
}
/**
* Returns the next frame number and increments the counter.
*
* @return an <code>int</code> value.
*/
public synchronized int nextFrameNumber() {
Looping l = getLoopingMode();
Direction d = getMovementDirection();
boolean endAnimation = false;
if (Looping.NONE == l) {
//Static sprite. Nothing more to do.
return currentFrame;
}
//Next frame:
currentFrame++;
if (Direction.NO_DIRECTION == d || Direction.RIGHT == d) {
if (currentFrame >= getNumFrames()) {
//Got to the end of the animation:
resetFrameNumber();
endAnimation = true;
}
} else if (Direction.LEFT == d) {
if (currentFrame >= getNumFrames() * 2) {
//Got to the end of animation.
resetFrameNumber();
endAnimation = true;
}
}
//Terminate looping if necessary:
if (endAnimation && Looping.ONCE == l) {
setLoopingMode(Looping.NONE);
}
//Return new frame number:
return currentFrame;
}
/**
* Renders the sprite at the specified coordinates.
*
* @param g a <code>Graphics</code> value
* @param coordinates a <code>Point</code> value
* @return a <code>boolean</code> value
*/
public synchronized boolean renderSprite(Graphics g, Point coordinates) {
if (!isInitialized()) {
//Sprite not initialized. Cannot draw.
return false;
}
//Draw stuff:
getAnimation().drawTile(g, coordinates, getCurrentFrame());
//Get next frame number:
//nextFrameNumber();
//Done:
return true;
}
}

View File

@@ -0,0 +1,62 @@
package edu.gatech.cs2335.lemmings.graphics;
import javax.print.attribute.EnumSyntax;
/**
* Class Direction: an enumeration for the directions a sprite could
* be facing.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 12, 2004) - Created the Direction class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 12, 2004
*/
public class Direction extends EnumSyntax {
/**
* Represents the direction of heading nowhere. Neither left nor
* right. This one will be used by the immobile lemmings, like the
* blocker.
*/
public static final Direction NO_DIRECTION = new Direction(0);
/**
* Represents the right-to-left motion direction.
*/
public static final Direction LEFT = new Direction(1);
/**
* Represents the left-to-right motion direction.
*/
public static final Direction RIGHT = new Direction(2);
// /**
// * Part of the EnumSyntax implementation.
// */
// private static final String[] stringTable = {
// "No Direction",
// "Left",
// "Right"};
// /**
// * Part of the EnumSyntax implementation.
// */
// private static final Direction[] enumValueTable = {
// NO_DIRECTION,
// LEFT,
// RIGHT
// };
/**
* Creates a new <code>Direction</code> instance.
*
* @param value an <code>int</code> value
*/
private Direction(int value) {
super(value);
}
}

View File

@@ -0,0 +1,218 @@
package edu.gatech.cs2335.lemmings.graphics;
import java.net.URL;
import java.io.File;
import java.io.IOException;
import java.awt.Rectangle;
import java.awt.Graphics2D;
import java.awt.AlphaComposite;
import java.awt.image.BufferedImage;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsConfiguration;
/**
* Class ImageLoader: The image loader class provides the
* functionality for loading images from the hard drive or the web
* into BufferedImages.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 06, 2004) - Created the ImageLoader class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 06, 2004
*/
public final class ImageLoader {
/**
* Contains the location for all of the system images.
*/
public static final String IMAGE_LOCATION = "images/";
/**
* Implement this as a singleton.
*/
private static ImageLoader instance;
/**
* The default graphics configuration for the current client.
*/
private GraphicsConfiguration config;
/**
* Creates a new <code>ImageLoader</code> instance.
*/
private ImageLoader() {
config = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
}
/**
* Singleton implementation.
*
* @return an <code>ImageLoader</code> value
*/
public static ImageLoader getInstance() {
if (instance == null) {
instance = new ImageLoader();
}
return instance;
}
/**
* Returns the graphics configuration used.
*
* @return a <code>GraphicsConfiguration</code> value
*/
public GraphicsConfiguration getImageConfiguration() {
return config;
}
/**
* Loads the image from the specified URL. Returns an accelerated
* version of the image, ready to be rendered.
*
* @param path an <code>URL</code> value
* @return a <code>BufferedImage</code> value
*/
public BufferedImage loadImage(final URL path) {
if (path == null) {
System.err.println("ImageLoader.loadImage - received NULL path... "
+ "Loading aborted");
System.err.flush();
return null;
}
try {
BufferedImage result = javax.imageio.ImageIO.read(path);
if (result == null) {
System.err.println("ImageLoader.loadImage - could not read image \""
+ path.getPath() + "\"...");
System.err.flush();
return null;
}
return accelerateImage(result);
} catch (IOException ioe) {
System.err.println("ImageLoader.loadImage - Could not load image \""
+ path.getPath() + "\"...");
System.err.println("\tIOException: " + ioe.getMessage());
System.err.flush();
return null;
}
}
/**
* Loads the image from the file specified.
*
* @param src a <code>File</code> value
* @return a <code>BufferedImage</code> value
*/
public BufferedImage loadImage(final File src) {
if (src == null) {
System.err.println("ImageLoader.loadImage - received NULL file... "
+ "Loading aborted.");
System.err.flush();
return null;
}
if (!src.exists()) {
System.err.println("ImageLoader.loadImage - Could not find the file "
+ "specified... Loading aborted.");
System.err.println(src.getAbsolutePath());
System.err.flush();
return null;
}
try {
BufferedImage result = javax.imageio.ImageIO.read(src);
if (result == null) {
System.err.println("ImageLoader.loadImage - could not read image \""
+ src.getAbsolutePath() + "\"...");
System.err.flush();
return null;
}
return accelerateImage(result);
} catch (IOException ioe) {
System.err.println("ImageLoader.loadImage - Could not load image \""
+ src.getAbsolutePath() + "\"...");
System.err.println("\tIOException: " + ioe.getMessage());
System.err.flush();
return null;
}
}
/**
* Loads a local image for instance, for insertion into the graphics
* pane.
*
* @param imagePath a <code>String</code> value
* @return a <code>BufferedImage</code> value
*/
public BufferedImage loadLocalImage(final String imagePath) {
// return loadImage(this.getClass().getResource(imagePath));
return loadImage(new File(imagePath));
}
/**
* Loads a system image from the standard location.
*
* @param imageName a <code>String</code> value
* @return a <code>BufferedImage</code> value
*/
public BufferedImage loadSystemImage(final String imageName) {
return loadLocalImage(IMAGE_LOCATION + imageName);
}
/**
* Prepares a hardware-accelerated image if possible.
*
* @param src a <code>BufferedImage</code> value
* @return a <code>BufferedImage</code> value
*/
private BufferedImage accelerateImage(final BufferedImage src) {
BufferedImage result
= config.createCompatibleImage(src.getWidth(),
src.getHeight(),
src.getColorModel().getTransparency());
Graphics2D g2d = result.createGraphics();
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(src, 0, 0, null);
g2d.dispose();
return result;
}
/**
* Returns a hardware-accelerated blank image of the specified size.
*
* @param width an <code>int</code> value
* @param height an <code>int</code> value
* @return a <code>BufferedImage</code> value
*/
public BufferedImage createBlankImage(int width, int height) {
BufferedImage result
= config.createCompatibleImage(width, height,
java.awt.Transparency.BITMASK);
Graphics2D g2D = (Graphics2D) result.createGraphics();
g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
Rectangle rect = new Rectangle(0, 0, width, height);
g2D.fill(rect);
return result;
}
}

View File

@@ -0,0 +1,109 @@
package edu.gatech.cs2335.lemmings.graphics;
import java.awt.Point;
import java.awt.Image;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Toolkit;
//import java.awt.image.Raster;
//import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.MemoryImageSource;
/**
* Class ImageUtilities: contains certain utility functions that can
* be useful for various image manipulating classes.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 12, 2004) - Created the ImageUtilities class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 12, 2004
*/
public class ImageUtilities {
/**
* Show debug output?
*/
//private static final boolean VERBOSE = false;
/**
* Singleton Implementation.
*/
private static ImageUtilities instance;
/**
* Creates a new <code>ImageUtilities</code> instance.
*/
private ImageUtilities() { }
/**
* Singleton Implementation.
*
* @return an <code>ImageUtilities</code> value
*/
public static ImageUtilities getInstance() {
if (instance == null) { instance = new ImageUtilities(); }
return instance;
}
/**
* Returns the color of the pixel at the specified coordinates of the image.
*
* @param image a <code>BufferedImage</code> value
* @param x an <code>int</code> value
* @param y an <code>int</code> value
* @return a <code>Color</code> value
*/
public Color getPixel(final BufferedImage image, int x, int y) {
/*
Raster imageRaster = image.getRaster();
ColorSpace imageSpace = image.getColorModel().getColorSpace();
float[] components = new float[imageSpace.getNumComponents() + 1];
Color result = null;
if (VERBOSE) {
System.out.println("ImageUtilities.getPixel - getting @ (" + x + ", "
+ y + ")");
System.out.flush();
}
components = imageRaster.getPixel(x, y, components);
if (VERBOSE) {
System.out.println("Color components:");
for (int i = 0; i < components.length; i++) {
System.out.println("\tc[" + i + "] = " + components[i]);
}
}
result = new Color((int) components[0],
(int) components[1],
(int) components[2]);
return result;
*/
return new Color(image.getRGB(x, y));
}
/**
* Creates an invisible cursor.
*
* @return a <code>Cursor</code> value
*/
public Cursor createBlankCursor() {
int[] pixels = new int[16 * 16];
Image img = Toolkit.getDefaultToolkit().createImage(
new MemoryImageSource(16, 16, pixels, 0, 16));
Cursor result = Toolkit.getDefaultToolkit()
.createCustomCursor(img, new Point(0, 0), "invisible_cursor");
return result;
}
}

View File

@@ -0,0 +1,173 @@
package edu.gatech.cs2335.lemmings.graphics;
//import java.util.List;
//import java.util.Vector;
//import java.net.URL;
import java.awt.Point;
//import java.awt.Color;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.Raster;
//import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
/**
* Class ImprovedTileSet: This tileset is essentially an improved
* version of the regular tileset.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 01, 2004) - Created the ImprovedTileSet class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 01, 2004
*/
public class ImprovedTileSet extends TileSet {
/**
* The list of tiles in this tileset, accelerated and ready to
* render.
*/
private BufferedImage[] tiles;
/**
* Describe constant <code>VERBOSE</code> here.
*
*/
private static final boolean VERBOSE = false;
/**
* Creates a new <code>ImprovedTileSet</code> instance.
*/
public ImprovedTileSet() {
tiles = null;
}
/**
* Describe <code>specialLoad</code> method here.
*
* @return a <code>boolean</code> value
*/
protected boolean specialLoad() {
if (VERBOSE) {
System.out.println("How can we NOT use Verbose? ");
}
//Now, create the images and stuff:
tiles = new BufferedImage[getFrameList().length];
for (int k = 0; k < tiles.length; k++) {
//Fetch raster:
Rectangle src = getFrameList()[k].getSource();
Raster data = getFramesetImage().getData(src);
tiles[k] = new BufferedImage(data.getWidth(),
data.getHeight(),
getFramesetImage().getType());
tiles[k].setData(data.createRaster(data.getSampleModel(),
data.getDataBuffer(), null));
}
return true;
}
/**
* Performs all the necessary clean-up operations.
*
* @return a <code>boolean</code> value
*/
public synchronized boolean unloadTileset() {
//Clean up the tile list if necessary:
if (tiles != null) {
tiles = null;
}
return true;
}
/**
* Returns the number of tiles in the tileset.
* @return an <code>int</code> value
*/
public synchronized int getTileCount() {
return tiles.length;
}
/**
* Returns the dimensions of the largest tile in the tileset.
*
* @return a <code>Dimension</code> value
*/
public synchronized Dimension getLargestDimension() {
Dimension result = new Dimension();
for (int i = 0; i < tiles.length; i++) {
int width = tiles[i].getWidth();
int height = tiles[i].getHeight();
if (width > result.width) {
result.width = width;
}
if (height > result.height) {
result.height = height;
}
}
return result;
}
/**
* Returns the dimensions of the tile with the specified id.
*
* @param tileNum an <code>int</code> value
* @return a <code>Dimension</code> value
*/
public synchronized Dimension getDimension(int tileNum) {
Dimension result = new Dimension();
result.width = tiles[tileNum].getWidth();
result.height = tiles[tileNum].getHeight();
return result;
}
/**
* Puts the tile with the specified number onto the graphics context
* passed in at the specified coordinates. Note that the position of
* the tile will depend on its anchor point, which will be put
* exactly at the coordinates passed in.
*
* @param destination the context to which we want to draw the tile.
* @param coordinates the coordinates at which we want to draw the tile.
* @param tileNum the number of the tile we want to draw. The tiles
* will be numbered automatically, from left to right, from top to
* bottom, when the image file is parsed.
*/
public synchronized void drawTile(Graphics destination,
Point coordinates,
int tileNum) {
if (DEBUG) {
System.out.println("TileSet.drawTile - Drawing tile " + tileNum + " at ("
+ coordinates.x + ", " + coordinates.y + ")");
System.out.flush();
}
coordinates.translate(getFrameList()[tileNum].getExtent().x,
getFrameList()[tileNum].getExtent().y);
//Render tile:
destination.drawImage(tiles[tileNum],
coordinates.x,
coordinates.y,
null);
coordinates.translate(-getFrameList()[tileNum].getExtent().x,
-getFrameList()[tileNum].getExtent().y);
}
}

View File

@@ -0,0 +1,62 @@
package edu.gatech.cs2335.lemmings.graphics;
import javax.print.attribute.EnumSyntax;
/**
* Class Looping: an enumeration for the looping mode an animated
* sprite could use.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 12, 2004) - Created the Looping class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 12, 2004
*/
public class Looping extends EnumSyntax {
/**
* Represents the animation that is "stuck" in the current frame. No
* looping takes place.
*/
public static final Looping NONE = new Looping(0);
/**
* Represents the animation that only loops once, after which it
* reverts to the NONE looping mode in the first frame.
*/
public static final Looping ONCE = new Looping(1);
/**
* Represents the animation which repeats infinitely.
*/
public static final Looping INFINITE = new Looping(2);
// /**
// * Part of the EnumSyntax implementation.
// */
// private static final String[] stringTable = {
// "None",
// "Once",
// "Infinite"};
// /**
// * Part of the EnumSyntax implementation.
// */
// private static final Looping[] enumValueTable = {
// NONE,
// ONCE,
// INFINITE
// };
/**
* Creates a new <code>Looping</code> instance.
*
* @param value an <code>int</code> value
*/
private Looping(int value) {
super(value);
}
}

View File

@@ -0,0 +1,996 @@
package edu.gatech.cs2335.lemmings.graphics;
import java.awt.Point;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.List;
import edu.gatech.cs2335.lemmings.engine.Level;
import edu.gatech.cs2335.lemmings.engine.Portal;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.PrettySprite;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.ExploderJob;
import edu.gatech.cs2335.lemmings.gui.LFancyLabel;
///////////// Zooming test Stuff
import java.awt.geom.AffineTransform;
/**
* The Lemmings Renderer
*
* Takes care of drawing stuff to screen
*/
public final class Renderer {
////////////////////////////// Zooming Test Stuff ///////////////////////////
/**
* The global Do-Zoom switch. With this turned off, everything will
* be a bit faster, and won't use any of the affine transformation
* crap.
*/
private static final boolean DO_ZOOM = true;
/**
* The list of possible zoom levels.
*/
private static final double[] ZOOMLEVELS = {
4.0, // Zoomed In [0]
2.0,
1.0, // Normal Zoom [2]
0.5,
0.25 // Zoomed Out [4]
};
/**
* This is a temporary variable to save the "normal" transformation
* matrix.
*/
private AffineTransform saveAT;
/**
* This is where the zoom transformation will be stored.
*/
private AffineTransform myTransform;
/////////////////////////////////////////////////////////////////////////////
/**
* Display debug information?
*/
public static final boolean VERBOSE = false;
/**
* Display a lot of debug information?
*/
public static final boolean DEBUG = false;
/**
* Temporary Buffer to store stuff.
*/
private BufferedImage buffer;
/**
* The bounds of the display on the frame that we will be rendering to.
*/
private Rectangle display;
/**
* Stores the map area.
*/
private Dimension mapSize;
/**
* Stores the current User View area.
*/
private Rectangle mapViewport;
/**
* Stores the current Background View Area
*/
private Rectangle backgroundViewport;
/**
* Stores the current Scaling (for use in various Resolutions/Zooms)
*/
//private Point scaleFactor;
/**
* The rate at which we are scrolling.
*/
private Point scrollRate;
/**
* Stores the current zoom
*/
private int zoomFactor;
/**
* Stores the current game level
*/
private Level level;
/**
* Just a temporary point.
*/
private Point coords = new Point();
/**
* Just a temporary point.
*/
private Point coords2 = new Point();
/**
* The coordinates of the mouse.
*/
private Point mouseCoordinates = new Point();
/**
* The cursor to denote the currently selected lemming.
*/
private AnimatedSprite cursor;
/**
* Contains a pointer to the lemming currently selected.
*/
private Lemming selectedLemming;
/**
* Now that's something new - a temporary rectangle.
*/
private Rectangle rcTemp = new Rectangle();
/**
* Should we render water?
*/
private boolean renderWater = false;
/**
* Position of water.
*/
private int waterPosition = -1;
/**
* The height of water.
*/
private int waterHeight = -1;
/**
* The width of water.
*/
private int waterWidth = -1;
/**
* Creates a new <code>Renderer</code> instance.
*
* @param l The level that we will be rendering.
* @param disp The display bounds.
*/
public Renderer(Level l, Rectangle disp) {
//////////////////////////// Zoom Stuff ///////////////////////////////////
if (DO_ZOOM) {
myTransform = new AffineTransform();
}
///////////////////////////////////////////////////////////////////////////
setLevel(l);
setDisplay(disp);
setMapSize(l.getMap().getSize());
//Set up the view anchor:
mapViewport = new Rectangle(0, 0, display.width, display.height);
//Set up the background viewport:
backgroundViewport = new Rectangle(mapViewport);
//Set up scaleFactor:
/*
scaleFactor = new Point(mapViewport.width / backgroundViewport.width,
mapViewport.height / backgroundViewport.height);
*/
setZoomFactor(ZOOMLEVELS.length / 2);
//Center viewport on the entrance point:
mapViewport.translate(l.getData().getEntrancePoint().x,
l.getData().getEntrancePoint().y);
mapViewport.translate(-disp.width / 2, -disp.height / 2);
clipAnchor();
scrollRate = new Point();
selectedLemming = null;
cursor = new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("cursor"),
Direction.NO_DIRECTION, Looping.INFINITE);
}
/**
* Updates the cursor.
*/
public void updateCursor() {
cursor.nextFrameNumber();
}
/*
* Initializes the buffer to a blank image of the specified size.
*
* @param width an <code>int</code> value
* @param height an <code>int</code> value
*/
//private void createBuffer(int width, int height) {
// buffer = ImageLoader.getInstance().createBlankImage(width, height);
//}
/**
*accessor for renderer
*
*@return Rectangle
*/
public Rectangle getMapViewPort() {
return mapViewport;
}
/**
* If the view anchor is such that it goes outside the dimensions of
* the map, clips it so it doesn't.
*/
private void clipAnchor() {
//Clip right side:
if (mapViewport.x + mapViewport.width > mapSize.width) {
mapViewport.x = mapSize.width - mapViewport.width;
}
//Clip bottom side:
if (mapViewport.y + mapViewport.height > mapSize.height) {
mapViewport.y = mapSize.height - mapViewport.height;
}
//Clip left side:
if (mapViewport.x < 0) {
mapViewport.x = 0;
}
//Clip top side:
if (mapViewport.y < 0) {
mapViewport.y = 0;
}
}
/**
* Get the value of level.
* @return value of level.
*/
public Level getLevel() {
return level;
}
/**
* Set the value of level.
* @param v Value to assign to level.
*/
public void setLevel(Level v) {
this.level = v;
buffer = level.getMap().createGameMap();
}
/**
* Get the value of display.
* @return value of display.
*/
public Rectangle getDisplay() {
return display;
}
/**
* Set the value of display.
* @param v Value to assign to display.
*/
public void setDisplay(Rectangle v) {
this.display = v;
//Initialize the back buffer:
// createBuffer(display.width, display.height);
// rerenderTerrain();
}
/**
* Get the value of mapSize.
* @return value of mapSize.
*/
public Dimension getMapSize() {
return mapSize;
}
/**
* Set the value of mapSize.
* @param v Value to assign to mapSize.
*/
protected void setMapSize(Dimension v) {
this.mapSize = v;
}
/**
* Get the value of zoomFactor.
* @return value of zoomFactor.
*/
public int getZoomFactor() {
return zoomFactor;
}
/**
* Set the value of zoomFactor.
* @param v Value to assign to zoomFactor.
*/
public void setZoomFactor(int v) {
this.zoomFactor = v;
if (DO_ZOOM) {
double z = ZOOMLEVELS[v];
//Set up affine:
myTransform.setToScale(z, z);
//Update the viewports:
zoomRectangle(mapViewport, display);
//Clippie:
clipAnchor();
}
}
/**
* Applies zooming to the rectangle.
*
* @param r a <code>Rectangle</code> value
* @param ref a <code>Rectangle</code> value
*/
private void zoomRectangle(Rectangle r, Rectangle ref) {
double z = ZOOMLEVELS[zoomFactor];
//Calculate center point of the rectangle:
int x = r.x + (r.width / 2);
int y = r.y + (r.height / 2);
//Resize rectangle:
r.width = (int) (ref.width / z);
r.height = (int) (ref.height / z);
//Recenter viewport:
r.x = x - (r.width / 2);
r.y = y - (r.height / 2);
}
/**
* Applies unzooming to the rectangle.
*
* @param r a <code>Rectangle</code> value
* @param ref a <code>Rectangle</code> value
*/
private void unzoomRectangle(Rectangle r, Rectangle ref) {
double z = ZOOMLEVELS[zoomFactor];
//Calculate center point of the rectangle:
int x = r.x + (r.width / 2);
int y = r.y + (r.height / 2);
//Resize rectangle:
r.width = (int) (ref.width * z);
r.height = (int) (ref.height * z);
//Recenter viewport:
r.x = x - (r.width / 2);
r.y = y - (r.height / 2);
}
/**
* Zooms in.
*/
public void zoomIn() {
int z = getZoomFactor() - 1;
if (z >= 0) {
setZoomFactor(z);
}
}
/**
* Zooms out.
*/
public void zoomOut() {
int z = getZoomFactor() + 1;
if (z < ZOOMLEVELS.length) {
setZoomFactor(z);
}
}
/**
* Scroll the map by offset in the x-y directions.
* @param offset the amount to offset
*/
public void scroll(Point offset) {
scrollRate.x = offset.x;
scrollRate.y = offset.y;
}
/**
* Describe <code>getScrollRate</code> method here.
*
* @return a <code>Point</code> value
*/
public Point getScrollRate() {
return new Point(scrollRate);
}
/**
* Describe <code>getMouseCoordinates</code> method here.
*
* @return a <code>Point</code> value
*/
public Point getMouseCoordinates() {
return new Point(mouseCoordinates);
}
/**
* Describe <code>setMouseCoordinates</code> method here.
*
* @param coord a <code>Point</code> value
*/
public synchronized void setMouseCoordinates(Point coord) {
mouseCoordinates.x = coord.x;
mouseCoordinates.y = coord.y;
if (VERBOSE) {
System.out.println("Renderer: New Mouse Coordinates: ( " + coord.x
+ ", " + coord.y + ")");
System.out.flush();
}
//mouseToWorld(mouseCoordinates);
}
/**
* Converts mouse coordinates to world coordinates.
*
* @param c a <code>Point</code> value
* @return a <code>Point</code> value
*/
public Point mouseToWorld(Point c) {
//Find offset from viewport:
c.translate(-display.x, -display.y);
if (DO_ZOOM) {
//Scale coordinates:
c.x = (int) (c.x / ZOOMLEVELS[zoomFactor]);
c.y = (int) (c.y / ZOOMLEVELS[zoomFactor]);
}
//Find offset from world:
c.translate(mapViewport.x, mapViewport.y);
return c;
}
/**
* Converts world coordinates to mouse coordinates.
*
* @param c a <code>Point</code> value
* @return a <code>Point</code> value
*/
public Point worldToMouse(Point c) {
//Find offset from viewport:
c.translate(-mapViewport.x, -mapViewport.y);
if (DO_ZOOM) {
//Scale coordinates:
c.x = (int) (c.x * ZOOMLEVELS[zoomFactor]);
c.y = (int) (c.y * ZOOMLEVELS[zoomFactor]);
}
//Find offset from screen:
c.translate(display.x, display.y);
return c;
}
/*
* Convert the mouse click point to a gameWorld point
*
* @param c the coordinates to translate
* @return translated coordinates
*/
//private Point screenToWorld(Point c) {
// //Find offset from viewport:
// c.translate(-display.x, -display.y);
//
// //Find offset from world:
// c.translate(mapViewport.x, mapViewport.y);
//
// return c;
//}
/**
* Convert the world coordinates to world point
*
* @param c the coordinates to translate
* @return translated coordinates
*/
private Point worldToScreen(Point c) {
//Find offset from viewport:
c.translate(-mapViewport.x, -mapViewport.y);
//Find offset from screen:
c.translate(display.x, display.y);
return c;
}
/**
* Describe <code>renderExploder</code> method here.
*
* @param l a <code>Lemming</code> value
* @param target a <code>Graphics2D</code> value
*/
private synchronized void renderExploder(Lemming l, Graphics2D target) {
//Render the exploder's numbers here.
coords.x = l.getPosition().getX();
coords.y = l.getPosition().getY();
coords.x -= 5;
coords.y -= 40;
worldToScreen(coords);
String str = Integer
.toString(((ExploderJob) l.getOccupation()).getTimeLeftOnEarth() / 5);
target.setColor(Color.red);
target.setFont(LFancyLabel.DEFAULT_FONT);
target.drawString(str, coords.x, coords.y);
}
/**
* Update a given Graphics Frame
*
* @param target the frame to update
* @return success
*/
public synchronized boolean updateFrame(Graphics target) {
Graphics2D g = (Graphics2D) target;
if (DEBUG) {
System.out.println("Renderer: Rendering ...");
System.out.flush();
}
//Do scrolling:
if (scrollRate.x != 0 || scrollRate.y != 0) {
if (DO_ZOOM) {
mapViewport.translate((int) (scrollRate.x / ZOOMLEVELS[zoomFactor]),
(int) (scrollRate.y / ZOOMLEVELS[zoomFactor]));
} else {
mapViewport.translate(scrollRate.x, scrollRate.y);
}
//Make sure we don't scroll out of the map:
clipAnchor();
}
//Set up clipping:
g.setClip(display);
//Find coordinates:
coords.x = 0;
coords.y = 0;
worldToScreen(coords);
//Render background:
g.drawImage(level.getMap().getBackground(), null, display.x, display.y);
////////////////////////// Zoom ///////////////////////////////////////////
saveAT = g.getTransform();
g.setTransform(myTransform);
///////////////////////////////////////////////////////////////////////////
//Blit Terrain:
g.drawImage(buffer, null, coords.x, coords.y);
//Render portals:
List ports = level.getLevelPortals();
for (int i = 0; i < ports.size(); i++) {
Portal p = (Portal) ports.get(i);
if (DEBUG) {
System.out.println("Renderer: Rendering portal...");
System.out.flush();
}
//Render portal:
coords.x = p.getPhysics().getPosition().getX();
coords.y = p.getPhysics().getPosition().getY();
//Get screen coordinates of portal:
worldToScreen(coords);
p.getAnimation().renderSprite(g, coords);
}
//Render Lemmings:
List lems = level.getActiveLemmings();
selectedLemming = null;
for (int i = 0; i < lems.size(); i++) {
Lemming l = (Lemming) lems.get(i);
//Get world coordinates of lemming:
coords.x = l.getPosition().getX();
coords.y = l.getPosition().getY();
if (!mapViewport.contains(coords)) {
//Lemming is outside the screen. Don't bother.
continue;
}
//Get screen coordinates of lemming:
worldToScreen(coords);
if (selectedLemming == null) {
checkLemmingSelected(l, g);
if (selectedLemming != null) {
cursor.renderSprite(g, coords);
}
}
//Render lemming:
l.getOccupation().getAnimation().renderSprite(g, coords);
if (l.getOccupation() instanceof ExploderJob) {
renderExploder(l, g);
}
}
//Render pretty sprites:
List pl = level.getPrettySprites();
for (int i = 0; i < pl.size(); i++) {
PrettySprite ps = (PrettySprite) pl.get(i);
//Get world coordinates of sprite:
coords.x = ps.getPosition().getX();
coords.y = ps.getPosition().getY();
if (!mapViewport.contains(coords)) {
//Sprite is outside the screen. Don't bother.
continue;
}
//Get screen coordinates of sprite:
worldToScreen(coords);
//Render sprite:
ps.getAnimation().renderSprite(g, coords);
}
//Render water if necessary:
if (renderWater) {
renderWater(g);
}
////////////////////////// Zoom ///////////////////////////////////////////
g.setTransform(saveAT);
///////////////////////////////////////////////////////////////////////////
//Unclip:
g.setClip(null);
return true;
}
/**
* Describe <code>renderWater</code> method here.
*
* @param g a <code>Graphics2D</code> value
*/
private void renderWater(Graphics2D g) {
coords.x = 0;
coords.y = waterPosition;
if (coords.y <= mapViewport.y + mapViewport.height) {
//Some water's on screen:
int x = mapViewport.x + mapViewport.width;
int y = mapViewport.y + mapViewport.height;
int yInc = 2 * waterHeight / 3;
int xInit = -waterWidth / 2;
int counter = 0;
int mapWidth = level.getMap().getMap().getWidth();
int mapHeight = level.getMap().getMap().getHeight();
AnimatedSprite was = level.getMap().getWaterAnimation();
//The y coordinate now contains where we should put top layer of water.
for (coords.y = waterPosition;
coords.y <= y && coords.y <= mapHeight;
coords.y += yInc) {
//The above is iterating through rows of water.
coords.x = mapViewport.x;
if (counter % 2 == 1) {
coords.x += xInit;
}
for (; coords.x <= x && coords.x <= mapWidth; coords.x += waterWidth) {
//This is iterating through columns of water.
coords2.x = coords.x;
coords2.y = coords.y;
worldToScreen(coords2);
was.renderSprite(g, coords2);
}
counter++;
}
}
}
/**
* Once the level has been set, calculates how water needs to be drawn.
*/
public void calculateWaterLevel() {
if (VERBOSE) {
System.out.println("Renderer: Calculating Water Level...");
System.out.flush();
}
int wl = level.getData().getWaterLevel();
if (wl < 0) {
renderWater = false;
return;
}
if (VERBOSE) {
System.out.println("\tGot water height - " + wl);
System.out.flush();
}
renderWater = true;
//Calculate top-most world position of water.
waterPosition = level.getMap().getMap().getHeight() - wl;
if (VERBOSE) {
System.out.println("\tGot water position - " + waterPosition);
System.out.flush();
}
//Calculate water dimensions:
waterHeight
= level.getMap().getWaterAnimation().getAnimation().getExtent(0).height
+ level.getMap().getWaterAnimation().getAnimation().getExtent(0).y;
if (VERBOSE) {
System.out.println("\tGot unit height - " + waterHeight);
System.out.flush();
}
waterWidth
= level.getMap().getWaterAnimation().getAnimation().getExtent(0).width
+ level.getMap().getWaterAnimation().getAnimation().getExtent(0).x;
if (VERBOSE) {
System.out.println("\tGot unit width - " + waterWidth);
System.out.flush();
}
}
/**
* Checks if the lemming is selected.
*
* @param l a <code>Lemming</code> value
* @param g a <code>Point</code> value
*/
private void checkLemmingSelected(Lemming l, Graphics2D g) {
Rectangle r = null;
r = l.getOccupation().getAnimation().getAnimation()
.getExtent(l.getOccupation().getAnimation().getCurrentFrame());
rcTemp.x = r.x;
rcTemp.y = r.y;
rcTemp.width = r.width;
rcTemp.height = r.height;
coords2.x = l.getPosition().getX();
coords2.y = l.getPosition().getY();
worldToMouse(coords2);
rcTemp.translate(coords2.x, coords2.y);
unzoomRectangle(rcTemp, rcTemp);
if (DEBUG) {
System.out.println("Lemming:");
System.out.println("\tWorld: " + l.getPosition());
System.out.println("\tScreen: " + coords2);
System.out.println("\tUnzoomed: " + rcTemp);
System.out.println("");
System.out.flush();
g.setColor(Color.red);
g.setTransform(saveAT);
g.fill(rcTemp);
g.setTransform(myTransform);
}
if (rcTemp.contains(mouseCoordinates)) {
selectedLemming = l;
}
}
/**
* Describe <code>getSelectedLemming</code> method here.
*
* @return a <code>Lemming</code> value
*/
public Lemming getSelectedLemming() {
return selectedLemming;
}
/**
* Rerenders the terrain.
*/
public synchronized void rerenderTerrain() {
}
/**
* Returns a string representation of the renderer.
*
* @return a <code>String</code> value
*/
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Renderer:").append("\n");
sb.append(level.getData().toString());
sb.append("Map Size: ").append(mapSize).append("\n");
sb.append("Rendering to: ").append(display).append("\n");
sb.append("Map view Anchor: ").append(mapViewport).append("\n");
sb.append("Zoom: ").append(zoomFactor).append("\n");
return sb.toString();
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
//javax.swing.JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
final javax.swing.JFrame frame
= new javax.swing
.JFrame("Renderer Test",
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration());
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setSize(800, 600);
frame.setVisible(true);
//Initialize double buffering:
//frame.createBufferStrategy(2);
//Create and set up the content pane.
Level l = edu.gatech.cs2335.lemmings.file.LevelReader.getInstance()
.loadLevel("test_level");
final Renderer r = new Renderer(l, new Rectangle(0, 20, 400, 400));
javax.swing.JPanel newContentPane
= new javax.swing.JPanel(new java.awt.BorderLayout());
//newContentPane.add(tst, java.awt.BorderLayout.CENTER);
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Initialize double buffering:
frame.createBufferStrategy(2);
final java.awt.image.BufferStrategy strategy = frame.getBufferStrategy();
new Thread(new Runnable() {
public void run() {
long lastFrame = System.currentTimeMillis();
while (true) {
int width = frame.getWidth();
int height = frame.getHeight();
//Get the graphics context from buffer strategy:
Graphics g = strategy.getDrawGraphics();
//Do the drawing:
g.setColor(java.awt.Color.gray);
g.fillRect(0, 0, width, height);
width /= 2;
height /= 2;
r.updateFrame(g);
//Flip buffer:
strategy.show();
//Cap the frame rate:
lastFrame += 20; //Frame rate
try {
Thread.sleep(Math.max(0,
lastFrame - System.currentTimeMillis()));
} catch (Exception e) {
continue;
}
}
}
}
).start();
}
/**
*
*@param args args
*/
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

View File

@@ -0,0 +1,645 @@
package edu.gatech.cs2335.lemmings.graphics;
import java.util.List;
import java.util.Vector;
//import java.net.URL;
import java.awt.Point;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
/**
* The tileset class simplifies loading bitmap files that contain
* several tiles. It is a fairly flexible class, but it does impose
* certain restrictions on the way the file is organized. First of
* all, there needs to be a set of control pixels in the upper-right
* corner of the image. The control pixels start at the coordinate
* (width-1, 0) and go down from there in the following order:
* transparency pixel, empty-region pixel, outside anchor pixel,
* used-region pixel, inside anchor pixel; five in all. The
* transparency pixel is used for color-keying, and all the pixels of
* that color in the image will be made transparent. The outside and
* inside anchor pixels do the same thing currently, and are used to
* denote the image anchor. When the tile is blitted using the
* putTile method, the anchor has the coordinates passed in to the
* method, and the rest of the image is drawn relatively to it. In
* other words, suppose we blit to rectangular tiles using the same
* target coordinates. One of the tiles has its anchor in the
* top-left corner, and the other - in the center. Then the center of
* the latter tile will coincide with the top-left corner of the
* former tile. The empty-region and used-region pixels are used to
* denote where the useful areas of the tile are, and which ones can
* be discarded. The reason for that is that the tiles in the tileset
* file have to be arranged in a table. In other words, all tiles in
* one column have to be the same width, and all the tiles in one row
* have to be the same height. That is not to say, however, that the
* width and the height have to be the equal, or that the tiles in
* different rows have to have the same height, and in different
* columns - the same width. Suppose a game has only two tiles. We
* can arrange them in a column. One of the tiles is square, and the
* other - a very thin rectangle. Then, we could mark the whole first
* tile as used, and for the second tile - only the space that is
* actually used up. Since we arranged the tiles in a column, the
* widths of the tiles have to be the same. However, we can mark the
* unused areas of the thin tile with the unused-area pixel color,
* and they will be discarded when blitting.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 11, 2004) - Created the TileSet class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 11, 2004
*/
public class TileSet {
/**
* Show debug output?
*/
protected static final boolean DEBUG = false;
/**
* Show lots of debug output?
*/
protected static final boolean VERBOSE = false;
/**
* The list of informations pertaining to the frames in this set.
*/
private FrameInformation[] frameList;
/**
* The name of the file we want to load the image from, in case it
* needs to be reloaded somehow.
*/
private String fileName;
/**
* The image, where the whole tileset is stored.
*/
private BufferedImage framesetImage;
/**
* Creates a new <code>TileSet</code> instance.
*/
public TileSet() {
frameList = null;
fileName = null;
framesetImage = null;
}
/**
* Loads the tileset from a file. Returns true upon success or false
* upon failure.
*
* @param name a <code>String</code> value
* @return a <code>boolean</code> value
*/
public boolean loadTileset(String name) {
//Dimensions of the image
int width = 0;
int height = 0;
//Number of tiles horizontally and vertically
// int xCount = 0;
// int yCount = 0;
//Lists of coordinates for the tile bounds
List horizontalTileBounds = new Vector();
List verticalTileBounds = new Vector();
//Color control
Color transparentColor = null;
Color boundColor = null;
Color anchorColor = null;
Color insideColor = null;
Color insideAnchorColor = null;
Color testColor = null;
//Image info
Raster tilesetRaster = null;
ColorSpace tilesetSpace = null;
//Miscellaneous temporaries
int x = 0;
int y = 0;
int row = 0;
int col = 0;
int currentTile = 0;
// boolean found = false;
//First, clean up if we already have something loaded:
if (!unloadTileset()) {
//Could not clean up...
return false;
}
//Save the name of the file:
fileName = name;
//Load the image from the file:
framesetImage = ImageLoader.getInstance().loadLocalImage(name);
if (framesetImage == null) {
System.err.println("TileSet.loadTileset - could not load image.");
System.err.flush();
return false;
}
//Get image info:
tilesetRaster = framesetImage.getRaster();
tilesetSpace = framesetImage.getColorModel().getColorSpace();
//Get image dimensions:
width = tilesetRaster.getWidth();
height = tilesetRaster.getHeight();
//Define color control variables:
transparentColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 0);
boundColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 1);
anchorColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 2);
insideColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 3);
insideAnchorColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 4);
testColor = Color.black;
//Find the coordinates of the vertical tile bounds:
for (x = 0; x < width; x++) {
testColor = ImageUtilities.getInstance().getPixel(framesetImage, x, 0);
if (testColor.equals(transparentColor)) {
//Add the coordinate to the list:
if (VERBOSE) {
System.out.println("TileSet: Adding x coordinate - " + x);
System.out.flush();
}
verticalTileBounds.add(new Integer(x));
}
}
//Find the coordinates of the horizontal tile bounds:
for (y = 0; y < height; y++) {
testColor = ImageUtilities.getInstance().getPixel(framesetImage, 0, y);
if (testColor.equals(transparentColor)) {
//Add the coordinate to the list:
if (VERBOSE) {
System.out.println("TileSet: Adding y coordinate - " + y);
System.out.flush();
}
horizontalTileBounds.add(new Integer(y));
}
}
//Allocate the list of frame infos:
frameList = new FrameInformation[(verticalTileBounds.size() - 1)
* (horizontalTileBounds.size() - 1)];
//Scan the tiles in:
for (row = 0; row < horizontalTileBounds.size() - 1; row++) {
for (col = 0; col < verticalTileBounds.size() - 1; col++) {
currentTile = col + row * (verticalTileBounds.size() - 1);
if (DEBUG) {
System.out.println("TileSet: Processing tile (" + col + ", "
+ row + ") - #" + currentTile);
System.out.flush();
}
frameList[currentTile] = new FrameInformation();
//Determine the anchor point:
frameList[currentTile].getAnchor().x
= findFirstOccurrence(((Integer) verticalTileBounds.get(col))
.intValue(),
((Integer) verticalTileBounds.get(col + 1))
.intValue(),
((Integer) horizontalTileBounds.get(row))
.intValue(),
anchorColor, insideAnchorColor, false);
frameList[currentTile].getAnchor().y
= findFirstOccurrence(((Integer) horizontalTileBounds.get(row))
.intValue(),
((Integer) horizontalTileBounds.get(row + 1))
.intValue(),
((Integer) verticalTileBounds.get(col))
.intValue(),
anchorColor, insideAnchorColor, true);
//Find the first tile pixel:
frameList[currentTile].getSource().x
= findFirstOccurrence(((Integer) verticalTileBounds.get(col))
.intValue(),
((Integer) verticalTileBounds.get(col + 1))
.intValue(),
((Integer) horizontalTileBounds.get(row))
.intValue(),
insideColor, insideAnchorColor, false);
frameList[currentTile].getSource().y
= findFirstOccurrence(((Integer) horizontalTileBounds.get(row))
.intValue(),
((Integer) horizontalTileBounds.get(row + 1))
.intValue(),
((Integer) verticalTileBounds.get(col))
.intValue(),
insideColor, insideAnchorColor, true);
if (frameList[currentTile].getSource().x == 0) {
//Could not find. Use default:
frameList[currentTile].getSource().x
= ((Integer) verticalTileBounds.get(col)).intValue() + 1;
}
if (frameList[currentTile].getSource().y == 0) {
//Could not find. Use default:
frameList[currentTile].getSource().y
= ((Integer) horizontalTileBounds.get(row)).intValue() + 1;
}
//Find the last tile pixel:
frameList[currentTile].getSource().width
= findLastOccurrence(((Integer) verticalTileBounds.get(col))
.intValue(),
((Integer) verticalTileBounds.get(col + 1))
.intValue(),
((Integer) horizontalTileBounds.get(row))
.intValue(),
insideColor, insideAnchorColor, false)
- frameList[currentTile].getSource().x + 1;
frameList[currentTile].getSource().height
= findLastOccurrence(((Integer) horizontalTileBounds.get(row))
.intValue(),
((Integer) horizontalTileBounds.get(row + 1))
.intValue(),
((Integer) verticalTileBounds.get(col))
.intValue(),
insideColor, insideAnchorColor, true)
- frameList[currentTile].getSource().y + 1;
if (frameList[currentTile].getSource().width <= 0) {
//Could not find. Use default:
frameList[currentTile].getSource().width
= ((Integer) verticalTileBounds.get(col + 1)).intValue()
- frameList[currentTile].getSource().x;
}
if (frameList[currentTile].getSource().height <= 0) {
//Could not find. Use default:
frameList[currentTile].getSource().height
= ((Integer) horizontalTileBounds.get(row + 1)).intValue()
- frameList[currentTile].getSource().y;
}
//Calculate tile extent:
frameList[currentTile]
.setExtent(new Rectangle(frameList[currentTile].getSource()));
frameList[currentTile].getExtent()
.translate(-frameList[currentTile].getAnchor().x,
-frameList[currentTile].getAnchor().y);
}
}
specialLoad();
return true;
}
/**
* Describe <code>specialLoad</code> method here.
*
* @return a <code>boolean</code> value
*/
protected boolean specialLoad() {
return true;
}
/**
* Describe <code>getFrameList</code> method here.
*
* @return a <code>FrameInformation[]</code> value
*/
protected FrameInformation[] getFrameList() {
return frameList;
}
/**
* Describe <code>getFramesetImage</code> method here.
*
* @return a <code>BufferedImage</code> value
*/
protected BufferedImage getFramesetImage() {
return framesetImage;
}
/**
* Finds the coordinate of the first occurrence of one of the test colors.
*
* @param lowBound an <code>int</code> value
* @param highBound an <code>int</code> value
* @param coordinate an <code>int</code> value
* @param c1 a <code>Color</code> value
* @param c2 a <code>Color</code> value
* @param vertical a <code>boolean</code> value
* @return an <code>int</code> value
*/
protected int findFirstOccurrence(int lowBound, int highBound,
int coordinate,
Color c1, Color c2, boolean vertical) {
Color testColor = null;
int x = 0;
int y = 0;
for (int i = lowBound + 1; i < highBound; i++) {
if (vertical) {
x = coordinate;
y = i;
} else {
x = i;
y = coordinate;
}
testColor = ImageUtilities.getInstance().getPixel(framesetImage, x, y);
if (testColor.equals(c1) || testColor.equals(c2)) {
return i;
}
}
return 0;
}
/**
* Finds the coordinate of the last occurrence of one of the test colors.
*
* @param lowBound an <code>int</code> value
* @param highBound an <code>int</code> value
* @param coordinate an <code>int</code> value
* @param c1 a <code>Color</code> value
* @param c2 a <code>Color</code> value
* @param vertical a <code>boolean</code> value
* @return an <code>int</code> value
*/
protected int findLastOccurrence(int lowBound, int highBound,
int coordinate,
Color c1, Color c2, boolean vertical) {
Color testColor = null;
int x = 0;
int y = 0;
for (int i = highBound - 1; i > lowBound; i--) {
if (vertical) {
x = coordinate;
y = i;
} else {
x = i;
y = coordinate;
}
testColor = ImageUtilities.getInstance().getPixel(framesetImage, x, y);
if (testColor.equals(c1) || testColor.equals(c2)) {
return i;
}
}
return 0;
}
/**
* If the surface was lost, reloads it, but does not reparse the file.
*
* @return a <code>boolean</code> value
*/
public boolean reloadTileset() {
//Load the image from the file:
framesetImage = ImageLoader.getInstance().loadLocalImage(getFileName());
if (framesetImage == null) {
System.err.println("TileSet.reloadTileset - could not load image");
System.err.flush();
return false;
}
return true;
}
/**
* Performs all the necessary clean-up operations.
*
* @return a <code>boolean</code> value
*/
public boolean unloadTileset() {
//Clean up the tile list if necessary:
if (frameList != null) {
frameList = null;
fileName = null;
framesetImage = null;
}
return true;
}
/**
* Returns the number of tiles in the tileset.
* @return an <code>int</code> value
*/
public int getTileCount() {
return frameList.length;
}
/**
* Returns the image on which the tileset resides.
* @return a <code>BufferedImage</code> value
*/
public BufferedImage getImage() {
return framesetImage;
}
/**
* Returns the name of the file containing the tileset in a string.
* @return a <code>String</code> value
*/
public String getFileName() {
return fileName;
}
/**
* Returns the dimensions of the largest tile in the tileset.
*
* @return a <code>Dimension</code> value
*/
public Dimension getLargestDimension() {
Dimension result = new Dimension();
for (int i = 0; i < frameList.length; i++) {
int width = frameList[i].getSource().width;
int height = frameList[i].getSource().height;
if (width > result.width) {
result.width = width;
}
if (height > result.height) {
result.height = height;
}
}
return result;
}
/**
* Returns the dimensions of the tile with the specified id.
*
* @param tileNum an <code>int</code> value
* @return a <code>Dimension</code> value
*/
public Dimension getDimension(int tileNum) {
Dimension result = new Dimension();
result.width = frameList[tileNum].getSource().width;
result.height = frameList[tileNum].getSource().height;
return result;
}
/**
* Returns the extent of the tile with the specified id.
*
* @param tileNum an <code>int</code> value
* @return a <code>Rectangle</code> value
*/
public Rectangle getExtent(int tileNum) {
Rectangle result = new Rectangle(frameList[tileNum].getExtent());
return result;
}
/**
* Puts the tile with the specified number onto the graphics context
* passed in at the specified coordinates. Note that the position of
* the tile will depend on its anchor point, which will be put
* exactly at the coordinates passed in.
*
* @param destination the context to which we want to draw the tile.
* @param coordinates the coordinates at which we want to draw the tile.
* @param tileNum the number of the tile we want to draw. The tiles
* will be numbered automatically, from left to right, from top to
* bottom, when the image file is parsed.
*/
public void drawTile(Graphics destination, Point coordinates, int tileNum) {
if (DEBUG) {
System.out.println("TileSet.drawTile - Drawing tile " + tileNum + " at ("
+ coordinates.x + ", " + coordinates.y + ")");
System.out.flush();
}
frameList[tileNum].getExtent().translate(coordinates.x, coordinates.y);
//Fetch raster:
Rectangle src = frameList[tileNum].getSource();
if (VERBOSE) {
System.out.println("TileSet.drawTile: src rectangle - " + src);
System.out.flush();
}
Raster data = framesetImage.getData(src);
if (VERBOSE) {
System.out.println("TileSet.drawTile: data - " + data);
System.out.flush();
}
BufferedImage temp = new BufferedImage(data.getWidth(),
data.getHeight(),
framesetImage.getType());
temp.setData(data.createRaster(data.getSampleModel(),
data.getDataBuffer(), null));
if (VERBOSE) {
System.out.println("TileSet.drawTile: temp - " + temp);
System.out.flush();
}
//Render tile:
destination.drawImage(temp,
frameList[tileNum].getExtent().x,
frameList[tileNum].getExtent().y,
null);
frameList[tileNum].getExtent().translate(-coordinates.x, -coordinates.y);
}
/**
* Information structure for one tile in the tileset.
*/
protected class FrameInformation {
/**
* This rectangle represents the location of the frame in the
* master frame set.
*/
private Rectangle source;
/**
* The extent of the frame.
*/
private Rectangle extent;
/**
* The anchor point of the frame. That is, when we say that we
* want to render the frame at certain coordinates, the point with
* the coordinates stored here, relative to the top-left corner of
* the frame, will be drawn at the specified coordinates.
*/
private Point anchor;
/**
* Creates a new <code>FrameInformation</code>
* instance. Initializes all of the data members to contain all
* default values (zeros).
*/
public FrameInformation() {
source = new Rectangle();
extent = new Rectangle();
anchor = new Point();
}
/**
* Get the value of source.
* @return value of source.
*/
public Rectangle getSource() {
return source;
}
/**
* Set the value of source.
* @param v Value to assign to source.
*/
public void setSource(Rectangle v) {
this.source = v;
}
/**
* Get the value of extent.
* @return value of extent.
*/
public Rectangle getExtent() {
return extent;
}
/**
* Set the value of extent.
* @param v Value to assign to extent.
*/
public void setExtent(Rectangle v) {
this.extent = v;
}
/**
* Get the value of anchor.
* @return value of anchor.
*/
public Point getAnchor() {
return anchor;
}
/**
* Set the value of anchor.
* @param v Value to assign to anchor.
*/
public void setAnchor(Point v) {
this.anchor = v;
}
}
}

View File

@@ -0,0 +1,218 @@
package edu.gatech.cs2335.lemmings.graphics;
import java.util.Map;
import java.util.HashMap;
import java.util.Properties;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* Class TileSetManager: This guy will allow us to manage tilesets in
* such a fashion that every tileset is only loaded once.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 12, 2004) - Created the TileSetManager class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 12, 2004
*/
public class TileSetManager {
/**
* Show debug output?
*/
private static final boolean VERBOSE = false;
/**
* The name of the file, where the mappings between the resource
* names and their paths reside.
*/
public static final String RESOURCE_MAP_FILE = "resources.txt";
/**
* Singleton implementation.
*/
private static TileSetManager instance;
/**
* This map will contain the tilesets currently loaded.
*/
private Map tileSets;
/**
* This contains the mappings between the paths and the ids of the
* resources.
*/
private Map paths;
/**
* Creates a new <code>TileSetManager</code> instance. Since this
* class is a songleton, there is no need for the constructor to be
* public.
*/
private TileSetManager() {
tileSets = new HashMap();
paths = new HashMap();
loadPaths();
}
/**
* Singleton Implementation.
*
* @return a <code>TileSetManager</code> value
*/
public static synchronized TileSetManager getInstance() {
if (instance == null) {
instance = new TileSetManager();
}
return instance;
}
/**
* This function will load the mapping between the paths and ids of
* the resources.
*/
private void loadPaths() {
FileInputStream fis = null;
Properties props = null;
paths.clear();
//Open up the resource properties file:
try {
fis = new FileInputStream(RESOURCE_MAP_FILE);
} catch (IOException ioe) {
System.err.println("TileSetManager: Could not open settings file...");
System.err.println("\tIOException: " + ioe.getMessage());
System.err.flush();
return;
}
try {
props = new Properties();
props.load(fis);
} catch (IOException ioe) {
System.err.println("TileSetManager: Could not "
+ "read the properties file...");
System.err.println("\tIOException: " + ioe.getMessage());
System.err.flush();
return;
}
if (VERBOSE) {
System.out.println("TileSetManager: Loaded the following properties");
System.out.println(props);
System.out.flush();
}
paths.putAll(props);
}
/**
* Loads the resource if needed.
*
* @param name a <code>String</code> value
* @param path a <code>String</code> value
* @return a <code>boolean</code> value
*/
public boolean loadResource(String name, String path) {
String newName = name;
String p = path;
if (newName == null) {
//Check if the path is valid:
if (p == null) { return false; }
File f = new File(p);
if (!f.exists()) { return false; }
newName = f.getName();
//Check if a resource with the same name already exists:
if (this.paths.containsKey(newName)) { return false; }
//If we are still here, need to add the new entry to the paths:
this.paths.put(newName, f.getPath());
p = (String) this.paths.get(newName);
} else {
if (this.paths.containsKey(newName)) {
if (p != null) {
//Need to update the entry in the table:
File f = new File(p);
if (!f.exists()) { return false; }
this.paths.remove(newName);
this.paths.put(newName, f.getPath());
//Need to also check if we have already loaded the item into
//the table:
if (this.tileSets.containsKey(newName)) {
this.tileSets.remove(newName);
}
p = (String) this.paths.get(newName);
} else {
p = "images/" + (String) this.paths.get(newName);
this.paths.put(name, p);
}
} else {
if (p == null) {
return false;
} else {
File f = new File(p);
if (!f.exists()) { return false; }
this.paths.put(newName, f.getPath());
p = (String) this.paths.get(newName);
}
}
}
//If we are still here, the paths and everything are good. Time to
//actually load the resource:
TileSet set = new ImprovedTileSet();
if (!set.loadTileset(p)) { return false; }
this.tileSets.put(newName, set);
return true;
}
/**
* Returns true if the tileset is loaded and false if it is not.
*
* @param name a <code>String</code> value
* @return a <code>boolean</code> value
*/
public boolean tileSetLoaded(String name) {
return this.tileSets.containsKey(name);
}
/**
* Returns the tileset with the specified name. Loads it if necessary.
*
* @param name a <code>String</code> value
* @return a <code>TileSet</code> value
*/
public TileSet getTileSet(String name) {
if (!tileSetLoaded(name)) {
if (!loadResource(name, null)) {
return null;
}
}
return (TileSet) this.tileSets.get(name);
}
}

View File

@@ -0,0 +1,224 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
import java.awt.Graphics;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.graphics.Renderer;
//import edu.gatech.cs2335.lemmings.graphics.Looping;
//import edu.gatech.cs2335.lemmings.graphics.Direction;
//import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
//import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* Class GamePlayPanel: This class will display the game itself.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 28, 2004) - Created the GamePlayPanel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 28, 2004
*/
public class GamePlayPanel extends LLeafComponent
implements IMouseMotionable, IMouseWheelable, IClickable {
/**
* If the cursor is within this many pixels of the edge of the map,
* we will scroll.
*/
public static final int SCROLL_OFFSET = 25;
/**
* The renderer used to draw stuff.
*/
private Renderer renderer;
/**
* Just a temporary point.
*/
private Point coords = new Point();
/**
* Creates a new <code>GamePlayPanel</code> instance.
*@param r r
*/
public GamePlayPanel(Renderer r) {
renderer = r;
setBounds(r.getDisplay());
renderer.calculateWaterLevel();
}
/**
*Returns a renderer
*@return renderer
*/
public Renderer getRenderer() {
return renderer;
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
GamePlayPanel gpp = (GamePlayPanel) component;
gpp.renderer = this.renderer;
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected synchronized boolean paint(Graphics g) {
boolean result = true;
//Render field:
result = result && renderer.updateFrame(g);
//Render scroll fields:
if (LApplication.VERBOSE) {
java.awt.Rectangle t = new java.awt.Rectangle(getBounds().x,
getBounds().y,
getBounds().width,
SCROLL_OFFSET);
java.awt.Rectangle l = new java.awt.Rectangle(getBounds().x,
getBounds().y,
SCROLL_OFFSET,
getBounds().height);
java.awt.Rectangle b
= new java.awt
.Rectangle(getBounds().x,
getBounds().y + getBounds().height - SCROLL_OFFSET,
getBounds().width, SCROLL_OFFSET);
java.awt.Rectangle r
= new java.awt
.Rectangle(getBounds().x + getBounds().width - SCROLL_OFFSET,
getBounds().y, SCROLL_OFFSET, getBounds().height);
java.awt.Graphics2D gr = (java.awt.Graphics2D) g;
gr.setColor(new java.awt.Color(1.0f, 0.0f, 0.0f, 0.5f));
gr.fill(t);
gr.fill(l);
gr.fill(b);
gr.fill(r);
}
//Render cursor over selected lemming:
/*
if (selectedLemming != null) {
coords.x = selectedLemming.getPosition().getX();
coords.y = selectedLemming.getPosition().getY();
renderer.worldToScreen(coords);
result = result && cursor.renderSprite(g, coords);
}
*/
return true;
}
/**
* Allows the implementing class to be told when the mouse has moved.
*
* @param oldX an <code>int</code> value
* @param oldY an <code>int</code> value
* @param newX an <code>int</code> value
* @param newY an <code>int</code> value
*/
public synchronized void processMouseMotion(int oldX, int oldY,
int newX, int newY) {
//PMD FIX
int nX = newX;
int nY = newY;
//See if we are selecting any lemmings:
coords.x = nX;
coords.y = nY;
renderer.setMouseCoordinates(coords);
//selectedLemming = renderer.getLevel().findLemmingAt(coords);
//Process scrolling:
coords.x = 0;
coords.y = 0;
nX -= getBounds().x;
nY -= getBounds().y;
//Check horizontal:
if (nX < SCROLL_OFFSET) {
//Scroll left:
coords.x = nX - SCROLL_OFFSET;
//The fact that coords.x is negative here means we will be
//scrolling left, not right.
} else {
int rdiff = getBounds().width - nX;
if (rdiff < SCROLL_OFFSET) {
//Scroll right:
coords.x = SCROLL_OFFSET - rdiff;
}
}
//Check vertical:
if (nY < SCROLL_OFFSET) {
//Scroll up:
coords.y = nY - SCROLL_OFFSET;
} else {
int bdiff = getBounds().height - nY;
if (bdiff < SCROLL_OFFSET) {
//Scroll down:
coords.y = SCROLL_OFFSET - bdiff;
}
}
if (LApplication.VERBOSE) {
System.out.println("Scrolling: (" + coords.x + ", " + coords.y + ")");
System.out.flush();
}
//Apply scrolling:
renderer.scroll(coords);
}
/**
* Registers the click of a mouse button at the specified coordinates.
*
* @param button an <code>int</code> value
* @param coord a <code>Point</code> value
*/
public void registerMouseClick(int button, Point coord) {
Lemming s = renderer.getSelectedLemming();
if (s != null) {
renderer.getLevel().assignJobToLemming(s);
}
}
/**
* Processes the mouse wheel rotation.
*
* @param numClicks The number of clicks the wheel was rotated. A
* negative value means the wheel was rotated away from the user,
* and positive means it was rotated towards the user.
*/
public void processMouseWheel(int numClicks) {
if (numClicks < 0) {
renderer.zoomIn();
} else if (numClicks > 0) {
renderer.zoomOut();
}
}
}

View File

@@ -0,0 +1,27 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
/**
* Interface IClickable: This interface allows us to say that a certain
* component is clickable by the mouse.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 14, 2004) - Created the IClickable interface
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 14, 2004
*/
public interface IClickable {
/**
* Registers the click of a mouse button at the specified coordinates.
*
* @param button an <code>int</code> value
* @param coords a <code>Point</code> value
*/
public void registerMouseClick(int button, Point coords);
}

View File

@@ -0,0 +1,30 @@
package edu.gatech.cs2335.lemmings.gui;
//import java.awt.Point;
/**
* Interface IMouseMotionable: Allows the implementing classes to know
* where the mouse is.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 10, 2004) - Created the IMouseMotionable interface
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 10, 2004
*/
public interface IMouseMotionable {
/**
* Allows the implementing class to be told when the mouse has moved.
*
* @param oldX an <code>int</code> value
* @param oldY an <code>int</code> value
* @param newX an <code>int</code> value
* @param newY an <code>int</code> value
*/
public void processMouseMotion(int oldX, int oldY, int newX, int newY);
}

View File

@@ -0,0 +1,26 @@
package edu.gatech.cs2335.lemmings.gui;
/**
* Interface IMouseWheelable: Should be implemented by all components that
* want to listen to mouse wheel rotations.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the IMouseWheelable interface
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 11, 2004
*/
public interface IMouseWheelable {
/**
* Processes the mouse wheel rotation.
*
* @param numClicks The number of clicks the wheel was rotated. A
* negative value means the wheel was rotated away from the user,
* and positive means it was rotated towards the user.
*/
public void processMouseWheel(int numClicks);
}

View File

@@ -0,0 +1,27 @@
package edu.gatech.cs2335.lemmings.gui;
/**
* Interface ITypable: This interface should be implemented by all
* components that would like to be notified of the keyboard events.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 14, 2004) - Created the ITypable interface
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 14, 2004
*/
public interface ITypable {
/**
* Processes the key type event.
*
* @param key The key that was typed.
* @param modifiers The modifiers that accompanied the
* key-type. This is a collection of ORed flags, like SHIFT, ALT,
* etc.
*/
public void processKeyTyped(char key, int modifiers);
}

View File

@@ -0,0 +1,271 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Graphics;
//import java.awt.Dimension;
//import java.awt.event.ActionEvent;
//import java.awt.event.ActionListener;
//import java.util.List;
//import java.util.Vector;
import edu.gatech.cs2335.lemmings.engine.Level;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJob;
import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobFactory;
import edu.gatech.cs2335.lemmings.graphics.Looping;
/**
* Class JobButton: This button will display the animation for the job
* specified, and the number of that type of job available.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 07, 2004) - Created the JobButton class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 07, 2004
*/
public final class JobButton extends LAbstractButton {
/**
* The color to use for unselected buttons.
*/
public static final Color UNSELECTED_COLOR
= new Color(0.5f, 0.5f, 0.5f, 0.9f);
/**
* The color to use for selected buttons.
*/
public static final Color SELECTED_COLOR
= new Color(0.5f, 0.5f, 0.5f, 0.5f);
/**
* The level associated with this button.
*/
private Level level;
/**
* The job associated with this button.
*/
private LemmingJob job;
/**
* The label that will contain the text for the number of jobs.
*/
private LLabel lblNumJobs;
/**
* Set to true if this button is selected, or false if it is not.
*/
private boolean selected;
/**
* A temporary point for the purposes of fast rendering.
*/
private Point ptTemp = new Point();
/**
* Creates a new <code>JobButton</code> instance.
*
* @param l a <code>Level</code> value
* @param id a <code>String</code> value
*/
public JobButton(Level l, String id) {
lblNumJobs = new LLabel();
lblNumJobs.setText("");
lblNumJobs.setShown(true);
setLevel(l);
setLemmingJob(id);
setSelected(false);
}
/**
* Returns the level associated with this button.
*
* @return a <code>Level</code> value
*/
public synchronized Level getLevel() {
return level;
}
/**
* Sets up a new level for this button.
*
* @param l a <code>Level</code> value
*/
public synchronized void setLevel(Level l) {
level = l;
}
/**
* Returns the job associated with this button.
*
* @return a <code>LemmingJob</code> value
*/
public synchronized LemmingJob getLemmingJob() {
return job;
}
/**
* Sets up a new job for this button.
*
* @param id a <code>String</code> value
*/
public synchronized void setLemmingJob(String id) {
if (id == null || id.equals("")) { return; }
//Set up animation
job = LemmingJobFactory.getInstance().makeJob(id);
if (job == null) {
return;
}
job.setOwner(new Lemming());
//Set up the new action command
setActionCommand(id);
//Set up number of jobs available
lblNumJobs.setText(Integer.toString(((GamePlayState) GameEngine
.getInstance()
.getCurrentState())
.getLevel().getJobsRemaining(id)));
updateDimensions();
}
/**
* Returns whether or not the button is currently selected.
*
* @return a <code>boolean</code> value
*/
public boolean isSelected() {
return selected;
}
/**
* Describe <code>setSelected</code> method here.
*
* @param v a <code>boolean</code> value
*/
public void setSelected(boolean v) {
selected = v;
if (v) {
//We should animate the button:
job.getAnimation().setLoopingMode(Looping.INFINITE);
} else {
//We should not animate the button:
job.getAnimation().setLoopingMode(Looping.NONE);
}
}
/**
* Toggles the selection on the button.
*/
public void toggleSelected() {
if (isSelected()) {
setSelected(false);
} else {
setSelected(true);
}
}
/**
* Updates the button.
*/
protected void updateButton() {
job.getAnimation().nextFrameNumber();
//Set up number of jobs available
lblNumJobs.setText(Integer.toString(((GamePlayState) GameEngine
.getInstance()
.getCurrentState())
.getLevel()
.getJobsRemaining(job.getId())));
}
/**
* Updates the bounds of the button.
*/
protected void updateDimensions() {
int x = getBounds().x;
int y = getBounds().y;
Rectangle e = job.getAnimation().getAnimation()
.getExtent(job.getAnimation().getCurrentFrame());
e = new Rectangle(e);
lblNumJobs.setPosition(new Point(x + e.width,
y - lblNumJobs.getBounds().height - 5));
e.width += 30;
e.height += 5;
setBounds(e);
getBounds().translate(x, y);
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
//Render background thingie:
if (isSelected()) {
g.setColor(UNSELECTED_COLOR);
} else {
g.setColor(SELECTED_COLOR);
}
((java.awt.Graphics2D) g).fill(getBounds());
//Render the image:
Rectangle e = job.getAnimation().getAnimation()
.getExtent(job.getAnimation().getCurrentFrame());
ptTemp.x = getBounds().x - e.x;
ptTemp.y = getBounds().y - e.y;
job.getAnimation().renderSprite(g, ptTemp);
//Render text:
lblNumJobs.makeDirty();
lblNumJobs.renderAll(g);
return true;
}
/**
* Describe <code>toString</code> method here.
*
* @return a <code>String</code> value
*/
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Job Button: \"").append(job.getId()).append("\" - {")
.append(" numJobs: ").append(level.getJobsRemaining(job.getId()))
.append(" bounds: ").append(getBounds());
return sb.toString();
}
}

View File

@@ -0,0 +1,311 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
//import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Graphics;
//import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Vector;
import java.util.Iterator;
import edu.gatech.cs2335.lemmings.engine.Level;
//import edu.gatech.cs2335.lemmings.engine.Lemming;
//import edu.gatech.cs2335.lemmings.engine.GameEngine;
//import edu.gatech.cs2335.lemmings.engine.GamePlayState;
//import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJob;
//import edu.gatech.cs2335.lemmings.engine.lemmingjob.LemmingJobFactory;
//import edu.gatech.cs2335.lemmings.graphics.Looping;
/**
* Class JobContainer: The container for lemming job buttons. It will
* display them and align them nicely and stuff.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 10, 2004) - Created the JobContainer class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 10, 2004
*/
public final class JobContainer extends LContainer implements ActionListener {
/**
* Tells the container that the buttons should be arranged on the
* x-axis.
*/
public static final int X_AXIS = 0;
/**
* Tells the container that the buttons should be arranged on the
* y-axis.
*/
public static final int Y_AXIS = 1;
/**
* The number of pixels to put between the buttons.
*/
public static final int SPACING = 5;
/**
* The level that the container is associated with.
*/
private Level level;
/**
* How should the buttons be arranged?
*/
private int arrangement;
/**
* The event.
*/
private JobSelectionEvent event;
/**
* The components to notify of job selection.
*/
private List listeners;
/**
* Creates a new <code>JobContainer</code> instance.
* @param l a <code>Level</code> value
*/
public JobContainer(Level l) {
setLevel(l);
setArrangement(Y_AXIS);
event = new JobSelectionEvent("");
listeners = new Vector();
}
/**
* Adds an JobSelectionListener to the button.
*
* @param l the JobSelectionListener to be added.
*/
public void addJobSelectionListener(JobSelectionListener l) {
if (!listeners.contains(l)) {
listeners.add(l);
}
}
/**
* Removes an JobSelectionListener from the button. If the listener is the
* currently set JobSelection for the button, then the JobSelection is set to
* null.
*
* @param l the listener to be removed.
*/
public void removeJobSelectionListener(JobSelectionListener l) {
listeners.remove(l);
}
/**
* Returns the listeners for this button.
*
* @return a <code>List</code> value
*/
protected List getJobSelectionListeners() {
return listeners;
}
/**
* Sets up the list of action listeners.
*
* @param ll a <code>List</code> value
*/
protected void setJobSelectionListeners(List ll) {
listeners = new Vector(ll);
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance is lazily
* created using the event parameter.
*
* @param e the JobSelectionEvent object.
*/
protected void fireJobSelected(JobSelectionEvent e) {
Iterator i = listeners.iterator();
while (i.hasNext()) {
((JobSelectionListener) i.next()).jobSelected(e);
}
}
/**
* Describe <code>getLevel</code> method here.
*
* @return a <code>Level</code> value
*/
public Level getLevel() {
return level;
}
/**
* Describe <code>setLevel</code> method here.
*
* @param l a <code>Level</code> value
*/
public void setLevel(Level l) {
level = l;
}
/**
* Describe <code>getArrangement</code> method here.
*
* @return an <code>int</code> value
*/
public int getArrangement() {
return arrangement;
}
/**
* Describe <code>setArrangment</code> method here.
*
* @param a an <code>int</code> value
*/
public void setArrangement(int a) {
if (a == X_AXIS || a == Y_AXIS) {
arrangement = a;
rearrangeButtons();
} else {
throw new IllegalArgumentException("Bad value for button arrangement");
}
}
/**
* Adds a job button for the job with the specified ID.
*
* @param jobId a <code>String</code> value
*/
public void addJobButton(String jobId) {
//Create the new job button:
JobButton butt = new JobButton(level, jobId);
butt.setShown(true);
butt.addActionListener(this);
List children = getChildren();
if (children.size() == 0) {
//This is the first button:
butt.setPosition(new Point(getBounds().x, getBounds().y));
} else {
//This is not the first button:
Point newPos = new Point();
Rectangle e = butt.getLemmingJob().getAnimation().getAnimation()
.getExtent(butt.getLemmingJob().getAnimation().getCurrentFrame());
LComponent prevChild = ((LComponent) children.get(children.size() - 1));
newPos.x = prevChild.getBounds().x - e.x;
newPos.y = prevChild.getBounds().y - e.y;
if (arrangement == X_AXIS) {
//Move to the right:
newPos.x += prevChild.getBounds().width + SPACING;
} else {
//Move down:
newPos.y += prevChild.getBounds().height + SPACING;
}
butt.setPosition(newPos);
}
addChild(butt);
if (children.size() == 1) {
setSelectedButton(jobId);
}
}
/**
* Rearranges all buttons.
*/
protected void rearrangeButtons() {
List children = new Vector(getChildren());
clear();
for (int i = 0; i < children.size(); i++) {
addJobButton(((JobButton) children.get(i)).getLemmingJob().getId());
}
}
/**
* Updates the buttons.
*/
public void updateButtons() {
List children = getChildren();
for (int i = 0; i < children.size(); i++) {
JobButton butt = (JobButton) children.get(i);
butt.updateButton();
}
}
/**
* Selects the button with the spercified id.
*
* @param id a <code>String</code> value
*/
public void setSelectedButton(String id) {
List children = getChildren();
//Check if we need to do job selection any way:
if (level.getJobsRemaining(id) <= 0) { return; }
for (int i = 0; i < children.size(); i++) {
JobButton butt = (JobButton) children.get(i);
if (!id.equals(butt.getActionCommand())) {
if (butt.isSelected()) {
butt.setSelected(false);
}
} else {
if (!butt.isSelected()) {
butt.setSelected(true);
}
}
}
//Tell the level a new job has been selected:
level.setActiveJobId(id);
}
/**
* Describe <code>actionPerformed</code> method here.
*
* @param e an <code>ActionEvent</code> value
*/
public void actionPerformed(ActionEvent e) {
//Update the selection.
setSelectedButton(e.getActionCommand());
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
//Nothing (?) to paint here.
return true;
}
}

View File

@@ -0,0 +1,50 @@
package edu.gatech.cs2335.lemmings.gui;
import java.util.EventObject;
/**
* Class JobSelectionEvent: Fired whenever a new job has been selected.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 10, 2004) - Created the JobSelectionEvent class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 10, 2004
*/
public final class JobSelectionEvent extends EventObject {
/**
* The id of the newly selected job.
*/
private String jobId;
/**
* Creates a new <code>JobSelectionEvent</code> instance.
* @param id a <code>String</code> value
*/
public JobSelectionEvent(String id) {
super(id);
setJobId(id);
}
/**
*
*@param r r
*/
public void setJobId(String r) {
jobId = r;
}
/**
*
*@return String
*/
public String getJobId() {
return jobId;
}
}

View File

@@ -0,0 +1,27 @@
package edu.gatech.cs2335.lemmings.gui;
import java.util.EventListener;
/**
* Interface JobSelectionListener: The interface that should be
* implemented by all classes that want to listen to job selection
* events.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 10, 2004) - Created the JobSelectionListener interface
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 10, 2004
*/
public interface JobSelectionListener extends EventListener {
/**
* Invoked when a job has been selected.
*
* @param e The event associated with this invokation.
*/
public void jobSelected(JobSelectionEvent e);
}

View File

@@ -0,0 +1,138 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import java.util.Iterator;
/**
* Class LAbstractButton: The abstract button class.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 29, 2004) - Created the LAbstractButton class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 29, 2004
*/
public abstract class LAbstractButton extends LLeafComponent
implements IClickable {
/**
* The list of listeners that are listening to this button.
*/
private List listeners;
/**
* The action command associated with this button.
*/
private String actionCommand;
/**
* Creates a new <code>LAbstractButton</code> instance.
*/
public LAbstractButton() {
super();
listeners = new Vector();
actionCommand = "";
}
/**
* Adds an ActionListener to the button.
*
* @param l the ActionListener to be added.
*/
public void addActionListener(ActionListener l) {
if (!listeners.contains(l)) {
listeners.add(l);
}
}
/**
* Removes an ActionListener from the button. If the listener is the
* currently set Action for the button, then the Action is set to
* null.
*
* @param l the listener to be removed.
*/
public void removeActionListener(ActionListener l) {
listeners.remove(l);
}
/**
* Returns the listeners for this button.
*
* @return a <code>List</code> value
*/
protected List getActionListeners() {
return listeners;
}
/**
* Sets up the list of action listeners.
*
* @param ll a <code>List</code> value
*/
protected void setActionListeners(List ll) {
listeners = new Vector(ll);
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance is lazily
* created using the event parameter.
*
* @param e the ActionEvent object.
*/
protected void fireActionPerformed(ActionEvent e) {
Iterator i = listeners.iterator();
while (i.hasNext()) {
((ActionListener) i.next()).actionPerformed(e);
}
}
/**
* Returns the action command for this button.
*
* @return a <code>String</code> value
*/
public String getActionCommand() {
return (this.actionCommand);
}
/**
* Sets a new action command for this button.
*
* @param v a <code>String</code> value
*/
public void setActionCommand(String v) {
this.actionCommand = v;
}
/**
* Registers the click of a mouse button at the specified coordinates.
*
* @param button an <code>int</code> value
* @param coords a <code>Point</code> value
*/
public void registerMouseClick(int button, Point coords) {
ActionEvent event = new ActionEvent(this, ((int) new Date().getTime()),
getActionCommand());
if (LApplication.VERBOSE) {
System.out.println("Button " + getClass().getName() + " clicked.");
System.out.flush();
}
this.fireActionPerformed(event);
}
}

View File

@@ -0,0 +1,474 @@
package edu.gatech.cs2335.lemmings.gui;
//import java.util.Timer;
//import java.util.TimerTask;
//import java.awt.Canvas;
import java.awt.Point;
//import java.awt.Color;
//import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Dimension;
//import java.awt.Rectangle;
//import java.awt.Graphics2D;
//import java.awt.AlphaComposite;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsConfiguration;
import java.awt.image.BufferStrategy;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.MouseMotionAdapter;
//import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
/**
* Class LApplication: This is the main application class. When
* instantiated, it will create a new window with nothing in it. The
* window's content pane is what we will be drawing in and every
* frame, we will call our own components' renderall method to render
* the stuff on screen.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 13, 2004) - Created the LApplication class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 13, 2004
*/
public class LApplication {
/**
* Show debug output?
*/
public static final boolean VERBOSE = false;
/**
* Show lots of debug output?
*/
public static final boolean DEBUG = false;
/**
* The default title of the application window.
*/
public static final String DEFAULT_TITLE = "LApplication";
/**
* The default location of the top-left corner of the application.
*/
public static final Point DEFAULT_LOCATION = new Point(50, 50);
/**
* The default size of the application window.
*/
public static final Dimension DEFAULT_SIZE = new Dimension(800, 600);
/**
* The frame rate cap.
*/
public static final float MAXIMUM_FRAME_RATE = 60;
/**
* This is the delay between frames.
*/
public static final long FRAME_DELAY = (long) (1000.0f / MAXIMUM_FRAME_RATE);
/**
* If we are not rendering, then check this many millis if we should go.
*/
public static final long NO_GO_DELAY = 1000;
/**
* If we are lagging by this much, skip a frame.
*/
public static final int MAX_TARDINESS = 1000;
/**
* The buffer strategy associated with this application.
*/
private BufferStrategy strategy;
/**
* The graphics configuration used by this application.
*/
private GraphicsConfiguration gConfig;
/**
* The root pane associated with this application.
*/
private LRootPane rootPane;
/**
* Specifies whether we are currently rendering or not.
*/
private boolean rendererGoing;
/**
* The frame that everything happens in.
*/
private JFrame frame;
/*
* The task that will be used for rendering.
*/
//private TimerTask timerTask;
/**
* The obligatory temporary point to avoid dynamic allocation during
* runtime.
*/
private Point ptTemp = new Point();
/*
* The obligatory temporary point to avoid dynamic allocation during
* runtime.
*/
//private Point ptTemp2 = new Point();
/**
* The cursor to be used in this application. If null, a default
* cursor is used.
*/
//private LCursor cursor;
/**
* Determines whether or not we should render the cursor.
*/
//private boolean renderCursor;
/**
* Creates a new <code>LApplication</code> instance.
*
* @param title The title to give to the window. If null is passed
* in, a default vaue will be provided.
* @param location The location of the application window. If null
* is passed in, a default vaue will be provided.
* @param size The size of the application window. If null is passed
* in, a default vaue will be provided.
*/
public LApplication(String title, Point location, Dimension size) {
rendererGoing = false;
//renderCursor = false;
if (title == null) {
title = DEFAULT_TITLE;
}
if (location == null) {
location = DEFAULT_LOCATION;
}
if (size == null) {
size = DEFAULT_SIZE;
}
//Use default graphics configuration:
gConfig = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
//Create frame:
frame = new JFrame(title, gConfig);
// canvas = new Canvas(gConfig);
// frame.setUndecorated(true);
/*
{
public void paint(Graphics g) {
//Java is saying we should redraw the frame... Okay.
repaint();
}
};
*/
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
ptTemp.x = e.getX();
ptTemp.y = e.getY();
getRootPane().dispatchMouseMotionEvent(ptTemp, ptTemp);
}
});
frame.addMouseWheelListener(new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
ptTemp.x = e.getX();
ptTemp.y = e.getY();
getRootPane().dispatchMouseWheelEvent(ptTemp, e.getWheelRotation());
}
});
frame.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
ptTemp.x = e.getX();
ptTemp.y = e.getY();
getRootPane().dispatchMouseClickEvent(e.getButton(), ptTemp);
}
});
frame.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
getRootPane().dispatchKeyboardEvent(e.getKeyChar(),
e.getModifiers());
}
});
//Display the window:
frame.setLocation(location);
frame.setSize(size);
frame.setResizable(false);
frame.setVisible(true);
//Initialize double buffering:
frame.createBufferStrategy(2);
strategy = frame.getBufferStrategy();
//Initialize the root pane:
rootPane = new LRootPane();
rootPane.setShown(true);
//Set up the renderer thread:
new Thread(new Runnable() {
public void run() {
long lastFrame = System.currentTimeMillis();
// boolean render = true;
while (true) {
if (!rendererGoing || !frame.isVisible()) {
//Sleep a bit, then continue:
try {
lastFrame += NO_GO_DELAY;
Thread.sleep(Math.max(0,
lastFrame - System.currentTimeMillis()));
} catch (Exception e) {
continue;
} finally {
lastFrame = System.currentTimeMillis();
}
}
//Do the rendering:
repaint();
// render = false;
//Cap frame rate:
lastFrame += FRAME_DELAY;
try {
if (DEBUG) {
System.out.println("Want to sleep "
+ (lastFrame - System.currentTimeMillis())
+ " millis");
System.out.flush();
}
Thread.sleep(Math.max(0,
lastFrame - System.currentTimeMillis()));
} catch (Exception e) {
continue;
} finally {
lastFrame = System.currentTimeMillis();
}
}
}
}).start();
// cursor = null;
}
/**
* Returns the graphics configuration used by the application.
* @return a <code>GraphicsConfiguration</code> value
*/
public GraphicsConfiguration getConfiguration() {
return gConfig;
}
/**
* Returns the root pane used for this application.
* @return a <code>LRootPane</code> value
*/
public LRootPane getRootPane() {
return rootPane;
}
/**
* Sets up the new root pane:
*
* @param pane a <code>LRootPane</code> value
*/
public synchronized void setRootPane(LRootPane pane) {
// rootPane.clear();
pane.copy(rootPane);
}
/**
* Repaints everything that needs repainting.
*/
public synchronized void repaint() {
if (DEBUG) {
System.out.println("LApplication: Rendering...");
System.out.flush();
}
//Get the graphics context from buffer strategy:
Graphics g = strategy.getDrawGraphics();
//Clear all:
//FIXME: Hack. Need to make it so only things that need
// rerendering are rerendered.
//g.setColor(java.awt.Color.gray);
//g.fillRect(0, 0, frame.getWidth(), frame.getHeight());
//Do the drawing:
rootPane.renderAll(g);
/*
if (renderCursor) {
//Render the cursor:
cursor.renderCursor(g);
}
*/
//Flip the buffer:
strategy.show();
}
/**
* Starts the renderer loop.
*/
public synchronized void startRenderer() {
if (rendererGoing) {
return;
}
rendererGoing = true;
/*
if (timerTask != null) {
//Already started task
return;
}
//Set up renderer timer:
timerTask = new TimerTask() {
public void run() {
if (System.currentTimeMillis() - scheduledExecutionTime()
>= MAX_TARDINESS) {
return; // Too late; skip this execution.
}
if (!rendererGoing) {
return;
}
if (!frame.isVisible()) {
//No need to redraw:
return;
}
// Perform the task
repaint();
}
};
new Timer().scheduleAtFixedRate(timerTask,
0,
(int) (1000.0f / MAXIMUM_FRAME_RATE));
*/
}
/**
* Stops the renderer.
*/
public synchronized void stopRenderer() {
rendererGoing = false;
}
// /**
// * Fades screen to black in the specified time.
// *
// * @param time The number of millis to fade.
// */
// public synchronized void fade(long time) {
// stopRenderer();
// getRootPane().clear();
// //We want 60 steps a second. So:
// long numSteps = (time * 6) / 100;
// long delay = time / numSteps;
// float increment = 1.0f / numSteps;
// float current = 0.0f;
// Graphics2D g2D = (Graphics2D) strategy.getDrawGraphics();
// Rectangle rect = new Rectangle(0,0,frame.getWidth(),frame.getHeight());
// long last = System.currentTimeMillis();
// if (VERBOSE) {
// System.out.println("LApplication: Set up fading... ");
// System.out.println("\tTime: " + time);
// System.out.println("\tSteps: " + numSteps);
// System.out.println("\tDelay: " + delay);
// System.out.println("\tIncrement: " + increment);
// System.out.flush();
// }
// g2D.setColor(Color.black);
// for (int i = 0; i < numSteps; i++) {
// g2D.setComposite(AlphaComposite
// .getInstance(AlphaComposite.XOR, current));
// g2D.fill(rect);
// current += increment;
// last += delay;
// try {
// Thread.sleep(Math.max(0, last - System.currentTimeMillis()));
// } catch (Exception e) {
// System.out.println("");
// } finally {
// last = System.currentTimeMillis();
// }
// //Flip the buffer:
// strategy.show();
// }
// startRenderer();
// getRootPane().makeDirty();
// }
// /**
// * Returns the cursor currently used, or null if no cursor is used.
// *
// * @return a <code>LCursor</code> value
// */
// public LCursor getCursor() {
// return cursor;
// }
// /**
// * Sets the new cursor up.
// *
// * @param c a <code>LCursor</code> value
// */
// public synchronized void setCursor(LCursor c) {
// if (c == null) {
// cursor = null;
// renderCursor = false;
// //Use default cursor:
// frame.setCursor(Cursor.getDefaultCursor());
// } else {
// cursor = c;
// renderCursor = true;
// //Use custom cursor:
// frame.setCursor(ImageUtilities.getInstance().createBlankCursor());
// }
// }
}

View File

@@ -0,0 +1,188 @@
package edu.gatech.cs2335.lemmings.gui;
//import java.awt.Font;
import java.awt.Color;
import java.awt.Point;
import java.awt.Graphics;
//import java.awt.Dimension;
import java.awt.Rectangle;
//import java.awt.FontMetrics;
//import java.awt.GraphicsEnvironment;
//import java.awt.event.ActionEvent;
//import java.awt.event.ActionListener;
//import java.util.Date;
//import java.util.List;
//import java.util.Vector;
//import java.util.Iterator;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
/**
* Class LButton: This is the basic class for a button.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 14, 2004) - Created the LButton class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 14, 2004
*/
public class LButton extends LAbstractButton {
/**
* The text to display on this button.
*/
private String text;
/**
* The image to display on this button.
*/
private AnimatedSprite image;
/**
* Creates a new <code>LButton</code> instance.
*/
public LButton() {
super();
text = "LButton";
image = null;
}
/**
* Returns the text displayed on this button.
*
* @return a <code>String</code> value
*/
public String getText() {
return (this.text);
}
/**
* Sets the new value of the text on this button.
*
* @param v a <code>String</code> value
*/
public void setText(String v) {
this.text = v;
setActionCommand(text);
image = null;
updateDimensions();
}
/**
* Returns the image displayed on this button.
*
* @return an <code>AnimatedSprite</code> value
*/
public AnimatedSprite getImage() {
return image;
}
/**
* Sets up a new value for the image of this button.
*
* @param newImage an <code>AnimatedSprite</code> value
*/
public void setImage(AnimatedSprite newImage) {
image = newImage;
setActionCommand(newImage.getAnimation().getFileName());
text = null;
updateDimensions();
}
/**
* Describe <code>updateDimensions</code> method here.
*
*/
protected void updateDimensions() {
if (text != null) {
/*
//We are updating due to changes in text:
FontMetrics metrics = new FontMetrics(LFancyLabel.DEFAULT_FONT);
setSize(metrics.charsWidth(text.toCharArray(), 0,
text.length()), metrics.getHeight());
*/
//Hack. Must make the size be actually dependent on text...
setSize(100, 100);
} else if (image != null) {
//We are updating due to image:
int x = getBounds().x;
int y = getBounds().y;
Rectangle e = image.getAnimation().getExtent(image.getCurrentFrame());
if (LApplication.VERBOSE) {
System.out.println("LButton: Coords - (" + x + ", " + y + ")");
System.out.println(" Extent - " + e);
System.out.flush();
}
setBounds(e);
getBounds().translate(x, y);
} else {
//Both text and image are null... Bad.
throw new NullPointerException("Both text and image are null.");
}
makeDirty();
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
LButton target = (LButton) component;
target.setActionListeners(getActionListeners());
target.setActionCommand(this.getActionCommand());
target.text = this.getText();
target.image = this.getImage();
target.updateDimensions();
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
Rectangle bounds = this.getBounds();
if (text != null) {
//Render text button...
g.setColor(Color.black);
g.setFont(LFancyLabel.DEFAULT_FONT);
//Render border first:
g.draw3DRect(bounds.x - 5, bounds.y - 5,
bounds.width + 5, bounds.height + 5, true);
//Render text:
g.drawString(getText(), bounds.x, bounds.y);
return true;
} else if (image != null) {
//Render image button...
Rectangle e = image.getAnimation().getExtent(image.getCurrentFrame());
return image.renderSprite(g, new Point(bounds.x - e.x, bounds.y - e.y));
} else {
//No text, no image... :/
throw new NullPointerException("Both text and image are null.");
}
}
}

View File

@@ -0,0 +1,102 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Color;
/**
* Class LColoredComponent: An abstract parent for the colored components.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 13, 2004) - Created the LColoredComponent class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 13, 2004
*/
public abstract class LColoredComponent extends LLeafComponent {
/**
* The default foreground color.
*/
public static final Color DEFAULT_FOREGROUND_COLOR = Color.black;
/**
* The default background color.
*/
public static final Color DEFAULT_BACKGROUND_COLOR = Color.white;
/**
* The foreground color associated with this component.
*/
private Color foreground;
/**
* The background color associated with this component.
*/
private Color background;
/**
* Creates a new <code>LColoredComponent</code> instance.
*/
public LColoredComponent() {
super();
setBackground(DEFAULT_BACKGROUND_COLOR);
setForeground(DEFAULT_FOREGROUND_COLOR);
}
/**
* Returns the foreground associated with this component.
*
* @return a <code>Color</code> value
*/
public final Color getForeground() {
return foreground; //new Color(foreground.getRGB());
}
/**
* Associates the new foreground with this colored component.
*
* @param c a <code>Color</code> value
*/
public final void setForeground(Color c) {
setForeground(c.getRGB());
}
/**
* Associates the new foreground with this colored component.
*
* @param rgb an <code>int</code> value
*/
public final void setForeground(int rgb) {
this.foreground = new Color(rgb, true);
}
/**
* Returns the background associated with this component.
*
* @return a <code>Color</code> value
*/
public final Color getBackground() {
return background; // new Color(background.getRGB());
}
/**
* Associates the new background with this colored component.
*
* @param c a <code>Color</code> value
*/
public final void setBackground(Color c) {
setBackground(c.getRGB());
}
/**
* Associates the new background with this colored component.
*
* @param rgb an <code>int</code> value
*/
public final void setBackground(int rgb) {
this.background = new Color(rgb, true);
}
}

View File

@@ -0,0 +1,289 @@
package edu.gatech.cs2335.lemmings.gui;
//import java.util.List;
//import java.util.Vector;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Graphics;
/**
* The base component class, from which all of the more specific
* components, such as buttons, will be derived.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public abstract class LComponent {
/**
* The parent component of this component. If this is the root
* panel, then it will have no parent.
*/
private LContainer parent;
/**
* The rectangle defining the position and the dimensions of the
* window in virtual GUI coordinates
*/
private Rectangle position;
/**
* Determines whether the component is hidden or visible.
*/
private boolean isVisible;
/**
* Indicates that the component is dirty.
*/
private boolean isDirty;
/**
* Creates a new <code>LComponent</code> instance.
*
*/
public LComponent() {
parent = null;
position = new Rectangle();
isVisible = false;
isDirty = true;
}
/**
* Performs all of the initialization necessary for the
* component. Returns true if the initialization went through
* successfully and false if something went wrong.
*
* @return a <code>boolean</code> value
*/
public boolean initialize() {
return true;
}
/**
* Performs all the necessary operations to clean up after the
* component. Does *not* destroy the children of the component,
* however.
*
* @return a <code>boolean</code> value
*/
public boolean cleanUp() {
return true;
}
/**
* Returns a pointer to the parent component of this one if there is
* one, or NULL if this component has no parent or is the root
* window.
*
* @return a <code>LComponent</code> value
*/
public final LContainer getParent() {
return parent;
}
/**
* Sets a new parent for this component.
*
* @param c a <code>LContainer</code> value
*/
public final void setParent(LContainer c) {
parent = c;
}
/**
* Returns true if the component is visible, and false if it is not.
*
* @return a <code>boolean</code> value
*/
public final boolean isShown() {
return isVisible;
}
/**
* Sets the visibility of the component to the value passed in.
*
* @param value a <code>boolean</code> value
*/
public final void setShown(boolean value) {
isVisible = value;
}
/**
* Returns true if the component is to be rerendered during the next frame.
*@return boolean
*/
public final boolean isComponentDirty() {
return isDirty;
}
/**
* Sets the component's dirty value to the one passed in.
*@param value v
*/
protected final void setDirty(boolean value) {
isDirty = value;
}
/**
* Returns true if the component has input focus, and false if it does not.
*
* @return a <code>boolean</code> value
*/
public final boolean isActive() {
if (getParent() == null) {
//I am the root and therefore active by defult.
return true;
}
if (!isShown()) {
//I am hidden. Could not be active.
return false;
}
if (!getParent().isActive()) {
//The parent is inactive, therefore neither am I:
return false;
}
if (this == getParent().getChildren()
.get(getParent().getChildren().size() - 1)) {
//I am the last child in the parent's list, and therefore the top-most:
return true;
}
//If all else fails...
return false;
}
/**
* Brings the current component to the top of the z-order. (It's
* children will still remain on top of it, however.)
*
*/
public final void bringToTop() {
//Gotta save the parent pointer because when we call Remove, it
//will set my parent to NULL.
LContainer p = getParent();
if (p == null) {
return;
}
//Bring parent to top in case it is not on top already:
p.bringToTop();
//This basically moves the element to the end of the list of
//children of the parent. That means that it will be rendered
//last, and thus will be on top of all other children of its
//parent.
p.removeChild(this);
p.addChild(this);
}
/**
* Returns the rectangle denoting the bounds of the component.
*
* @return a <code>Rectangle</code> value
*/
public final Rectangle getBounds() {
return this.position;
}
/**
* Sets the new bounds of the component. No error checking is
* performed beyond that the size of the rectangle should be
* meaningful.
*@param newBounds n
*/
public final void setBounds(Rectangle newBounds) {
this.position = newBounds;
}
/**
* Sets the location of the top-left corner of the component to the
* desired coordinates.
*
* @param p a <code>Point</code> value
*/
public final void setPosition(Point p) {
p.translate(-position.x, -position.y);
move(p);
p.translate(position.x, position.y);
updateDimensions();
}
/**
* Sets the size of the component.
*
* @param d a <code>Dimension</code> value
*/
public final void setSize(Dimension d) {
if (d.width < 0 || d.height < 0) {
return;
}
setSize(d.width, d.height);
updateDimensions();
}
/**
* Describe <code>setSize</code> method here.
*
* @param width an <code>int</code> value
* @param height an <code>int</code> value
*/
protected final void setSize(int width, int height) {
this.position.width = width;
this.position.height = height;
}
/**
* Updates the dimensions of the component. This must *NOT* under
* any circumstances call the method setPosition() or setSize()!!!
*/
protected void updateDimensions() { }
/**
* Marks the component dirty.
*/
public abstract void makeDirty();
/**
* Moves the component by the offsets specified by the components of
* the point.
*
* @param offset a <code>Point</code> value
*/
public abstract void move(Point offset);
/**
* Calls paint on self and then all the children in z order.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
public abstract boolean renderAll(Graphics g);
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected abstract void copy(LComponent component);
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected abstract boolean paint(Graphics g);
}

View File

@@ -0,0 +1,246 @@
package edu.gatech.cs2335.lemmings.gui;
import java.util.List;
import java.util.Vector;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Graphics;
/**
* Class LContainer: The parent for all components that can contain
* other components.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 13, 2004) - Created the LContainer class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 13, 2004
*/
public abstract class LContainer extends LComponent {
/**
* The list of children of the component.
*/
private List children;
/**
* Creates a new <code>LContainer</code> instance.
*/
public LContainer() {
super();
children = new Vector();
}
/**
* Moves the component by the offsets specified by the components of
* the point.
*
* @param offset a <code>Point</code> value
*/
public final void move(Point offset) {
Rectangle r = getBounds();
r.x += offset.x;
r.y += offset.y;
/*
Point p = new Point(getBounds().x, getBounds().y);
p.x += offset.x;
p.y += offset.y;
this.setPosition(p);
*/
//Now that we've moved self, we need to also move all the children:
for (int i = 0; i < children.size(); i++) {
((LComponent) children.get(i)).move(offset);
}
}
/**
* Marks the component dirty.
*/
public final void makeDirty() {
setDirty(true);
//Set all children dirty as well:
for (int i = 0; i < children.size(); i++) {
((LComponent) children.get(i)).makeDirty();
}
}
/**
* Calls paint on self and then all the children in z order.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
public final boolean renderAll(Graphics g) {
if (!isShown()) {
//I am invisible. No go.
return false;
}
/*
if (!this.isComponentDirty()) {
//Not dirtty. No need to rerender:
return true;
}
*/
//Paint self:
if (!this.paint(g)) {
//Could not paint self:
return false;
}
this.setDirty(false);
//Now, render the children:
for (int i = 0; i < children.size(); i++) {
((LComponent) children.get(i)).renderAll(g);
}
return true;
}
/**
* Attempts to add the component passed in as a child of this
* component. Returns true if the addition was successful, and false
* if it was not.
*
* @param child a <code>LComponent</code> value
* @return a <code>boolean</code> value
*/
public final boolean addChild(LComponent child) {
if (child == null) {
return false;
}
if (!children.contains(child)) {
//The list does not yet contain the child. Add it:
children.add(child);
}
child.setParent(this);
return true;
}
/**
* Attempts to remove the component passed in from the list of
* children of the current component. Returns true if the removal is
* successful, or false if the child could not be found in the
* list. The child is not deleted after removal.
*
* @param child a <code>LComponent</code> value
* @return a <code>boolean</code> value
*/
public final boolean removeChild(LComponent child) {
if (child == null) {
return false;
}
if (!children.contains(child)) { return false; }
child.setParent(null);
children.remove(child);
return true;
}
/**
* Returns the list of children.
* @return a <code>List</code> value
*/
protected final List getChildren() {
return children;
}
/**
* Removes all children.
*/
public final void clear() {
while (children.size() > 0) {
removeChild((LComponent) children.get(0));
}
children.clear();
}
/**
* Returns the pointer to the child of this component that the
* specified coordinates are inside of, if there is one, or NULL if
* there is no such child.
*
* @param coords a <code>Point</code> value
* @return a <code>LComponent</code> value
*/
public final LComponent findChildAtCoordinates(Point coords) {
int childCount = children.size() - 1;
LComponent currentComponent = null;
LComponent childComponent = null;
//If I am invisible, so are my children. No need to continue:
if (!this.isShown()) {
return null;
}
//Start with the top-most child:
for (int i = childCount; i >= 0; i--) {
currentComponent = (LComponent) children.get(i);
if (currentComponent instanceof LContainer) {
childComponent = ((LContainer) currentComponent)
.findChildAtCoordinates(coords);
if (childComponent != null) {
//Found one:
return childComponent;
}
} else if (currentComponent.getBounds().contains(coords)
&& currentComponent.isShown()) {
return currentComponent;
}
}
//I have no children at the specified coordinates... Am I there?
if (this.getBounds().contains(coords)) {
//So I am!
return this;
}
//Nothing fit...
return null;
}
/**
* Returns the top-most active child of this component.
*@return bcomponent
*/
public final LComponent findActiveChild() {
if (!isActive()) {
//I'm not active, so neither could be my children:
return null;
}
//Get last child:
LComponent temp = (LComponent) children.get(children.size() - 1);
//Get last child's active child:
if (temp instanceof LContainer) {
temp = ((LContainer) temp).findActiveChild();
}
if (temp == null) {
return this;
}
return temp;
}
}

View File

@@ -0,0 +1,215 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
//import java.awt.AlphaComposite;
/**
* Class LFader: This little widget allows one to fade the screen to a
* certain color in different kinds of cool ways.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 15, 2004) - Created the LFader class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 15, 2004
*/
public class LFader extends LColoredComponent {
/**
* Show debug output?
*/
private static final boolean VERBOSE = false;
/**
* Just a little flag so we know when we have started fading.
*/
private boolean startedFading;
/**
* The amount of time remaining in the fading.
*/
private long timeRemaining;
/**
* Creates a new <code>LFader</code> instance.
*/
public LFader() {
startedFading = false;
}
/**
* Creates a new thread that will update the fading of the thing
* specially timed to complete in the specified number of millis.
*
* @param time a <code>long</code> value
* @return a <code>Thread</code> value
*/
private Thread createThread(final long time) {
Thread defThread = new Thread(new Runnable() {
public void run() { }
});
//First, make sure we have a valid time:
if (time < 0) {
return defThread;
}
if ((time * 6) / 100 == 0) {
//The time period provided is much too short.
return defThread;
}
Runnable r = new Runnable() {
public void run() {
//We would like to have 60 steps per second, so calculate
//the number of steps.
long numSteps = (time * 6) / 100;
//Calculate the delay between each step:
long delay = time / numSteps;
//Calculate the percent of the fade to do at each step:
float incrementPercent = 1.0f / numSteps;
//A variable to store the progress to the completion of the fade:
float currentPercentComplete = 0.0f;
//The time of the last update:
long lastTime = System.currentTimeMillis();
//The targetted end fade time:
long finalTime = time + lastTime;
if (VERBOSE) {
System.out.println("LFader: Set up fading... ");
System.out.println(" Time: " + time);
System.out.println(" Steps: " + numSteps);
System.out.println(" Delay: " + delay);
System.out.println(" Increment: " + incrementPercent);
System.out.flush();
}
resetFade();
startedFading = true;
for (int i = 0; i < numSteps; i++) {
//Update the fading:
update(currentPercentComplete);
//Increment percent to completion:
currentPercentComplete += incrementPercent;
//Sleep some:
lastTime += delay;
try {
Thread.sleep(Math.max(0, lastTime - System.currentTimeMillis()));
} catch (Exception e) {
//Sleep interrupted...
System.out.println("");
} finally {
lastTime = System.currentTimeMillis();
timeRemaining = finalTime - lastTime;
}
}
// startedFading = false;
}
};
return new Thread(r);
}
/**
* Fades to foreground in the time specified.
*
* @param time a <code>long</code> value
*/
public void fadeToForeground(long time) {
createThread(time).start();
}
/**
* Returns true if we are currently fading.
*
* @return a <code>boolean</code> value
*/
public boolean isFading() {
return startedFading;
}
/**
* Describe <code>getTimeRemaining</code> method here.
*
* @return a <code>long</code> value
*/
public long getTimeRemaining() {
return timeRemaining;
}
/**
* Updates the fading.
* @param progress the percent to completion.
*/
protected void update(float progress) {
if (VERBOSE) {
System.out.println("Updating - " + (progress * 100) + "%");
System.out.flush();
}
//Do a simple fade to black for now:
Color f = getForeground();
setForeground(new Color(f.getRed() / 255.0f,
f.getGreen() / 255.0f,
f.getBlue() / 255.0f,
progress));
}
/**
* Resets the fading to the original configuration.
*/
protected void resetFade() {
Color f = getForeground();
setForeground(new Color(f.getRed() / 255.0f,
f.getGreen() / 255.0f,
f.getBlue() / 255.0f,
0.0f));
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
if (!startedFading) {
return true;
}
if (VERBOSE) {
System.out.println("Fading - " + getForeground());
System.out.flush();
}
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getForeground());
g2d.fill(getBounds());
return true;
}
}

View File

@@ -0,0 +1,129 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
/**
* Class LFancyLabel: In addition to the regular label functionality,
* this guy allows you to specify different fonts for the label.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 13, 2004) - Created the LFancyLabel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 13, 2004
*/
public final class LFancyLabel extends LLabel {
/**
* Show lots of debug output?
*/
private static final boolean VERBOSE = false;
/**
* The default font.
*/
public static final Font DEFAULT_FONT;
static {
//Initialize default font to the first font available
Font temp = GraphicsEnvironment
.getLocalGraphicsEnvironment().getAllFonts()[0];
DEFAULT_FONT = temp.deriveFont(Font.PLAIN, 16.0f);
}
/**
* The font used to render this fancy label.
*/
private Font currentFont;
/**
* Creates a new <code>LFancyLabel</code> instance.
*/
public LFancyLabel() {
this(" ");
}
/**
* Creates a new <code>LFancyLabel</code> instance.
*
* @param text a <code>String</code> value
*/
public LFancyLabel(String text) {
this(text, DEFAULT_FONT);
}
/**
* Creates a new <code>LFancyLabel</code> instance.
*
* @param text a <code>String</code> value
* @param font a <code>Font</code> value
*/
public LFancyLabel(String text, Font font) {
super(text);
setFont(font);
}
/**
* Returns the fint associated with this label.
*
* @return a <code>Font</code> value
*/
public Font getFont() {
return currentFont;
}
/**
* Sets up a new font value.
*
* @param newFont a <code>Font</code> value
*/
public void setFont(Font newFont) {
if (newFont == null) {
throw new NullPointerException();
}
currentFont = newFont;
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
super.copy(component);
LFancyLabel target = (LFancyLabel) component;
target.setFont(new Font(getFont().getName(), getFont().getStyle(),
getFont().getSize()));
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
if (VERBOSE) {
System.out.println("LFancyLabel: Rendering with:");
System.out.println("\t" + getFont());
System.out.flush();
}
g.setColor(this.getForeground());
g.setFont(this.getFont());
g.drawString(this.getText(), this.getBounds().x, this.getBounds().y);
return true;
}
}

View File

@@ -0,0 +1,117 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Graphics;
import edu.gatech.cs2335.lemmings.graphics.TileSet;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
/**
* Class LIcon: This class will display an image.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 14, 2004) - Created the LIcon class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 14, 2004
*/
public final class LIcon extends LLeafComponent {
/**
* The sprite containing the image to be displayed.
*/
private AnimatedSprite image;
/**
* Creates a new <code>LIcon</code> instance.
*
*/
public LIcon() {
this(null);
}
/**
* Creates a new <code>LIcon</code> instance.
*@param set set
*/
public LIcon(TileSet set) {
setAnimation(new AnimatedSprite(set,
Direction.NO_DIRECTION,
Looping.NONE));
}
/**
* Returns the animation associated with this icon.
*
* @return an <code>AnimatedSprite</code> value
*/
public synchronized AnimatedSprite getAnimation() {
return image;
}
/**
* Sets up a new icon image.
*
* @param newSprite an <code>AnimatedSprite</code> value
*/
public synchronized void setAnimation(AnimatedSprite newSprite) {
//Save the sprite:
image = newSprite;
//Update dimensions:
Dimension newSize = image.getAnimation().getLargestDimension();
setSize(newSize.width, newSize.height);
//Updated:
this.makeDirty();
}
/**
* Returns the looping mode used by this animation.
*
* @return a <code>Looping</code> value
*/
public Looping getLoopingMode() {
return getAnimation().getLoopingMode();
}
/**
* Sets up a new looping mode for this icon.
*
* @param mode a <code>Looping</code> value
*/
public void setLoopingMode(Looping mode) {
getAnimation().setLoopingMode(mode);
this.makeDirty();
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
LIcon target = (LIcon) component;
target.setAnimation(this.getAnimation());
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
return image.renderSprite(g, new Point(getBounds().x, getBounds().y));
}
}

View File

@@ -0,0 +1,114 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Graphics;
/**
* Class LLabel: Your regular old label.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 13, 2004) - Created the LLabel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 13, 2004
*/
public class LLabel extends LColoredComponent {
/**
* Show lots of debug output?
*/
private static final boolean VERBOSE = false;
/**
* The text to display.
*/
private String labelText;
/**
* Creates a new <code>LLabel</code> instance.
*/
public LLabel() {
this(" ");
}
/**
* Creates a new <code>LLabel</code> instance.
*
* @param text a <code>String</code> value
*/
public LLabel(String text) {
setText(text);
}
/**
* Returns the text of this label.
*
* @return a <code>String</code> value
*/
public final String getText() {
return labelText;
}
/**
* Gives this label new text.
*
* @param text a <code>String</code> value
*/
public final void setText(String text) {
String t = text;
if (t == null) {
t = " ";
}
labelText = t;
updateDimensions();
}
/**
* Updates the bounds of the button.
*/
protected final void updateDimensions() {
//We should now know the number of columns...
//Make the margin of 6:
int width = labelText.length() * LTextField.CHARACTER_DIMENSION.width;
int height = LTextField.CHARACTER_DIMENSION.height;
getBounds().width = width;
getBounds().height = height;
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
LLabel target = (LLabel) component;
target.setText(new String(this.getText()));
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
if (VERBOSE) {
System.out.println("LLabel: Painting...");
System.out.flush();
}
g.setColor(getForeground());
g.setFont(LFancyLabel.DEFAULT_FONT);
g.drawString(getText(), getBounds().x, getBounds().y + getBounds().height);
return true;
}
}

View File

@@ -0,0 +1,87 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Graphics;
/**
* Class LLeafComponent: This is the opposite of a container. That is,
* there can be no children for this component.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 13, 2004) - Created the LLeafComponent class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 13, 2004
*/
public abstract class LLeafComponent extends LComponent {
/**
* Creates a new <code>LLeafComponent</code> instance.
*/
public LLeafComponent() {
super();
}
/**
* Moves the component by the offsets specified by the components of
* the point.
*
* @param offset a <code>Point</code> value
*/
public final void move(Point offset) {
Rectangle r = getBounds();
r.x += offset.x;
r.y += offset.y;
/*
Point p = new Point(getBounds().x, getBounds().y);
p.x += offset.x;
p.y += offset.y;
this.setPosition(p);
*/
}
/**
* Marks the component dirty.
*/
public final void makeDirty() {
setDirty(true);
}
/**
* Calls paint on self and then all the children in z order.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
public final boolean renderAll(Graphics g) {
if (!isShown()) {
//I am invisible. No go.
return false;
}
/*
if (!this.isComponentDirty()) {
//Not dirtty. No need to rerender:
return true;
}
*/
this.setDirty(false);
//Paint self:
if (!this.paint(g)) {
//Could not paint self:
return false;
}
return true;
}
}

View File

@@ -0,0 +1,146 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
import java.awt.Graphics;
import java.util.List;
//import java.awt.event.MouseEvent;
//import java.awt.event.KeyEvent;
/**
* Class LRootPane: This is the class for the root pane. It will be
* the root of everything else. It will be immovable, and will
* distribute the events to whichever children get them.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 13, 2004) - Created the LRootPane class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 13, 2004
*/
public class LRootPane extends LContainer {
/**
* Creates a new <code>LRootPane</code> instance.
*/
public LRootPane() {
super();
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
if (component == null) {
return;
}
LRootPane target = (LRootPane) component;
target.clear();
//Copy the children pointers over.
List c = getChildren();
for (int i = 0; i < c.size(); i++) {
target.addChild((LComponent) c.get(i));
}
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
//Root pane is invisible:
return true;
}
/**
* Dispatches the mouse event to the appropriate child.
*@param button b
*@param coords c
*/
public void dispatchMouseClickEvent(int button, Point coords) {
LComponent comp = findChildAtCoordinates(coords);
if (comp == null) {
return;
}
if (comp instanceof IClickable) {
//Translate to coords relative to the clickable:
coords.translate(-comp.getBounds().x, -comp.getBounds().y);
//Register:
((IClickable) comp).registerMouseClick(button, coords);
}
}
/**
* Dispatches the mouse event to the appropriate child.
*@param oldCoords o
*@param newCoords n
*/
public void dispatchMouseMotionEvent(Point oldCoords, Point newCoords) {
LComponent comp = findChildAtCoordinates(newCoords);
if (comp == null) {
return;
}
if (comp instanceof IMouseMotionable) {
//Translate to coords relative to the mousable:
// oldCoords.translate(-comp.getBounds().x, -comp.getBounds().y);
// newCoords.translate(-comp.getBounds().x, -comp.getBounds().y);
//Register:
((IMouseMotionable) comp).processMouseMotion(oldCoords.x, oldCoords.y,
newCoords.x, newCoords.y);
}
}
/**
* Describe <code>dispatchMouseWheelEvent</code> method here.
*
* @param coordinates a <code>Point</code> value
* @param numClicks an <code>int</code> value
*/
public void dispatchMouseWheelEvent(Point coordinates, int numClicks) {
LComponent comp = findChildAtCoordinates(coordinates);
if (comp instanceof IMouseWheelable) {
((IMouseWheelable) comp).processMouseWheel(numClicks);
}
}
/**
* Dispatches the mouse event to the appropriate child.
*@param button b
*@param coords c
*@param offset o
*/
public void dispatchMouseDragEvent(int button, Point coords, Point offset) {
}
/**
* Dispatches the keyboard event to the appropriate child.
*@param keyCode k
*@param modifiers m
*/
public void dispatchKeyboardEvent(char keyCode, int modifiers) {
LComponent child = findActiveChild();
if (child instanceof ITypable) {
((ITypable) child).processKeyTyped(keyCode, modifiers);
}
}
}

View File

@@ -0,0 +1,380 @@
package edu.gatech.cs2335.lemmings.gui;
//import java.awt.Font;
import java.awt.Point;
import java.awt.Color;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Graphics;
import java.awt.Dimension;
//import java.awt.Rectangle;
import java.awt.Graphics2D;
import java.awt.BasicStroke;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;
//Font metrics stuff:
//import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
//import java.awt.GraphicsConfiguration;
/**
* Class LTextField: This guy basically allows one to type in some text.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 14, 2004) - Created the LTextField class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 14, 2004
*/
public final class LTextField extends LColoredComponent
implements ITypable, IClickable {
/**
* Show debug output?
*/
private static final boolean VERBOSE = false;
/**
* The dimensions of one character.
*/
public static final Dimension CHARACTER_DIMENSION;
static {
//Get the default configuration for the default device.
Graphics2D defaultGraphics
= (Graphics2D) GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration()
.createCompatibleImage(1, 1).getGraphics();
//These are the bounds of the widest character.
Rectangle2D bounds
= LFancyLabel.DEFAULT_FONT
.getMaxCharBounds(defaultGraphics.getFontRenderContext());
CHARACTER_DIMENSION = new Dimension((int) bounds.getWidth(),
(int) bounds.getHeight());
};
/**
* The stroke to use for the border of the textbox.
*/
private Stroke borderStroke = new BasicStroke(2.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_ROUND);
/**
* The color of the border of the textbox.
*/
private Color borderColor = Color.black;
/**
* The text currently set in the text field.
*/
private StringBuffer textBuffer;
/**
* The position of the caret.
*/
private int caretPosition;
/**
* The number of columns in this textbox.
*/
private int numColumns;
/**
* Creates a new <code>LTextField</code> instance.
*/
public LTextField() {
this(" ");
}
/**
* Creates a new <code>LTextLabel</code> instance.
*
* @param initialText a <code>String</code> value
*/
public LTextField(String initialText) {
setText(initialText);
setNumColumns(10);
}
/**
* Returns the text of this label.
*
* @return a <code>String</code> value
*/
public String getText() {
return textBuffer.toString();
}
/**
* Gives this label new text.
*
* @param t a <code>String</code> value
*/
public void setText(String t) {
String text = t;
if (text == null) {
text = " ";
}
textBuffer = new StringBuffer(text);
}
/**
* Get the value of numColumns.
* @return value of numColumns.
*/
public int getNumColumns() {
return numColumns;
}
/**
* Set the value of numColumns.
* @param v Value to assign to numColumns.
*/
public void setNumColumns(int v) {
//PMD FIX
int tv = v;
if (tv <= 0) {
tv = 1;
}
this.numColumns = tv;
updateDimensions();
}
/**
* Updates the bounds of the button.
*/
protected void updateDimensions() {
//We should now know the number of columns...
//Make the margin of 6:
int width = numColumns * CHARACTER_DIMENSION.width + 6;
int height = CHARACTER_DIMENSION.height + 6;
getBounds().width = width;
getBounds().height = height;
}
/**
* Sets the new caret position if possible.
*
* @param newPosition an <code>int</code> value
*/
public final void setCaretPosition(final int newPosition) {
int length = textBuffer.length();
if (newPosition < 0) {
this.caretPosition = 0;
}
if (newPosition > length) {
this.caretPosition = length;
}
end();
}
/**
* Returns the caret position.
*
* @return an <code>int</code> value
*/
public final int getCaretPosition() {
return caretPosition;
}
/**
* Moves the caret one to the left if possible.
*/
public final void moveCaretLeft() {
if (caretPosition > 0) {
caretPosition--;
}
}
/**
* Moves the caret one to the right is possible.
*/
public final void moveCaretRight() {
int length = textBuffer.length();
if (caretPosition < length) {
caretPosition++;
}
}
/**
* Does a HOME.
*/
public final void home() {
caretPosition = 0;
}
/**
* Does an END.
*/
public final void end() {
caretPosition = textBuffer.length();
}
/**
* Does a backspace.
*/
public final void backSpace() {
int oldPosition = getCaretPosition();
moveCaretLeft();
if (getCaretPosition() != oldPosition) {
delete();
}
}
/**
* Does a delete.
*/
public final void delete() {
int length = textBuffer.length();
if (caretPosition < length) {
//Delete the char:
textBuffer.deleteCharAt(caretPosition);
}
}
/**
* Registers the click of a mouse button at the specified coordinates.
*
* @param button an <code>int</code> value
* @param coords a <code>Point</code> value
*/
public void registerMouseClick(int button, Point coords) {
bringToTop();
}
/**
* Processes the key type event.
*
* @param key The key that was typed.
* @param modifiers The modifiers that accompanied the
* key-type. This is a collection of ORed flags, like SHIFT, ALT,
* etc.
*/
public void processKeyTyped(char key, int modifiers) {
if (VERBOSE) {
System.out.println("LTextField - Got key code " + (int) key);
System.out.println(" The ASCII is " + key);
System.out.println(" And the text "
+ KeyEvent.getKeyText(key));
System.out.println("");
System.out.flush();
}
switch (key) {
case KeyEvent.VK_BACK_SPACE:
backSpace();
break;
case KeyEvent.VK_DELETE:
delete();
break;
/***** This stuff doesn't work with the char key. Have to find a
* way to get around it and use the key code instead.
case KeyEvent.VK_LEFT: case KeyEvent.VK_UP:
moveCaretLeft();
break;
case KeyEvent.VK_RIGHT: case KeyEvent.VK_DOWN:
moveCaretRight();
break;
case KeyEvent.VK_HOME: case KeyEvent.VK_PAGE_UP:
home();
break;
case KeyEvent.VK_END: case KeyEvent.VK_PAGE_DOWN:
end();
break;
*/
default:
//Insert character:
// textBuffer.insert(caretPosition, KeyEvent.getKeyText(key));
textBuffer.insert(caretPosition, key);
moveCaretRight();
break;
}
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
LTextField target = (LTextField) component;
target.setText(new String(this.getText()));
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
//Render background:
g2d.setColor(getBackground());
g2d.fill(getBounds());
//Render border around the textbox:
g2d.setStroke(borderStroke);
g2d.setColor(borderColor);
g2d.draw(getBounds());
//Render text in the textbox:
//Set up clipping 'cause I'm lazy:
Shape oldClippie = g2d.getClip();
g2d.setClip(getBounds());
//XXX: I render the caret as a pipe ('|') here...
if (isActive()) {
textBuffer.insert(caretPosition, '|');
}
g.setColor(getForeground());
g.setFont(LFancyLabel.DEFAULT_FONT);
g.drawString(getText(),
getBounds().x + 3,
getBounds().y + getBounds().height - 5);
//XXX: Continued:
if (isActive()) {
textBuffer.deleteCharAt(caretPosition);
}
//Reset clipping. Again 'cause I'm lazy and don't want to compute
//which parts of the string to render myself:
g2d.setClip(oldClippie);
return true;
}
}

View File

@@ -0,0 +1,263 @@
package edu.gatech.cs2335.lemmings.gui;
//import java.awt.Font;
import java.awt.Color;
import java.awt.Point;
import java.awt.Graphics;
//import java.awt.Dimension;
import java.awt.Rectangle;
//import java.awt.FontMetrics;
//import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
//import java.util.Date;
//import java.util.List;
//import java.util.Vector;
//import java.util.Iterator;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
/**
* Class LToggleButton: this is the ltogglebutton class.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 14, 2004) - Created the LToggleButton class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 14, 2004
*/
public final class LToggleButton extends LAbstractButton
implements ActionListener {
/**
* The text to display on this button.
*/
private String text;
/**
* The image to display on this button.
*/
private AnimatedSprite image;
/**
* Set to true if this button is selected, or false if it is not.
*/
private boolean selected;
/**
* The color of the button when it is unselected.
*/
private Color unselectedColor = new Color(1.0f, 1.0f, 1.0f, 0.1f);
/**
* The color of the button when it is selected.
*/
private Color selectedColor = new Color(1.0f, 1.0f, 1.0f, 0.5f);
/**
* Describe variable <code>coord</code> here.
*
*/
private Point coord = new Point();
/**
* Creates a new <code>LToggleButton</code> instance.
*/
public LToggleButton() {
super();
text = "LToggleButton";
image = null;
setSelected(false);
addActionListener(this);
}
/**
* Returns the text displayed on this button.
*
* @return a <code>String</code> value
*/
public String getText() {
return (this.text);
}
/**
* Sets the new value of the text on this button.
*
* @param v a <code>String</code> value
*/
public void setText(String v) {
this.text = v;
setActionCommand(text);
image = null;
updateDimensions();
}
/**
* Returns the image displayed on this button.
*
* @return an <code>AnimatedSprite</code> value
*/
public AnimatedSprite getImage() {
return image;
}
/**
* Sets up a new value for the image of this button.
*
* @param newImage an <code>AnimatedSprite</code> value
*/
public void setImage(AnimatedSprite newImage) {
image = newImage;
setActionCommand(newImage.getAnimation().getFileName());
text = null;
updateDimensions();
}
/**
* Describe <code>updateDimensions</code> method here.
*
*/
protected void updateDimensions() {
if (text != null) {
/*
//We are updating due to changes in text:
FontMetrics metrics = new FontMetrics(LFancyLabel.DEFAULT_FONT);
setSize(metrics.charsWidth(text.toCharArray(), 0,
text.length()), metrics.getHeight());
*/
//Hack. Must make the size be actually dependent on text...
setSize(100, 100);
} else if (image != null) {
//We are updating due to image:
int x = getBounds().x;
int y = getBounds().y;
Rectangle e = image.getAnimation().getExtent(image.getCurrentFrame());
if (LApplication.VERBOSE) {
System.out.println("LButton: Coords - (" + x + ", " + y + ")");
System.out.println(" Extent - " + e);
System.out.flush();
}
setBounds(e);
getBounds().translate(x, y);
} else {
//Both text and image are null... Bad.
throw new NullPointerException("Both text and image are null.");
}
makeDirty();
}
/**
* Returns whether or not the button is currently selected.
*
* @return a <code>boolean</code> value
*/
public boolean isSelected() {
return selected;
}
/**
* Describe <code>setSelected</code> method here.
*
* @param v a <code>boolean</code> value
*/
public void setSelected(boolean v) {
selected = v;
}
/**
* Toggles the selection on the button.
*/
public void toggleSelected() {
if (isSelected()) {
setSelected(false);
} else {
setSelected(true);
}
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
LToggleButton target = (LToggleButton) component;
target.setActionListeners(getActionListeners());
target.setActionCommand(this.getActionCommand());
target.text = this.getText();
target.image = this.getImage();
target.updateDimensions();
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
java.awt.Graphics2D gr = (java.awt.Graphics2D) g;
Rectangle bounds = this.getBounds();
//Render background:
if (selected) {
gr.setColor(selectedColor);
} else {
gr.setColor(unselectedColor);
}
gr.fill(bounds);
if (text != null) {
//Render text button...
g.setColor(Color.black);
g.setFont(LFancyLabel.DEFAULT_FONT);
//Render border first:
g.draw3DRect(bounds.x - 5, bounds.y - 5,
bounds.width + 5, bounds.height + 5, true);
//Render text:
g.drawString(getText(), bounds.x, bounds.y);
return true;
} else if (image != null) {
//Render image button...
Rectangle e = image.getAnimation().getExtent(image.getCurrentFrame());
coord.x = bounds.x - e.x;
coord.y = bounds.y - e.y;
return image.renderSprite(g, coord);
} else {
//No text, no image... :/
throw new NullPointerException("Both text and image are null.");
}
}
/**
* Describe <code>actionPerformed</code> method here.
*
* @param e an <code>ActionEvent</code> value
*/
public void actionPerformed(ActionEvent e) {
toggleSelected();
}
}

View File

@@ -0,0 +1,251 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Font;
import java.awt.Point;
import java.awt.Color;
import java.awt.Stroke;
import java.awt.Graphics;
//import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.BasicStroke;
//import java.util.List;
//import java.util.Vector;
import edu.gatech.cs2335.lemmings.engine.LevelData;
import edu.gatech.cs2335.lemmings.file.LevelReader;
/**
* Class LevelButton: This is essentially the button that shows the
* name of the level, it's thumbnail, and some statistics.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 29, 2004) - Created the LevelButton class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 29, 2004
*/
public class LevelButton extends LAbstractButton {
/**
* The data associated with this level button.
*/
private LevelData data;
/**
* The preview of the level.
*/
//private LIcon preview; //Make this the minimap, eventually.
/**
* The label containing the level name.
*/
private LFancyLabel lblLevelName;
/**
* The label that will show the number of lemmings to be released.
*/
private LLabel lblNumLemmings;
/**
* The label that will show the percentage of lemmings to be saved.
*/
private LLabel lblSavePercent;
/**
* The label that will show the time limit on the level.
*/
private LLabel lblTimeLimit;
/**
* Set to true if this button is selected, or false if it is not.
*/
private boolean selected;
/**
* The stroke to use for the border of the button.
*/
private Stroke borderStroke = new BasicStroke(3.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_ROUND);
/**
* The color of the border of the button.
*/
private Color borderColor = Color.black;
/**
* The color of the button when it is unselected.
*/
private Color unselectedColor = new Color(1.0f, 1.0f, 1.0f, 0.1f);
/**
* The color of the button when it is selected.
*/
private Color selectedColor = new Color(1.0f, 1.0f, 1.0f, 0.5f);
/**
* Creates a new <code>LevelButton</code> instance.
*
* @param levelName The id associated with the level we would like
* to display in this button.
*/
public LevelButton(String levelName) {
//Initialize the preview here:
//preview = new LIcon();
setActionCommand(levelName);
//Initialize the labels:
lblLevelName = new LFancyLabel();
lblLevelName.setFont(LFancyLabel.DEFAULT_FONT
.deriveFont(Font.BOLD, 20.0f));
lblLevelName.setShown(true);
lblNumLemmings = new LLabel();
lblNumLemmings.setShown(true);
lblSavePercent = new LLabel();
lblSavePercent.setShown(true);
lblTimeLimit = new LLabel();
lblTimeLimit.setShown(true);
setLevel(levelName);
}
/**
* Returns whether or not the button is currently selected.
*
* @return a <code>boolean</code> value
*/
public boolean isSelected() {
return selected;
}
/**
* Describe <code>setSelected</code> method here.
*
* @param v a <code>boolean</code> value
*/
public void setSelected(boolean v) {
selected = v;
}
/**
* Toggles the selection on the button.
*/
public void toggleSelected() {
if (isSelected()) {
setSelected(false);
} else {
setSelected(true);
}
}
/**
* Updates the data when need be.
*/
private void updateData() {
lblLevelName.setText(data.getName());
lblNumLemmings.setText("Lemmings: " + data.getNumLemmings());
lblSavePercent.setText("Save: " + data.getPercentToSave() + "%");
lblTimeLimit.setText("Time: " + data.getTimeLimit() + " seconds");
}
/**
* Updates the bounds of the button.
*/
protected void updateDimensions() {
int x = getBounds().x;
int y = getBounds().y;
x += 120;
y += 25;
lblLevelName.setPosition(new Point(x, y));
y += 5;
lblNumLemmings.setPosition(new Point(x, y));
y += lblNumLemmings.getBounds().height + 5;
lblSavePercent.setPosition(new Point(x, y));
y += lblSavePercent.getBounds().height + 5;
lblTimeLimit.setPosition(new Point(x, y));
getBounds().width = 350;
getBounds().height = 100;
}
/**
* Sets up to display the level with the specified id.
*
* @param name a <code>String</code> value
* name cannot be found.
*/
public final void setLevel(String name) {
data = LevelReader.getInstance().loadLevelData(name);
setActionCommand(name);
if (data == null) {
throw new NullPointerException("No data for level \"" + name
+ "\" found...");
}
updateData();
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
//Bah, we won't be doing any *copying*, shall we?
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
java.awt.Graphics2D gr = (java.awt.Graphics2D) g;
Rectangle r = getBounds();
//Render button background:
if (selected) {
gr.setColor(selectedColor);
} else {
gr.setColor(unselectedColor);
}
gr.fill(r);
//Render border around the button:
gr.setStroke(borderStroke);
gr.setColor(borderColor);
gr.draw(r);
//Render labels 'n things:
lblLevelName.makeDirty();
lblLevelName.renderAll(g);
lblNumLemmings.makeDirty();
lblNumLemmings.renderAll(g);
lblSavePercent.makeDirty();
lblSavePercent.renderAll(g);
lblTimeLimit.makeDirty();
lblTimeLimit.renderAll(g);
return true;
}
}

View File

@@ -0,0 +1,396 @@
package edu.gatech.cs2335.lemmings.gui;
//import java.awt.Font;
import java.awt.Point;
//import java.awt.Color;
//import java.awt.Stroke;
import java.awt.Graphics;
//import java.awt.Dimension;
import java.awt.Rectangle;
//import java.awt.BasicStroke;
//import java.util.List;
//import java.util.Vector;
//import edu.gatech.cs2335.lemmings.engine.LevelData;
//import edu.gatech.cs2335.lemmings.file.LevelReader;
//import java.awt.Point;
//import java.awt.Color;
//import java.awt.Rectangle;
//import java.awt.Graphics;
//import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
//import java.util.Vector;
//import java.util.Iterator;
//import java.awt.Point;
//import java.awt.event.ActionEvent;
//import edu.gatech.cs2335.lemmings.gui.LIcon;
//import edu.gatech.cs2335.lemmings.gui.LButton;
//import edu.gatech.cs2335.lemmings.gui.LRootPane;
import edu.gatech.cs2335.lemmings.graphics.Looping;
import edu.gatech.cs2335.lemmings.graphics.Direction;
import edu.gatech.cs2335.lemmings.graphics.AnimatedSprite;
import edu.gatech.cs2335.lemmings.graphics.TileSetManager;
/**
* Class LevelContainer: Basically a panel that contains level buttons.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 11, 2004) - Created the LevelContainer class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 11, 2004
*/
public final class LevelContainer extends LContainer
implements ActionListener, IMouseWheelable {
/**
* Show debug output?
*/
public static final boolean VERBOSE = false;
/**
* The number of pixels to put between the buttons.
*/
public static final int SPACING = 15;
/**
* How many levels shall we display at once?
*/
private static final int ENTRIES_PER_PAGE = 8;
/**
* Contains the id of the currently selected level.
*/
private String selectedLevel;
/**
* Describe variable <code>up</code> here.
*
*/
private LButton up;
/**
* Describe variable <code>down</code> here.
*
*/
private LButton down;
/**
* The index of the currently displayed item.
*/
private int currentIndex;
/**
* Creates a new <code>LevelContainer</code> instance.
*/
public LevelContainer() {
//Add the scroller buttons:
up = new LButton();
up.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("scroll_up"),
Direction.NO_DIRECTION,
Looping.NONE));
up.setPosition(new Point(0, 0));
up.setShown(true);
up.setActionCommand("scroll_btn_up");
up.addActionListener(this);
down = new LButton();
down.setImage(new AnimatedSprite(TileSetManager.getInstance()
.getTileSet("scroll_down"),
Direction.NO_DIRECTION,
Looping.NONE));
down.setPosition(new Point(0, 0));
down.setShown(true);
down.setActionCommand("scroll_btn_down");
down.addActionListener(this);
addChild(up);
addChild(down);
currentIndex = 2;
}
/**
* Updates the bounds of the button.
*/
protected synchronized void updateDimensions() {
Rectangle r = getBounds();
up.setPosition(new Point(r.x + r.width, r.y));
down.setPosition(new Point(r.x + r.width, r.y + r.height));
}
/**
* Creates a level button for the specified level id.
*
* @param levelId a <code>String</code> value
*/
public synchronized void addLevelButton(String levelId) {
List children = getChildren();
int numButtons = children.size();
// boolean left = ((numButtons % 2) == 0);
//Create new Level Button:
LevelButton butt = new LevelButton(levelId);
// butt.setShown(true);
butt.addActionListener(this);
/*
Point coords = new Point();
if (numButtons == 0) {
//This is the first button:
coords.x = getBounds().x;
coords.y = getBounds().y;
} else if (left) {
//This is not the first button, but it will be in the left column:
LComponent pc = (LComponent) children.get(children.size() - 2);
coords.x = getBounds().x;
coords.y = pc.getBounds().y + pc.getBounds().height + SPACING;
} else {
//This is not the first button, and it will be in the right column:
LComponent pc = (LComponent) children.get(children.size() - 1);
coords.x = pc.getBounds().x + pc.getBounds().width + SPACING;
coords.y = pc.getBounds().y;
}
butt.setPosition(coords);
*/
addChild(butt);
updateShownLevels();
if (numButtons == 0) {
setSelectedButton(levelId);
}
}
/**
* Sets up the selected button.
*
* @param id a <code>String</code> value
*/
protected synchronized void setSelectedButton(String id) {
List children = getChildren();
for (int i = 2; i < children.size(); i++) {
LevelButton butt = (LevelButton) children.get(i);
if (!id.equals(butt.getActionCommand())) {
if (butt.isSelected()) {
butt.setSelected(false);
}
} else {
if (!butt.isSelected()) {
butt.setSelected(true);
}
}
}
//Remember Selected Level:
selectedLevel = id;
}
/**
* Describe <code>getSelectedLevel</code> method here.
*
* @return a <code>String</code> value
*/
public synchronized String getSelectedLevel() {
return selectedLevel;
}
/**
* Describe <code>actionPerformed</code> method here.
*
* @param e an <code>ActionEvent</code> value
*/
public synchronized void actionPerformed(ActionEvent e) {
//Is it up or down button?
String c = e.getActionCommand();
if ("scroll_btn_up".equals(c)) {
scrollUp();
} else if ("scroll_btn_down".equals(c)) {
scrollDown();
} else {
//Update the selection.
setSelectedButton(e.getActionCommand());
}
}
/**
* Scrolls up.
*/
private synchronized void scrollUp() {
if (currentIndex <= 2) {
currentIndex = 2;
} else {
currentIndex -= 2;
}
updateShownLevels();
}
/**
* Scrolls down.
*/
private synchronized void scrollDown() {
if (getChildren().size() - 2 < ENTRIES_PER_PAGE) {
return;
}
if (currentIndex >= getChildren().size() - ENTRIES_PER_PAGE) {
currentIndex = getChildren().size() - ENTRIES_PER_PAGE;
if ((currentIndex % 2) == 1) {
currentIndex++;
}
} else {
currentIndex += 2;
}
updateShownLevels();
}
/**
* Shows those level which need to be shown and hides other ones.
*/
private synchronized void updateShownLevels() {
List children = getChildren();
int numButtons = children.size();
int curNum = 0;
boolean left = false;
Point coords = new Point();
for (int i = 2; i < numButtons; i++) {
LComponent butt = (LComponent) children.get(i);
if (VERBOSE) {
System.err.println("");
System.err.println("LevelContainer: Got Child #" + i);
System.err.flush();
}
if (i >= currentIndex && i < currentIndex + ENTRIES_PER_PAGE) {
curNum = i - currentIndex;
left = ((curNum % 2) == 0);
if (VERBOSE) {
System.err.println("\tIndex Into Viewport: " + curNum);
System.err.println("\tLeft column? " + left);
System.err.flush();
}
//Show:
butt.setShown(true);
//butt.bringToTop();
//Set Position:
coords.x = 0;
coords.y = 0;
if (curNum == 0) {
//This is the first button:
coords.x = getBounds().x;
coords.y = getBounds().y + SPACING;
if (VERBOSE) {
System.err.println("\tFirst Button...");
System.err.println("\t\tX - " + coords.x);
System.err.println("\t\tY - " + coords.y);
System.err.flush();
}
} else if (left) {
//This is not the first button, but it will be in the left column:
LComponent pc = (LComponent) children.get(i - 2);
coords.x = getBounds().x;
coords.y = pc.getBounds().y + pc.getBounds().height + SPACING;
if (VERBOSE) {
System.err.println("\tLeft Column...");
System.err.println("\t\tX - " + coords.x);
System.err.println("\t\tY - " + coords.y);
System.err.flush();
}
} else {
//This is not the first button, and it will be in the right column:
LComponent pc = (LComponent) children.get(i - 1);
coords.x = pc.getBounds().x + pc.getBounds().width + SPACING;
coords.y = pc.getBounds().y;
if (VERBOSE) {
System.err.println("\tRight Column...");
System.err.println("\t\tX - " + coords.x);
System.err.println("\t\tY - " + coords.y);
System.err.flush();
}
}
butt.setPosition(coords);
} else {
//Hide:
butt.setShown(false);
// butt.setPosition();
}
}
}
/**
* Processes the mouse wheel rotation.
*
* @param numClicks The number of clicks the wheel was rotated. A
* negative value means the wheel was rotated away from the user,
* and positive means it was rotated towards the user.
*/
public synchronized void processMouseWheel(int numClicks) {
if (numClicks < 0) {
for (int i = numClicks; i < 0; i++) {
scrollUp();
}
} else {
for (int i = 0; i < numClicks; i++) {
scrollDown();
}
}
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
//Nothing (?) to paint here.
return true;
}
}

View File

@@ -0,0 +1,247 @@
package edu.gatech.cs2335.lemmings.gui;
import java.awt.Point;
import java.awt.Color;
//import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
//import java.awt.geom.Ellipse2D;
//import java.awt.Shape;
import java.awt.Stroke;
import java.awt.BasicStroke;
import java.awt.geom.AffineTransform;
import edu.gatech.cs2335.lemmings.engine.Level;
import edu.gatech.cs2335.lemmings.engine.Portal;
import edu.gatech.cs2335.lemmings.engine.Lemming;
import edu.gatech.cs2335.lemmings.engine.LevelExit;
import edu.gatech.cs2335.lemmings.engine.LevelEntrance;
/**
* Class MiniMap: Contains the minimap of a level.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 23, 2004) - Created the MiniMap class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Apr. 23, 2004
*/
public final class MiniMap extends LLeafComponent {
/**
* Describe variable <code>level</code> here.
*
*/
private Level level;
/**
* Stuff.
*/
private Dimension mapSize;
/**
* Describe variable <code>scaleFactor</code> here.
*
*/
private Rectangle2D.Double scaleFactor;
/**
* This is a temporary variable to save the "normal" transformation
* matrix.
*/
private AffineTransform saveAT;
/**
* This is where the zoom transformation will be stored.
*/
private AffineTransform myTransform;
//Never got to this cool stuff. :( It's all ASCII art.
// /**
// * Describe variable <code>lemmingShape</code> here.
// *
// */
// private Shape lemmingShape;
// /**
// * Describe variable <code>entranceShape</code> here.
// *
// */
// private Shape entranceShape;
// /**
// * Describe variable <code>exitShape</code> here.
// *
// */
// private Shape exitShape;
/**
* The stroke to use for the border of the button.
*/
private Stroke borderStroke = new BasicStroke(3.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_ROUND);
/**
* The color of the border of the button.
*/
private Color borderColor = Color.lightGray;
/**
* Describe variable <code>coords</code> here.
*
*/
private Point coords;
/**
* Creates a new <code>MiniMap</code> instance.
* @param l a <code>Level</code> value
*/
public MiniMap(Level l) {
mapSize = new Dimension();
myTransform = new AffineTransform();
scaleFactor = new Rectangle2D.Double();
coords = new Point();
setLevel(l);
}
/**
* Get the value of level.
* @return value of level.
*/
public Level getLevel() {
return level;
}
/**
* Set the value of level.
* @param v Value to assign to level.
*/
public void setLevel(Level v) {
this.level = v;
updateDimensions();
}
/**
* Describe <code>worldToMini</code> method here.
*
* @param p a <code>Point</code> value
*/
protected void worldToMini(Point p) {
p.x *= scaleFactor.width;
p.y *= scaleFactor.height;
}
/**
* Updates the bounds of the button.
*/
protected void updateDimensions() {
//Update map size:
mapSize.width = level.getMap().getMap().getWidth();
mapSize.height = level.getMap().getMap().getHeight();
//Update affine:
int width = getBounds().width;
int height = getBounds().height;
scaleFactor.width = (1.0 * width) / mapSize.width;
scaleFactor.height = (1.0 * height) / mapSize.height;
myTransform.setToScale(scaleFactor.width, scaleFactor.height);
}
/**
* Deep-copies self <b>into the component</b> passed in.
*
* @param component a <code>LComponent</code> value
*/
protected void copy(LComponent component) {
//Blah. It's 3:33am. I'm not going to go around implementing
//useless methods!
}
/**
* Performs all of the necessary drawing for this control only. The
* children will be taken care of separately. This method need not
* concern itself with them.
*
* @param g a <code>Graphics</code> value
* @return a <code>boolean</code> value
*/
protected boolean paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
//Render black background:
g2d.setColor(Color.black);
g2d.fill(getBounds());
//Render border around the button:
g2d.setStroke(borderStroke);
g2d.setColor(borderColor);
g2d.draw(getBounds());
//Render the map itself:
saveAT = g2d.getTransform();
g2d.setTransform(myTransform);
g2d.drawImage(level.getMap().getGameMap(), null,
(int) (getBounds().x / scaleFactor.width),
(int) (getBounds().y / scaleFactor.height));
g2d.setTransform(saveAT);
//Render lemmings:
g2d.setColor(Color.pink);
for (int i = 0; i < level.getActiveLemmings().size(); i++) {
Lemming l = (Lemming) level.getActiveLemmings().get(i);
coords.x = l.getPosition().getX();
coords.y = l.getPosition().getY();
worldToMini(coords);
g2d.drawString(".", coords.x + getBounds().x, coords.y + getBounds().y);
}
//Render portals:
for (int v = 0; v < level.getLevelPortals().size(); v++) {
Portal p = (Portal) level.getLevelPortals().get(v);
coords.x = p.getPhysics().getPosition().getX();
coords.y = p.getPhysics().getPosition().getY();
worldToMini(coords);
if (p instanceof LevelEntrance) {
g2d.setColor(Color.yellow);
g2d.drawString("V",
coords.x + getBounds().x,
coords.y + getBounds().y);
} else if (p instanceof LevelExit) {
g2d.setColor(Color.green);
g2d.drawString("H",
coords.x + getBounds().x,
coords.y + getBounds().y);
} else {
g2d.setColor(Color.red);
g2d.drawString("X",
coords.x + getBounds().x,
coords.y + getBounds().y);
}
}
return true;
}
}

View File

@@ -0,0 +1,155 @@
package edu.gatech.cs2335.lemmings.networking;
import java.util.List;
/**
* Class AbstractMessage: This class is the base for all network
* messages that will be sent between the client and the server. All
* of the more specific message classes will be derived from it and
* will add more and more spcific functionality onto the basics
* provided in this class.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 8, 2004) - Created the AbstractMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@mail.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Apr 8, 2004
*/
public abstract class AbstractMessage {
/**
* This variable contains the four-character code for the message,
* like: HELO, or QUIT.
*/
private String messageType;
/**
* This string contains the name of the sender of this message,
* which will either be "Server" for server messages, or the name of
* the user for clients.
*/
private String messageAuthor;
/**
* Creates a new <code>AbstractMessage</code> instance.
*/
public AbstractMessage() {
this(MessageSettings.TYPE_UNKNOWN);
}
/**
* Creates a new <code>AbstractMessage</code> instance.
*
* @param type a <code>String</code> value
*/
public AbstractMessage(final String type) {
this(type, null);
}
/**
* Creates a new <code>AbstractMessage</code> instance.
*
* @param type a <code>String</code> value
* @param author a <code>String</code> value
*/
public AbstractMessage(final String type, final String author) {
messageType = type;
messageAuthor = author;
}
/**
* Returns the message type.
*
* @return a <code>String</code> value
*/
public final String getMessageType() {
return messageType;
}
/**
* Sets a new message type.
*
* @param newType a <code>String</code> value
*/
protected final void setMessageType(final String newType) {
this.messageType = newType;
}
/**
* Returns the message type of the message string passed in.
*
* @param message a <code>String</code> value
* @return a <code>String</code> value
*/
public static String getMessageType(final String message) {
if (message == null) {
return null;
}
if (message.length() < MessageSettings.TYPE_LENGTH) {
return null;
}
if (message.indexOf("\037".charAt(0)) != MessageSettings.TYPE_LENGTH) {
return null;
}
return message.substring(0, MessageSettings.TYPE_LENGTH);
}
/**
* Returns the name of the user that sent out the message.
*
* @return a <code>String</code> value
*/
public final String getAuthor() {
return messageAuthor;
}
/**
* Gives message author a new value.
*
* @param author a <code>String</code> value
*/
public final void setAuthor(final String author) {
this.messageAuthor = author;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public abstract String tokenize();
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
return null;
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
return null;
}
}

View File

@@ -0,0 +1,125 @@
package edu.gatech.cs2335.lemmings.networking;
import java.util.List;
import java.util.StringTokenizer;
/**
* Class ChangeLemmingFlowMessage: Sent to change lemming flow.
*
* <PRE>
* Revision History:
* v1.0 (April 08, 2004) - Created the ChangeSpeedMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg284h@mail.gatech.edu">Andrew Knight</A>
* @version Version 1.0,April 08 , 2004
*/
public class ChangeLemmingFlowMessage extends AbstractMessage {
/**
* Speed associated with this message
*/
private int flowRate;
/**
* Creates a new <code>ChangeLemmingFlowMessage</code> instance.
*
* @param userName a <code>String</code> value
*/
public ChangeLemmingFlowMessage(final String userName) {
super(MessageSettings.TYPE_FLOW, userName);
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null || (data.size() != 2)) {
return null;
}
String name = (String) (data.get(0));
ChangeLemmingFlowMessage clfm = new ChangeLemmingFlowMessage(name);
clfm.setLemmingFlow(((Integer) data.get(1)).intValue());
return clfm;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public final String tokenize() {
String message = getMessageType() + "\037" + getAuthor()
+ "\037" + Integer.toString(flowRate);
return message;
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
String type = getMessageType(message);
String name;
StringTokenizer st = null;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.TYPE_FLOW)) {
return null;
}
st = new StringTokenizer(message, "\037");
//Message type:
if (!st.hasMoreTokens()) { return null; }
type = st.nextToken();
//Username:
if (!st.hasMoreTokens()) { return null; }
name = st.nextToken();
ChangeLemmingFlowMessage clfm = new ChangeLemmingFlowMessage(name);
if (!st.hasMoreTokens()) { return null; }
try {
clfm.setLemmingFlow(Integer.parseInt(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
return clfm;
}
/**
* Accessor for speed int
* @return int , speed
*/
public int getLemmingFlow() {
return flowRate;
}
/**
* Modifier for speed int
* @param flow , int for speed
*/
public void setLemmingFlow(int flow) {
this.flowRate = flow;
}
}

View File

@@ -0,0 +1,123 @@
package edu.gatech.cs2335.lemmings.networking;
import java.util.List;
import java.util.StringTokenizer;
/**
* Class ChangeSpeedMessage: Sent to change speed of game.
*
* <PRE>
* Revision History:
* v1.0 (April 08, 2004) - Created the ChangeSpeedMessage class.
* </PRE>
*
* @author <A HREF="mailto:gtg284h@mail.gatech.edu">Andrew Knight</A>
* @version Version 1.0,April 08 , 2004
*/
public class ChangeSpeedMessage extends AbstractMessage {
/**
* Speed associated with this message.
*/
private int speed;
/**
* Creates a new <code>ChangeSpeedMessage</code> instance.
*
* @param userName a <code>String</code> value.
*/
public ChangeSpeedMessage(final String userName) {
super(MessageSettings.TYPE_SPEED, userName);
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value.
* @return an <code>AbstractMessage</code> value.
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null || (data.size() != 2)) {
return null;
}
String name = (String) (data.get(0));
ChangeSpeedMessage csm = new ChangeSpeedMessage(name);
csm.setSpeed(((Integer) data.get(1)).intValue());
return csm;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public final String tokenize() {
String message = getMessageType() + "\037" + getAuthor()
+ "\037" + Integer.toString(speed);
return message;
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
String type = getMessageType(message);
String name;
StringTokenizer st = null;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.TYPE_SPEED)) {
return null;
}
st = new StringTokenizer(message, "\037");
//Message type:
if (!st.hasMoreTokens()) { return null; }
type = st.nextToken();
//Username:
if (!st.hasMoreTokens()) { return null; }
name = st.nextToken();
ChangeSpeedMessage csm = new ChangeSpeedMessage(name);
if (!st.hasMoreTokens()) { return null; }
try {
csm.setSpeed(Integer.parseInt(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
return csm;
}
/**
* Accessor for speed int.
* @return int , speed
*/
public int getSpeed() {
return speed;
}
/**
* Modifier for speed int.
* @param s , int for speed
*/
public void setSpeed(int s) {
this.speed = s;
}
}

View File

@@ -0,0 +1,192 @@
package edu.gatech.cs2335.lemmings.networking;
//import java.security.InvalidParameterException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
//import java.io.*;
//import java.net.*;
import java.io.IOException;
//import java.io.PrintWriter;
//import java.io.OutputStreamWriter;
//import java.util.List;
//import java.util.Vector;
//import java.util.Date;
/**
*
*/
public class ConnectionListener implements Runnable {
/**
*
*/
private int port;
/**
*
*/
private long timeout;
/**
* This variable is used to track whether the server is still
* alive. When it dies, it will simply set the serverQuit variable
* to true and this thread will terminate.
*
*/
private boolean clientQuit;
/**
*@param port p
*@param timeout t
*/
public ConnectionListener(int port, long timeout) {
this.port = port;
this.timeout = timeout;
this.clientQuit = false;
}
/**
* main thread establishes a server socket and accepts client
*/
public final void run() {
clientQuit = false;
ServerSocket serverSocket = null;
Socket clientSocket = null;
//opening a server socket
try {
serverSocket = new ServerSocket(port);
} catch (IOException ioe) {
System.err.println("ConnectionListener:"
+ " Could not open server socket on port "
+ port + "\n");
System.err.println("IOException: " + ioe.getMessage() + "\n");
return;
}
//Check if the socket is valid.
if (serverSocket == null || !serverSocket.isBound()) {
System.err.println("ConnectionListener: got bad socket \n");
return;
}
//keep waiting for connections
try {
while (!clientQuit) {
clientSocket = serverSocket.accept();
}
} catch (IOException ioe) {
System.err.println("ConnectionListener: Couldn't accept connection: \n"
+ ioe.getMessage());
}

View File

@@ -0,0 +1,90 @@
package edu.gatech.cs2335.lemmings.networking;
import java.util.List;
import java.util.StringTokenizer;
/**
* Class IdentificationMessage: Sends username to other player.
*
* <PRE>
* Revision History:
* v1.0 (April 08, 2004) - Created the IdentifiationMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg284h@mail.gatech.edu">Andrew Knight</A>
* @version Version 1.0,April 08 , 2004
*/
public class IdentificationMessage extends AbstractMessage {
/**
* Creates a new <code>IdentificationMessage</code> instance.
*
* @param userName a <code>String</code> value
*/
public IdentificationMessage(final String userName) {
super(MessageSettings.TYPE_HELO, userName);
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null || (data.size() != 1)) {
return null;
}
return new IdentificationMessage((String) (data.get(0)));
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public final String tokenize() {
String message = getMessageType() + "\037" + getAuthor();
return message;
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
String type = getMessageType(message);
String name;
StringTokenizer st = null;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.TYPE_HELO)) {
return null;
}
st = new StringTokenizer(message, "\037");
//Message type:
if (!st.hasMoreTokens()) { return null; }
type = st.nextToken();
//Username:
if (!st.hasMoreTokens()) { return null; }
name = st.nextToken();
IdentificationMessage im = new IdentificationMessage(name);
return im;
}
}

View File

@@ -0,0 +1,393 @@
package edu.gatech.cs2335.lemmings.networking;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.security.InvalidParameterException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.Vector;
//import java.util.Date;
import java.net.Socket;
//import java.net.ServerSocket;
import edu.gatech.cs2335.lemmings.graphics.Renderer;
import edu.gatech.cs2335.lemmings.gui.GamePlayPanel;
import edu.gatech.cs2335.lemmings.engine.Level;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
import edu.gatech.cs2335.lemmings.engine.GamePlayState;
import java.awt.Rectangle;
import edu.gatech.cs2335.lemmings.physics.Point;
//import edu.gatech.cs2335.lemmings.gui.GamePlayPanel;
/**
*
*/
public class LemmingConnection {
/**
* Contains the socket on which we want to listen to the server.
*/
private Socket socket;
/**
* A variable to keep track of when we should stop listening to the
* server.
*/
private boolean clientQuit;
/**
*update
*/
private static final long UPDATE_FREQUENCY = 1;
/**
*update delay
*/
private static final long UPDATE_DELAY = (long) (UPDATE_FREQUENCY / 1000.0f);
/**
*verbose
*/
private static final boolean VERBOSE = false;
/**
* Creates a new <code>LemmingConnection</code> instance.
* @param s a <code>Socket</code> value
*/
public LemmingConnection(Socket s) {
socket = s;
if (!socket.isBound()) {
throw new InvalidParameterException("LemmingConnection: "
+ "Server socket not bound!\n");
}
clientQuit = true;
}
/**
* Describe <code>startListening</code> method here.
*
*/
public void startListening() {
if (VERBOSE) {
System.out.println("LemmingConnection: Started listening...");
System.out.flush();
}
clientQuit = false;
new Thread(new Runnable() {
public void run() {
String message = null;
BufferedReader br = null;
//Open reader:
try {
br = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
} catch (IOException ioe) {
GameEngine.getInstance()
.showNetworkError("Could not open connection to other player.");
return;
}
while (!clientQuit) {
//Get next message:
try {
message = br.readLine();
if (message == null) {
throw new IOException("Server has dropped the connection.");
}
} catch (IOException ioe) {
GameEngine.getInstance()
.showNetworkError("Other player dropped connection...");
return;
}
//send message to the LemmingNetworking
LemmingNetworking.getInstance().receiveMessage(message);
if (VERBOSE) {
System.out.println("LemmingConnection: Got message " + message);
System.out.flush();
}
if (clientQuit) {
break;
}
}
}
}).start();
}
/**
* Describe <code>startSending</code> method here.
*
*/
public void startSending() {
if (VERBOSE) {
System.out.println("LemmingConnection: Started sending...");
System.out.flush();
}
clientQuit = false;
new Thread(new Runnable() {
public void run() {
long lastUpdate = System.currentTimeMillis();
if (VERBOSE) {
System.out.println("In Sending Thread. Starting While loop");
}
while (!clientQuit) {
AbstractMessage am = getUpdateMessage();
if (VERBOSE) {
System.out.println("Got the Update(stats)Message");
}
// if (((GamePlayState) GameEngine.getInstance().getCurrentState())
// .getGamePlayPanel().getRenderer() != null) {
// AbstractMessage full = getFullUpdateMessage();
// if (VERBOSE) {
// System.out.println("got FullUpdateMessage");
// }
// if (full == null) {
// continue;
// }
// if (VERBOSE) {
// System.out.println("Full = " + full.tokenize());
// }
// sendMessage(full.tokenize());
// }
if (am == null) {
continue;
}
sendMessage(am.tokenize());
if (VERBOSE) {
System.out.println("LemmingConnection: sent message "
+ am.tokenize());
System.out.flush();
}
//Sleep some:
lastUpdate += UPDATE_DELAY;
try {
Thread.sleep(Math
.max(0, lastUpdate
- System.currentTimeMillis()));
} catch (Exception e) {
continue;
} finally {
lastUpdate = System.currentTimeMillis();
}
}
}
}).start();
}
/**
*
*@return boolean
*/
public boolean getClientQuit() {
return clientQuit;
}
/**
*mutator
*@param value v
*/
public void setClientQuit(boolean value) {
clientQuit = value;
}
/**
* Describe <code>getUpdateMessage</code> method here.
*
* @return an <code>AbstractNetworkMessage</code> value
*/
public AbstractMessage getUpdateMessage() {
GamePlayState gps = null;
try {
gps = (GamePlayState) GameEngine.getInstance().
getCurrentState();
} catch (ClassCastException cce) {
return null;
}
Level l = gps.getLevel();
int lt = l.getNumLemmings();
int lr = lt - l.getPendingLemmings().size();
int ls = l.getLemmingsSaved();
int ld = l.getActiveLemmings().size();
List data = new Vector();
data.add("nUb");
data.add(new Integer(lr));
data.add(new Integer(ls));
data.add(new Integer(ld));
data.add(new Integer(lt));
if (VERBOSE) {
System.out.println("LC: ");
System.out.println("\tPending: "
+ l.getPendingLemmings().size());
System.out.println("\tActive: "
+ l.getActiveLemmings().size());
System.out.println("\tKilled: "
+ l.getLemmingHeaven().size());
System.out.flush();
}
return MessageFactory.getInstance()
.createMessage(MessageSettings.TYPE_STAT, data);
}
/**
*@return abstract message
*/
public AbstractMessage getFullUpdateMessage() {
if (VERBOSE) {
System.out.println("LemmingConnection"
+ " inside getFullUpdateMessage");
}
GamePlayState gps = null;
gps = (GamePlayState) GameEngine.getInstance()
.getCurrentState();
//Getting local data
Level l = gps.getLevel();
if (VERBOSE) {
System.out.println("LemmingConnection"
+ " getFullUpdateMessage gps.getLevel()" + l);
}
List lems = l.getActiveLemmings();
if (VERBOSE) {
System.out.println("LemmingConnection"
+ " getFullUpdateMessage gps.ActiveLemmings()"
+ lems.size());
}
int savedLemmings = l.getLemmingsSaved();
GamePlayPanel gpp = gps.getGamePlayPanel();
if (VERBOSE) {
System.out.println("LemmingConnection "
+ " getFullUpdateMessage gps.getGamePlayPanel()"
+ gpp);
}
Renderer rend = gpp.getRenderer();
if (VERBOSE) {
System.out.println("LemmingConnection "
+ " getFullUpdateMessage gpp.getRenderer()" + rend);
}
Rectangle rect = rend.getMapViewPort();
if (VERBOSE) {
System.out.println("LemmingConnection "
+ " getFullUpdateMessage rend.getMapViewPort()"
+ rect);
}
int x = (int) rect.getX();
int y = (int) rect.getY();
Point point = new Point();
point.setX(x);
point.setY(y);
//Making list to build message from.
List data = new Vector();
data.add("nUb"); //Name
data.add(point); //Viewpoint
data.add(new Integer(savedLemmings)); //Num Lemmings saved
//Num Lemmings out
data.add(lems); //Added Lemmings to data.
//data.add(terr); //terrain updates
if (VERBOSE) {
System.out.println("LC: ");
System.out.println("\tSaved: "
+ Integer.toString(l.getLemmingsSaved()));
System.out.println("\tViewpoint x " + x);
System.out.println("\tViewpoint y " + y);
System.out.println("\tNumberActive: "
+ l.getActiveLemmings().size());
System.out.flush();
}
return MessageFactory.getInstance()
.createMessage(MessageSettings.TYPE_UPDATE, data);
}
/**Sends the abstract message to the client,
*@param msg message
*@return success/fail
*/
public boolean sendMessage(String msg) {
PrintWriter pr = null;
//Get output stream:
try {
pr = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException ioe) {
GameEngine.getInstance()
.showNetworkError("Could not send message to other player.");
return false;
}
//Send message:
pr.println(msg);
pr.flush();
return true;
}
/**
*Stops the LemmingsConnection
*/
public void closeConnection() {
clientQuit = true;
}
}

View File

@@ -0,0 +1,219 @@
package edu.gatech.cs2335.lemmings.networking;
import java.io.IOException;
//import java.io.PrintWriter;
//import java.io.OutputStreamWriter;
//import java.util.List;
//import java.util.PhysicsVector;
//import java.util.Date;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import edu.gatech.cs2335.lemmings.engine.GameEngine;
/**
* The <code>LemmingNetworking</code> class is the facade to the
* entire package.
*
* @author <a href="mailto:gtg563g@gatech.edu">Daniyar Zhanbekov</a>
* @version 1.0
*/
public final class LemmingNetworking {
/**
* Show debug output.
*/
private static final boolean VERBOSE = false;
/**
* Implement as a singleton.
*/
private static LemmingNetworking instance = new LemmingNetworking();
/**
* Lemming connection.
*/
private LemmingConnection connection;
/**
* Singleton implementation.
*
* @return an <code>LemmingNetworking</code> value
*/
public static LemmingNetworking getInstance() {
return instance;
}
/**
* Describe <code>listenForConnection</code> method here.
*
* @param port an <code>int</code> value
*@return boolean
*/
public boolean listenForConnection(int port) {
ServerSocket serverSocket = null;
Socket clientSocket = null;
try {
serverSocket = new ServerSocket(port);
} catch (IOException ioe) {
GameEngine.getInstance()
.showNetworkError("Could not open server socket on port "
+ port);
return false;
}
//Check if the socket is valid.
if (serverSocket == null || !serverSocket.isBound()) {
GameEngine.getInstance().showNetworkError("Could not use socket for "
+ "connection.");
return false;
}
try {
clientSocket = serverSocket.accept();
} catch (IOException ioe) {
return false;
}
connection = new LemmingConnection(clientSocket);
return true;
}
/**
* Describe <code>connectTo</code> method here.
*
* @param host a <code>String</code> value
* @param port an <code>int</code> value
* @return a <code>boolean</code> value
*/
public boolean connectTo(String host, int port) {
Socket clientSocket = null;
boolean connected = false;
try {
//opening a client socket
clientSocket = new Socket(host, port);
} catch (UnknownHostException e) {
GameEngine.getInstance().showNetworkError("Could not find host: \""
+ host + "\"");
connected = false;
return false;
} catch (IOException e) {
GameEngine.getInstance().showNetworkError("Could not establish "
+ "connection to host: \""
+ host + "\"");
connected = false;
return false;
}
//since the socket is ok, starting a LemmingConnection
connection = new LemmingConnection(clientSocket);
return true;
}
/**
* Describe <code>shutDownNetworking</code> method here.
*
* @return a <code>boolean</code> value
*/
public boolean shutDownNetworking() {
if (connection != null) {
connection.closeConnection();
}
return true;
}
/**
* Describe <code>isConnected</code> method here.
*
* @return a <code>boolean</code> value
*/
public boolean isConnected() {
if (connection == null) {
return false;
}
return !connection.getClientQuit();
}
/**
* Describe <code>sendMessage</code> method here.
*
* @param message an <code>AbstractMessage</code> value
* @return a <code>boolean</code> value
*/
public boolean sendMessage(AbstractMessage message) {
String stringMessage = message.tokenize();
if (stringMessage == null) {
GameEngine.getInstance().showNetworkError("Could not send message to "
+ "other player. Connection"
+ " dropped.");
return false;
}
//call the LemmingConnection
connection.sendMessage(stringMessage);
return true;
}
/**
* Describe <code>receiveMessage</code> method here.
*
* @param message a <code>String</code> value
*/
protected void receiveMessage(String message) {
AbstractMessage msg = null;
if (VERBOSE) {
System.out.println("Got Message:");
System.out.println("\t" + message + "\n");
System.out.flush();
}
msg = MessageFactory.getInstance().parseMessage(message);
if (msg == null) {
//Got bad message format
/*Who gives a shit?
GameEngine.getInstance()
.showNetworkError("Received malicious message from other player. "
+ "Connection dropped.");
*/
return;
}
//forward the parsed message to the GameEngine
GameEngine.getInstance().receiveMessage(msg);
}
/**
* Describe <code>getConnection</code> method here.
*
* @return a <code>LemmingConnection</code> value
*/
public LemmingConnection getConnection() {
return connection;
}
/**
* Describe <code>getConnection</code> method here.
*
* @param client a <code>LemmingConnection</code> value
*/
protected void acceptConnection(Socket client) {
connection = new LemmingConnection(client);
}
}

View File

@@ -0,0 +1,49 @@
package edu.gatech.cs2335.lemmings.networking;
import java.util.List;
/**
*
*/
public class LevelFinishMessage extends AbstractMessage {
/**
* Creates a new <code>IdentificationMessage</code> instance.
*
* @param userName a <code>String</code> value
*/
public LevelFinishMessage(final String userName) {
super(MessageSettings.TYPE_LEVELEND, userName);
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {

View File

@@ -0,0 +1,187 @@
package edu.gatech.cs2335.lemmings.networking;
import java.util.List;
/**
* Class MessageFactory: Creates messages.
*
* <PRE>
* Revision History:
* v1.0 (Apr. 8, 2004) - Created the MessageFactory class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@mail.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 26, 2004
*/
public final class MessageFactory {
/**
* Implement as a singleton.
*/
private static MessageFactory instance;
/**
* Creates a new <code>MessageFactory</code> instance.
*/
private MessageFactory() { }
/**
* Singleton implementation.
*
* @return a <code>MessageFactory</code> value
*/
public static MessageFactory getInstance() {
if (instance == null) {
instance = new MessageFactory();
}
return instance;
}
/**
* Parses the string passed in and returns the appropriate network message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
public AbstractMessage parseMessage(final String message) {
String type = AbstractMessage.getMessageType(message);
if (type == null) {
return null;
}
if (type.equals(MessageSettings.TYPE_PAUSE)) {
return PauseMessage.parse(message);
}
if (type.equals(MessageSettings.TYPE_UPDATE)) {
return UpdateMessage.parse(message);
}
if (type.equals(MessageSettings.TYPE_SPEED)) {
return ChangeSpeedMessage.parse(message);
}
if (type.equals(MessageSettings.TYPE_STAT)) {
return StatsMessage.parse(message);
}
if (type.equals(MessageSettings.TYPE_NUKE)) {
return NukeRequestMessage.parse(message);
}
if (type.equals(MessageSettings.TYPE_FLOW)) {
return ChangeLemmingFlowMessage.parse(message);
}
if (type.equals(MessageSettings.TYPE_HELO)) {
return IdentificationMessage.parse(message);
}
if (type.equals(MessageSettings.TYPE_LEVELEND)) {
return LevelFinishMessage.parse(message);
}
return null;
}
/**
* Builds a network message class from a list of data.
*
* @param messageType a <code>String</code> value
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
public AbstractMessage createMessage(final String messageType,
final List data) {
if (messageType == null
|| (messageType.length() != MessageSettings.TYPE_LENGTH)) {
return null;
}
if (messageType.equals(MessageSettings.TYPE_PAUSE)) {
return PauseMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.TYPE_UPDATE)) {
return UpdateMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.TYPE_SPEED)) {
return ChangeSpeedMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.TYPE_STAT)) {
return StatsMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.TYPE_NUKE)) {
return NukeRequestMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.TYPE_FLOW)) {
return ChangeLemmingFlowMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.TYPE_HELO)) {
return IdentificationMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.TYPE_LEVELEND)) {
return LevelFinishMessage.buildMessage(data);
}
return null;
}
/**
* Creates a message of the specified type with the default
* parameters. The message returned is basically considered empty.
*
* @param messageType a <code>String</code> value
* @param authorName a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
public AbstractMessage makeBlankMessage(final String messageType,
final String authorName) {
if (messageType == null || messageType.equals("")) {
return null;
}
if (messageType.equals(MessageSettings.TYPE_PAUSE)) {
return new PauseMessage(authorName);
}
//if (messageType.equals(MessageSettings.TYPE_UPDATE)) {
// return new UpdateMessage(authorName);
//}
if (messageType.equals(MessageSettings.TYPE_STAT)) {
return new StatsMessage(authorName);
}
if (messageType.equals(MessageSettings.TYPE_SPEED)) {
return new ChangeSpeedMessage(authorName);
}
if (messageType.equals(MessageSettings.TYPE_NUKE)) {
return new NukeRequestMessage(authorName);
}
if (messageType.equals(MessageSettings.TYPE_FLOW)) {
return new ChangeLemmingFlowMessage(authorName);
}
if (messageType.equals(MessageSettings.TYPE_HELO)) {
return new IdentificationMessage(authorName);
}
if (messageType.equals(MessageSettings.TYPE_LEVELEND)) {
return new LevelFinishMessage(authorName);
}
return null;
}
}

View File

@@ -0,0 +1,80 @@
package edu.gatech.cs2335.lemmings.networking;
/**
* Class MessageSettings: This class contains the settings shared
* among all the different messages, as well as the MessageFactory.
*
* <PRE>
* Revision History:
* v1.0 (Apr 8, 2004) - Created the MessageSettings class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@mail.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Apr 8, 2004
*/
public final class MessageSettings {
/**
* Message type associated with all unknown messages.
*/
public static final String TYPE_UNKNOWN = "ABST";
/**
* Message type associated with stats update messages.
*/
public static final String TYPE_STAT = "STAT";
/**
* Message type associated with all pause messages.
*/
public static final String TYPE_PAUSE = "PAUS";
/**
* Message type associated with all update messages.
*/
public static final String TYPE_UPDATE = "UPDT";
/**
* Message type associated with all change speed messages.
*/
public static final String TYPE_SPEED = "SPED";

View File

@@ -0,0 +1,90 @@
package edu.gatech.cs2335.lemmings.networking;
import java.util.List;
import java.util.StringTokenizer;
/**
* Class NukeRequestMessage: Sent by host or client when Nuke is
* desired.
*
* <PRE>
* Revision History:
* v1.0 (April 08, 2004) - Created the NukeRequestMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg284h@mail.gatech.edu">Andrew Knight</A>
* @version Version 1.0,April 08 , 2004
*/
public class NukeRequestMessage extends AbstractMessage {
/**
* Creates a new <code>NukeRequestMessage</code> instance.
*
* @param userName a <code>String</code> value
*/
public NukeRequestMessage(final String userName) {
super(MessageSettings.TYPE_NUKE, userName);
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null || (data.size() != 1)) {
return null;
}
return new NukeRequestMessage((String) (data.get(0)));
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public final String tokenize() {
String message = getMessageType() + "\037" + getAuthor();
return message;
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
String type = getMessageType(message);
String name;
StringTokenizer st = null;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.TYPE_NUKE)) {
return null;
}
st = new StringTokenizer(message, "\037");
//Message type:
if (!st.hasMoreTokens()) { return null; }
type = st.nextToken();
//Username:
if (!st.hasMoreTokens()) { return null; }
name = st.nextToken();
return new NukeRequestMessage(name);
}
}

Some files were not shown because too many files have changed in this diff Show More