//ImageManager.java - draws all graphics, manages graphical content, // and handles the window and registers listeners package graphicsManager; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.geom.Line2D; import java.util.HashMap; import java.util.StringTokenizer; import java.util.Vector; import javax.swing.JFrame; import javax.swing.JPanel; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import utilities.*; import Core.Game; import Core.State; public class ImageManager extends JPanel implements MessageSystem, KeyListener, WindowListener { private static final long serialVersionUID = 1L; //add serial with ctrl+1 private Game game = null; //panel consts private int panelWidth = 800; private int panelHeight = 800; //gridboard sizes //TODO set gridsquare dynamically private int squaresWide = 6; private int squaresHigh = 6; private int gridsquareWidth = 100; private int gridsquareHeight = 100; private int distFromWallWide; private int distFromWallHigh; private int centerSquareX = 3 * gridsquareWidth + 1; private int centerSquareY = 3 * gridsquareHeight + 1; //colors private final Color defaultBkgColor = Color.white; private final Color gridlineColor = Color.black; private final Color pauseColor = Color.blue; private final Color pauseFontColor = Color.white; //text private final Color UIColor = Color.black; private Font missionFont = new Font("Sans Serif", Font.BOLD, 32); private Font scoreFont = new Font("Sans Serif", Font.BOLD, 14); private Font livesFont = new Font("Sans Serif", Font.BOLD, 14); private Font pauseText = new Font("Monospaced", Font.BOLD, 64); private final Color valueColor = Color.black; private Font valueFont = new Font ("Monospaced", Font.PLAIN, 14); //image directory private String imageDir = null; //image storage private HashMap imagePool; private Vector activeSprites; private HashMap splashPool; //state based switches private boolean mainscreen = true; private boolean playGame = false; //static subclasses & self private static ImageLoader imgLoader; public ImageManager() { } public void finalize() { //TODO unload all graphics, not just gameplay unload(); } /** * initializes and draws the window. also initializes the sub-managers. * this does not load sprites used to play a level! those are done in * loadLevel(...) * @param imgDir directory of where the images are located. absolute path plz */ public void initialize(Game g, String imgDir, int panelWidth, int panelHeight) { //dynamically switch between accelerations depending on OS String system = System.getProperty("os.name"); // switch on translucency acceleration in Windows if (system.startsWith("Windows")) { System.setProperty("sun.java2d.translaccel", "true"); } else { // if(system.startsWith("Mac")) { // switch on hardware acceleration if using OpenGL with pbuffers System.setProperty("sun.java2d.opengl", "true"); } //if params are too small, use defaults if (panelWidth < this.panelWidth) panelWidth = this.panelWidth; if (panelHeight < this.panelHeight) panelWidth = this.panelHeight; //center the gridboard distFromWallWide = (panelWidth //width of window - (gridsquareWidth * squaresWide)) //width of gridboard / 2; //for each side distFromWallHigh = (panelHeight //height of window - (gridsquareHeight * squaresHigh)) //width of gridboard / 2; //for each side //set defaults setBackground(Color.white); setPreferredSize( new Dimension(panelWidth, panelHeight) ); //spawn the window JFrame app = new JFrame("Munchers"); app.getContentPane().add(this, BorderLayout.CENTER); app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //present app.pack(); app.setResizable(false); app.setVisible(true); app.addKeyListener(this); app.addWindowListener(this); app.setFocusable(true); app.requestFocusInWindow(); //store panel variables this.panelHeight = panelHeight; this.panelWidth = panelWidth; this.setFocusable(false); //set the path of the image directory imageDir = imgDir; //initialize the image loader imgLoader = new ImageLoader(); //initialize the hash maps imagePool = new HashMap(); activeSprites = new Vector(); splashPool = new HashMap(); //store an instance of game game = g; //load splash screen loadSplashScreens("splashSprites.txt"); } /** * sets a new directory for drawing images. * @param dir absolute path. must include / at the end. */ public void setNewImageDirectory(String dir) { imageDir = dir; } /** * updates the locations of all the sprites and then redraws */ public void update() { //TODO write movement updates repaint(); } public Object query(short destination, short messageToSend, Object dataToSend) { //check destination if (destination != Flag.IMAGE) return game.query(destination, messageToSend, dataToSend); //send out proper messages switch (messageToSend) { case Flag.I_MOVE_SPRITE: return moveSprite((DoublePoint)dataToSend, false); case Flag.I_MOVE_PLAYER: return moveSprite((DoublePoint)dataToSend, true); case Flag.I_SPAWN_POOGIE: return addPoogie((SpriteQueryInfo)dataToSend); case Flag.I_REMOVE_POOGIE: return removePoogie((Point)dataToSend); case Flag.I_RESPAWN_PLAYER: return null; //TODO respawn algorithm //state-based cases case Flag.I_DRAW_NORMAL_GAMEPLAY: //set normal gameplay mainscreen = false; playGame = true; return null; case Flag.I_DRAW_MAIN_SPLASH: //set paramaters to draw the splash mainscreen = true; playGame = false; return null; } return null; } //----------- LOADING / UNLOADING -------------- // /** * * @param levelParamFile location of the file where the level data is * @return true if the level was loaded successfully. false * if the file did not exist or level did not load properly. */ public boolean loadLevel(String levelParamFile) { String imsFNm = imageDir + levelParamFile; //System.out.println("Reading file: " + levelParamFile); try { InputStream inStream = this.getClass().getResourceAsStream(imsFNm); BufferedReader buffReader = new BufferedReader( new InputStreamReader(inStream)); // BufferedReader br = new BufferedReader( new FileReader(imsFNm)); String line; while((line = buffReader.readLine()) != null) { if (line.length() == 0) // blank line continue; if (line.startsWith("//")) // comment continue; else loadImageFromFile(line); //this section can be used to divide image loading //when animation and such is implemented } buffReader.close(); } catch (IOException e) { System.out.println("Error reading file: " + imsFNm); System.exit(1); } //load the player image into the active image pool if(imagePool.containsKey("PLAYER")) { imagePool.get("PLAYER").setLocation(centerSquareX, centerSquareY); activeSprites.add(imagePool.get("PLAYER")); } else if(imagePool.containsKey("HOLDER")) { imagePool.get("HOLDER").setLocation(centerSquareX, centerSquareY); activeSprites.add(imagePool.get("HOLDER")); } else { System.out.println("ERROR: No player or holder image found."); System.exit(1); } //System.out.println("Player at: " + imagePool.get("PLAYER").getX() // + "," + imagePool.get("PLAYER").getY()); //activate level playGame=true; return true; } /** * * @param line line from the file. contains an ID and the image name to load * @return false if the file couldn't be loaded */ private boolean loadImageFromFile(String line) { StringTokenizer token = new StringTokenizer(line); String ID; //test id parameter if (!testIDforValidity(line)) { System.out.println("Contains invalid ID parameter: " + line); return false; } //check length if(token.countTokens() != 2) { System.out.println("Wrong number of arguments: " + line); return false; } //retrieve information from file ID = token.nextToken(); //test to see if id has been used if(imagePool.containsKey(ID)) { System.out.println("ID " + ID + " already in use."); return false; } //System.out.println("Loading image for " + ID); //add id & image to image pool imagePool.put(ID, imgLoader.loadImageFromFile(imageDir + token.nextToken())); //if nothing fails, return true; } /** * loads the splash screens. currently just the opening splash * @param levelParamFile file containing the splashes * @return true if file could load */ public boolean loadSplashScreens(String levelParamFile) { String imsFNm = imageDir + levelParamFile; //System.out.println("Reading file: " + levelParamFile); try { InputStream inStream = this.getClass().getResourceAsStream(imsFNm); BufferedReader buffReader = new BufferedReader( new InputStreamReader(inStream)); String line; while((line = buffReader.readLine()) != null) { if (line.length() == 0) // blank line continue; if (line.startsWith("//")) // comment continue; else loadSplashFromFile(line); } buffReader.close(); } catch (IOException e) { System.out.println("Error reading file: " + imsFNm); System.exit(1); } return true; } /** * load the splash and store it in the splashImages table * @param line file line where the image and it's ID are listed * @return true if file loads properly */ private boolean loadSplashFromFile(String line) { StringTokenizer token = new StringTokenizer(line); String ID; //test id parameter if (!testIDforValidity(line)) { System.out.println("Contains invalid ID parameter: " + line); return false; } //check length if(token.countTokens() != 2) { System.out.println("Wrong number of arguments: " + line); return false; } //retrieve information from file ID = token.nextToken(); //test to see if id has been used if(splashPool.containsKey(ID)) { System.out.println("ID " + ID + " already in use."); return false; } //System.out.println("Loading image for " + ID); //add id & image to image pool splashPool.put(ID, imgLoader.loadImageFromFile(imageDir + token.nextToken())); //if nothing fails, return true; } /** * */ public void unload() { //flush spriteimages to free the resources //flush active array int arrayMax = activeSprites.size(); for (int i = 0; i < arrayMax; i++) activeSprites.get(i).flush(); //clear the maps for reloading imagePool.clear(); activeSprites.clear(); } //-------------- STATE-BASED ----------------- // public void addMenu() { } public boolean removeMenu() { return true; } //------------ PAINTING -------------------- // /** * Paints the active components to the screen. * Overloaded. * @param g Graphics component. Required */ public void paintComponent(Graphics g) { super.paintComponents(g); Graphics2D g2d = (Graphics2D)g; //use anti-aliasing g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); //clear the background g2d.setColor(defaultBkgColor); g2d.fillRect(0, 0, panelWidth, panelHeight); //---- if no special drawings, draw game normally ---- // if(!mainscreen && playGame) { //draw background if(imagePool.containsKey("BACKGROUND")) //background is drawn separately to retain layering drawImage(g2d, imagePool.get("BACKGROUND")); else { g2d.setColor(defaultBkgColor); g2d.fillRect(0, 0, panelWidth, panelHeight); } //draw gridboard drawGridboard(g2d); //draw values in the squares //set up graphics device g2d.setColor(valueColor); g2d.setFont(valueFont); //send graphics device to gridboard query(Flag.GRIDBOARD, Flag.GB_DRAW_VALUES, g2d); //display active images int arrayMax = activeSprites.size(); for (int i = 0; i < arrayMax; i++) drawImage(g2d, activeSprites.get(i)); //draw UI drawUI(g2d); //draw pause or other events, if applicable. if (game.isPaused()) { drawPauseUI(g2d); } } // ---- /normal drawing -----// else if (mainscreen) { //draw the splash screen if(splashPool.containsKey("MAIN_SPLASH")) //background is drawn separately to retain layering drawImage(g2d, splashPool.get("MAIN_SPLASH")); else { g2d.setColor(defaultBkgColor); g2d.fillRect(0, 0, panelWidth, panelHeight); } } //allow the states to draw something too game.query(Flag.GAME, Flag.G_STATE_DRAW, g2d); } /** * draws a sprite image to the active graphics device * @param g2d instance of the graphics2d device in use * @param spriteImg image to be drawn */ private void drawImage(Graphics2D g2d, SpriteImage spriteImg) { if (spriteImg == null) { //create a dummy image //TODO create a holder image? g2d.setColor(Color.black); g2d.fillRect(spriteImg.getX(), spriteImg.getY(), 20, 20); g2d.setColor(Color.white); g2d.drawString("??", spriteImg.getX()+10, spriteImg.getY()+10); } else g2d.drawImage(spriteImg.getImage(), spriteImg.getX(), spriteImg.getY(), this); } /** * draws the lines of the gridboard * @param g2d instance of the graphics2d object to draw to */ private void drawGridboard(Graphics2D g2d) { g2d.setPaint(gridlineColor); //iterators int vertical = distFromWallWide; int horizontal = distFromWallHigh; //for line calculations double horizTop = (double)horizontal; double horizBottom = (double)(panelHeight - distFromWallHigh); double vertTop = (double) vertical; double vertBottom = (double)(panelWidth - distFromWallWide); Line2D.Double line; //draw vertical lines while (vertical <= panelWidth - distFromWallWide) { line = new Line2D.Double(vertical, horizTop, //top coordinate vertical, horizBottom); g2d.draw(line); vertical = vertical + gridsquareWidth; } //draw horizontal lines while (horizontal <= panelHeight - distFromWallHigh) { line = new Line2D.Double( vertTop, horizontal, //top coordinate vertBottom, horizontal); g2d.draw(line); horizontal = horizontal + gridsquareWidth; } } /** * draws the UI text and graphics * @param g2d active graphics2d device */ private void drawUI(Graphics2D g2d) { //draw mission g2d.setColor(UIColor); g2d.setFont(missionFont); g2d.drawString((String)game.query(Flag.VALUE, Flag.V_GET_TOPIC, null), distFromWallWide + 20, distFromWallHigh - 10); //draw score g2d.setFont(scoreFont); g2d.drawString("Score: " + query(Flag.ENTITY, Flag.E_GET_SCORE, null) , panelWidth-250, distFromWallHigh - 10); //draw lives g2d.setFont(livesFont); g2d.drawString("Lives: " + query(Flag.ENTITY, Flag.E_GET_PLAYER_LIVES, null) , panelWidth-160, panelHeight-80); } /** * draws player notice that game is paused * @param g2d graphics device in use */ private void drawPauseUI(Graphics2D g2d) { int rectX = 300; int rectheight = 100; int fontYstart = 220; //draw bar g2d.setColor(pauseColor); g2d.fillRect(0, rectX, panelWidth+1, rectheight); //draw text g2d.setColor(pauseFontColor); g2d.setFont(pauseText); g2d.drawString("P A U S E", fontYstart, rectX+ (3*rectheight/4)); } //------------- IMG MANIP ----------------- // /** used to move the sprites during gameplay. * @param spriteName name of the sprite. used as a key in the master hash table * @param newX where the sprite will be moved * @param newY where the sprite will be moved * @return returns true if the sprite was added, false if there's a problem */ private boolean moveSprite(DoublePoint dp, boolean includePlayer) { //find the matching sprite int index; //get index if (includePlayer) index = 0; else index = findSpriteIndex(dp.getOldPoint().x, dp.getOldPoint().y, false); //throw error if (index == -1) { System.out.println(" ERROR: Bad query call." + "Cannot move non-existent sprite. Check calling function."); return false; } //move the matching sprite activeSprites.get(index).setLocation(dp.getNewPoint().x, dp.getNewPoint().y); return true; } /** * this adds a sprite to the active drawing list, * pulling the info from the master sprite list * @return */ public boolean addPoogie(SpriteQueryInfo sqi) { //extract necessary info String imgPoolKey = sqi.getSpriteID(); int x = sqi.getX(); int y = sqi.getY(); //test id for validity if(!testIDforValidity(imgPoolKey)) { System.out.println("Invalid key: " + imgPoolKey); return false; } //check for image and load if (imagePool.containsKey(imgPoolKey)) activeSprites.add(imagePool.get(imgPoolKey)); else if(imagePool.containsKey("HOLDER")) activeSprites.add(imagePool.get("HOLDER")); else { System.out.println("image " + imgPoolKey + " does not exist."); return false; } //set location to spawn activeSprites.lastElement().setLocation(x, y); return true; } /** * Remove an active sprite from the list * @return true if sprite was found, false if sprite was not on the list */ public boolean removePoogie(Point location) { int index = findSpriteIndex(location.x, location.y, false); //this search excludes the player sprite if (index == -1) { System.out.println(" ERROR: Bad query call." + "Cannot remove non-existent Poogie. Check calling function."); return false; } //remove the entity activeSprites.remove(index); return true; } //--- tools ---// /** * Tests an identifier from the image file to see if it matches * any identifiers in the approved ID list. this is crucial as the * approved list is how the game pulls images from the pool into * the active drawing list. * @param line identifier pulled from file * @return true if ID is found on list */ private boolean testIDforValidity(String line) { //check for splash screens if (line.startsWith("MAIN_SPLASH")) { return true; } //check for playable sprites else if (line.startsWith("PLAYER") || line.startsWith("FRAIDIE") || line.startsWith("HOLDER") || line.startsWith("MESSIE") || line.startsWith("MUNCHIE") || line.startsWith("NORMIE") || line.startsWith("OMNOMMIE") || line.startsWith("SWEEPIE") || line.startsWith("BACKGROUND")) { return true; } else { return false; } } /** * searches active drawing array for a matching sprite * @param x x-coord of the sprite * @param y y-coord of the sprite * @param includePlayer include player in the return or not? * @return the index in the array of where to find matching player. -1 if not found. */ private int findSpriteIndex(int x, int y, boolean includePlayer) { //check array size if (activeSprites.size() <= 0) return -1; //make a temporary sprite SpriteImage sprite = new SpriteImage(null, x, y); for (int i = 0; i < activeSprites.size(); i++) { if (sprite.equals(activeSprites.get(i))) { if (!includePlayer && i == 0) continue; //skip player index if includePlayer is false. return i; //return the index } } //if all else fails, return a null return -1; } //-------- KEY EVENTS ---------- // public void keyPressed(KeyEvent e) { State currentState; currentState = (State)game.query(Flag.GAME, Flag.G_GET_STATE, null); currentState.handleKeyPress(e, game); } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } //------- WINDOWS EVENTS -------// public void windowActivated(WindowEvent e) { query(Flag.GAME, Flag.G_UNPAUSE, null); } public void windowDeactivated(WindowEvent e) { query(Flag.GAME, Flag.G_PAUSE, null); } public void windowDeiconified(WindowEvent e) { query(Flag.GAME, Flag.G_UNPAUSE, null); } public void windowIconified(WindowEvent e) { query(Flag.GAME, Flag.G_PAUSE, null); } public void windowClosing(WindowEvent e) { } public void windowClosed(WindowEvent e) {} public void windowOpened(WindowEvent e) {} }