package vooga.scroller.level_editor.controllerSuite; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.HashSet; import java.util.Set; import javax.imageio.ImageIO; import javax.swing.Scrollable; import util.Location; import vooga.scroller.level_editor.LevelEditing; import vooga.scroller.level_editor.StartPoint; import vooga.scroller.level_editor.model.EditableGrid; import vooga.scroller.level_editor.model.SpriteBox; import vooga.scroller.level_editor.view.LEGridView; import vooga.scroller.level_management.LevelPortal; import vooga.scroller.sprites.Sprite; import vooga.scroller.util.IBackgroundView; import vooga.scroller.util.Renderable; import vooga.scroller.util.Renderer; import vooga.scroller.util.mvc.IView; /** * LEGrid is the grid that contains all of the SpriteBoxes as well as a StartPoint * and a Door. It is rendered by the LEGridView. * * @author Danny Goodman, Deo Fagnisse * */ public class LEGrid implements EditableGrid, Renderable<LevelEditing>, Scrollable { /** * Called by LevelEditor */ public static final int DEFAULT_SPRITE_SIZE = 32; private static final Location DEFAULT_START_LOC = new Location(0, 0); private int mySpriteSize; private SpriteBox[][] myGrid; private Dimension mySize; private Set<SpriteBox> myPaintableBoxes; private StartPoint myStartPoint; private LevelPortal myDoor; private IBackgroundView myBackground; /** * Creates LEGrid from num of blocks * * @param numWidthBlocks - width in blocks * @param numHeightBlocks - height in blocks */ public LEGrid (int numWidthBlocks, int numHeightBlocks) { mySpriteSize = DEFAULT_SPRITE_SIZE; mySize = new Dimension(numWidthBlocks, numHeightBlocks); myGrid = new SpriteBox[numWidthBlocks][numHeightBlocks]; initializeGrid(); myPaintableBoxes = new HashSet<SpriteBox>(); } @Override public void paint (Graphics2D pen) { if (myBackground != null) { pen.drawImage(myBackground.getImage(), 0, 0, null); } for (int i = 0; i < mySize.width; i++) { for (int j = 0; j < mySize.height; j++) { myGrid[i][j].paint(pen); } } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public Renderer<LevelEditing> initializeRenderer (IView parent) { return new LEGridView(parent, this); } @Override public void addSprite (Sprite spr, int x, int y) { SpriteBox currentBox = getBox(x, y); addToBox(spr, currentBox); } @Override public void deleteSprite (int x, int y) { SpriteBox currentBox = getBox(x, y); currentBox.deleteSprite(); myPaintableBoxes.remove(currentBox); } @Override public void changeBackground (IBackgroundView bg) { myBackground = bg; } @Override public void changeGridSize (int width, int height) { myGrid = createResizedGrid(width, height); mySize = new Dimension(width, height); checkStartPoint(); checkDoor(); checkPaintableBoxes(); } @Override public Dimension getPreferredScrollableViewportSize () { return null; } @Override public int getScrollableUnitIncrement (Rectangle visibleRect, int orientation, int direction) { return mySpriteSize; } @Override public int getScrollableBlockIncrement (Rectangle visibleRect, int orientation, int direction) { return mySpriteSize; } @Override public boolean getScrollableTracksViewportWidth () { return false; } @Override public boolean getScrollableTracksViewportHeight () { return false; } @Override public void addStartPoint (int x, int y) { if (myStartPoint == null) { myStartPoint = new StartPoint(); } else { deleteSprite((int) myStartPoint.getX(), (int) myStartPoint.getY()); } addSprite(myStartPoint, x, y); } @Override public void addDoor (Sprite s, int x, int y) { if (myDoor != null) { deleteSprite((int) myDoor.getX(), (int) myDoor.getY()); } myDoor = (LevelPortal) s; addSprite(myDoor, x, y); } /** * remove startpoint from grid. called by LevelWriter so that StartPoint * can be obtained and stored in the setting menu, not in the ascii map. * * @return Location of StartPoint */ public Location removeStartPoint () { if (myStartPoint == null) { return DEFAULT_START_LOC; } Location center = myStartPoint.getCenter(); deleteSprite((int) center.getX(), (int) center.getY()); return center; } /** * Save sprite as myDoor instance variable. add Sprite to SpriteBox. * * @param xcoor - x coordinate of SpriteBox * @param ycoor - y coordinate of SpriteBox * @param sprite - Sprite to be added */ public void addDoorWithCoor (int xcoor, int ycoor, Sprite sprite) { myDoor = (LevelPortal) sprite; addSpriteWithCoor(xcoor, ycoor, sprite); } /** * add sprite to box given the coordinates of the box. * * @param xcoor - x coordinate of SpriteBox * @param ycoor - y coordinate of SpriteBox * @param sprite - Sprit eto be added */ public void addSpriteWithCoor (int xcoor, int ycoor, Sprite sprite) { addToBox(sprite, getBoxFromCoor(xcoor, ycoor)); } /** * returns set of SpriteBoxes to be painted i.e. those that contain Sprites * * @return Set<SpriteBox> */ public Set<SpriteBox> getBoxes () { return myPaintableBoxes; } /** * @param xcoor - x coordinate of SpriteBox * @param ycoor - y coordinate of SpriteBox * @return SpriteBox */ public SpriteBox getBoxFromCoor (int xcoor, int ycoor) { return myGrid[xcoor][ycoor]; } /** * @param xcoor - x coordinate of SpriteBox * @param ycoor - y coordinate of SpriteBox * @return Sprite */ public Sprite getSpriteFromCoor (int xcoor, int ycoor) { return getBoxFromCoor(xcoor, ycoor).getSprite(); } /** * @return myBackground */ public IBackgroundView getBackground () { return myBackground; } /** * @return mySpriteSize - size of all SpriteBoxes. */ public int getSpriteSize () { return mySpriteSize; } /** * @return mySize */ public Dimension getSize () { return mySize; } /** * Get the overall pixel size of this LEGrid. * * @return pixel size Dimension */ public Dimension getPixelSize () { Dimension res = new Dimension(getWidth(), getHeight()); return res; } /** * Checks StartPoint and Door for simulation * * @return true if valid */ public boolean isValidForSimulation () { return myStartPoint != null && myDoor != null; } /** * Checks Door and Background for saving * * @return true if valid */ public boolean isValidForSave () { return myDoor != null && myBackground != null; } /** * Saves Thumbnail view of level. * * @param levelFilePath - String of filePath to save Level.png */ public void saveThumbnail (String levelFilePath) { try { ImageIO.write(getThumbnail(), "PNG", new File(levelFilePath + ".png")); } catch (IOException e) { // Wont happen System.out.println("Image not saved"); } } private SpriteBox getBox (double x, double y) { int xCoord = (int) Math.floor(x / mySpriteSize); int yCoord = (int) Math.floor(y / mySpriteSize); return myGrid[xCoord][yCoord]; } private int getHeight () { return mySize.height * mySpriteSize; } private int getWidth () { return mySize.width * mySpriteSize; } /** * Recursive call to check if all SpriteBoxes that the Sprite overlaps are empty. * * @param current * @param width * @param height * @return */ private boolean checkAvailable (SpriteBox current, double width, double height) { if (!current.isAvailable()) { return false; } boolean bool1 = true; boolean bool2 = true; if (width > mySpriteSize) { SpriteBox next = getBox(current.getX() + mySpriteSize, current.getY()); bool1 = checkAvailable(next, width - mySpriteSize, height); } if (height > mySpriteSize && bool1) { SpriteBox nextBox = getBox(current.getX(), current.getY() + mySpriteSize); bool2 = checkAvailable(nextBox, width, height - mySpriteSize); } return bool1 && bool2; } /** * Recursive call that combines all boxes that the Sprite Overlaps. * * @param initial * @param current * @param width * @param height */ private void combineBoxes (SpriteBox initial, SpriteBox current, double width, double height) { if (width > mySpriteSize) { SpriteBox next = getBox(current.getX() + mySpriteSize, current.getY()); initial.combineWith(next); combineBoxes(initial, next, width - mySpriteSize, height); } if (height > mySpriteSize) { SpriteBox next = getBox(current.getX(), current.getY() + mySpriteSize); initial.combineWith(next); combineBoxes(initial, next, width, height - mySpriteSize); } } private void initializeGrid () { for (int x = 0; x < mySize.getWidth(); x++) { for (int y = 0; y < mySize.getHeight(); y++) { myGrid[x][y] = new SpriteBox(this, x, y); } } } /** * add Sprite to given Box. Chacks available, then adds Sprite and * makes paintable. Then combines boxes as needed. * * @param spr * @param currentBox */ private void addToBox (Sprite spr, SpriteBox currentBox) { if (checkAvailable(currentBox, spr.getWidth(), spr.getHeight())) { currentBox.addSprite(spr); myPaintableBoxes.add(currentBox); combineBoxes(currentBox, currentBox, spr.getWidth(), spr.getHeight()); } } /** * Helper method for resizing the Grid. * * @param width * @param height * @return */ private SpriteBox[][] createResizedGrid (int width, int height) { SpriteBox[][] newGrid = new SpriteBox[width][height]; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (i < mySize.width && j < mySize.height) { newGrid[i][j] = getBoxFromCoor(i, j); } else { newGrid[i][j] = new SpriteBox(this, i, j); } } } return newGrid; } /** * Helper method for resizing the Grid. */ private void checkStartPoint () { if (myStartPoint != null) { if (myStartPoint.getX() > getWidth() || myStartPoint.getY() > getHeight()) { myStartPoint = null; } } } /** * Helper method for resizing the Grid. */ private void checkDoor () { if (myDoor != null) { if (myDoor.getX() > getWidth() || myDoor.getY() > getHeight()) { myDoor = null; } } } /** * Helper method for resizing the Grid. */ private void checkPaintableBoxes () { for (SpriteBox box : myPaintableBoxes) { if (box.getX() > getWidth() || box.getY() > getHeight()) { myPaintableBoxes.remove(box); } } } private BufferedImage paintThumbnail (BufferedImage img) { Graphics2D drawer = img.createGraphics(); paint(drawer); return img; } private BufferedImage getThumbnail () { BufferedImage res = new BufferedImage(getPixelSize().width, getPixelSize().height, BufferedImage.TYPE_INT_ARGB); return paintThumbnail(res); } }