+{
+ private K first;
+ private T second;
+
+ public Pair(K first, T second)
+ {
+ this.first = first;
+ this.second = second;
+ }
+
+ public K getFirst()
+ {
+ return this.first;
+ }
+
+ public T getSecond()
+ {
+ return this.second;
+ }
+
+ public void setFirst(K first)
+ {
+ this.first = first;
+ }
+
+ public void setSecond(T second)
+ {
+ this.second = second;
+ }
+}
diff --git a/src/com/aargonian/editor/EditableTileSheet.java b/src/com/aargonian/editor/EditableTileSheet.java
index 39b2041..06317b6 100644
--- a/src/com/aargonian/editor/EditableTileSheet.java
+++ b/src/com/aargonian/editor/EditableTileSheet.java
@@ -6,22 +6,23 @@ import java.util.List;
/**
* Created by aargonian on 7/13/17.
- *
- * The EditableTileSheet Class is an implementation for a collection of Tiles arranged within a Sheet, as used by the editing
+ *
+ * The EditableTileSheet Class is an implementation for a collection of Tiles arranged within a Sheet, as used by the
+ * editing
* program. Although this can be thought of in the traditional TileSheet sense (and in fact, a TileSheet can be
* constructed from a TileSheetReader from an actual TileSheet image), the EditableTileSheet is mutable, and doesn't
* keep each tile instance as a subimage within a larger sheet, unless written out by a TileSheetWriter. Instead,
* individual tile images can be added or removed from the set at will.
- *
*/
public class EditableTileSheet
{
/*
- * An EditableTileSheet is the backing model for the TileSheets dock of the editor, and as such implements the Observer
+ * An EditableTileSheet is the backing model for the TileSheets dock of the editor, and as such implements the
+ * Observer
* pattern for the sake of a cleaner MVC implementation.
*/
private final List tileImages;
-
+
/**
* Creates an empty EditableTileSheet.
*/
@@ -29,7 +30,7 @@ public class EditableTileSheet
{
tileImages = new ArrayList();
}
-
+
public EditableTileSheet(List tileImages)
{
this.tileImages = tileImages;
diff --git a/src/com/aargonian/editor/EditorTool.java b/src/com/aargonian/editor/EditorTool.java
new file mode 100644
index 0000000..238c3e4
--- /dev/null
+++ b/src/com/aargonian/editor/EditorTool.java
@@ -0,0 +1,45 @@
+package com.aargonian.editor;
+
+import java.awt.event.*;
+
+/**
+ * Created by aargonian on 7/16/17.
+ *
+ * An EditorTool is an implementation of a Controller for the main EditableTileMap. EditorTools can be thought of very
+ * similarly to the "Tools" used by graphics editing programs. Each EditorTool gets access to the underlying TileMap
+ * model, and receives input events from the TileMapDisplay. It is the job of the EditorTool to translate these input
+ * events into corresponding actions on the underlying TileMap or in the TileMapDisplay.
+ */
+public class EditorTool implements MouseListener, MouseMotionListener, KeyListener
+{
+ private final TileMapDisplay display;
+ public EditorTool(TileMapDisplay display)
+ {
+ if(display == null)
+ throw new NullPointerException("Given TileMapDisplay was Null!");
+ this.display = display;
+ }
+
+ protected TileMapDisplay getCurrentDisplay() { return this.display; }
+
+ @Override
+ public void keyTyped(KeyEvent e) { }
+ @Override
+ public void keyPressed(KeyEvent e) { }
+ @Override
+ public void keyReleased(KeyEvent e) { }
+ @Override
+ public void mousePressed(MouseEvent e) {}
+ @Override
+ public void mouseClicked(MouseEvent e) { }
+ @Override
+ public void mouseReleased(MouseEvent e) { }
+ @Override
+ public void mouseEntered(MouseEvent e) { }
+ @Override
+ public void mouseExited(MouseEvent e) { }
+ @Override
+ public void mouseDragged(MouseEvent e) { }
+ @Override
+ public void mouseMoved(MouseEvent e) { }
+}
diff --git a/src/com/aargonian/editor/GameFrame.java b/src/com/aargonian/editor/GameFrame.java
index 0fde69e..db6e166 100644
--- a/src/com/aargonian/editor/GameFrame.java
+++ b/src/com/aargonian/editor/GameFrame.java
@@ -1,53 +1,62 @@
package com.aargonian.editor;
+import com.aargonian.com.aargonian.tile.TileImpl;
+import com.aargonian.com.aargonian.tile.TileMap;
+
import javax.swing.*;
import java.awt.*;
+import java.awt.event.ActionListener;
import java.util.ArrayList;
+import java.util.HashMap;
/**
* Created by aargonian on 7/8/17.
*/
-public class GameFrame {
+public class GameFrame
+{
public static final String PROG_TITLE = "TileEditor 0.1.0";
-
- public static void main(String[] args) {
+
+ public static void main(String[] args)
+ {
EventQueue.invokeLater(() ->
- {
- GameFrame.setupUI();
- }
- );
+ {
+ GameFrame.setupUI();
+ });
}
-
+
//TODO: Add an Icon Image
private static final JFrame setupFrame()
{
JFrame frame = new JFrame(PROG_TITLE);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-
+
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
- if(screenSize.width <= 1024 || screenSize.height <= 800) {
+ if(screenSize.width <= 1024 || screenSize.height <= 800)
+ {
frame.setExtendedState(JFrame.MAXIMIZED_BOTH); //maximize by default if the screen is really small
- } else {
- frame.setSize((int)Math.round(screenSize.width * 0.8), (int)Math.round(screenSize.height * 0.8));
+ }
+ else
+ {
+ frame.setSize((int) Math.round(screenSize.width * 0.8), (int) Math.round(screenSize.height * 0.8));
frame.setLocationRelativeTo(null); //Centers the frame
}
return frame;
}
-
+
/**
* Creates the pane that holds the different tile implementations the user may choose to put into the editor.
+ *
* @return The Tile Pane Component, Fully Constructed
*/
private static final JComponent setupTilesPanel()
{
throw new UnsupportedOperationException("Not Supported Yet.");
}
-
+
public static final void setupUI()
{
JFrame frame = setupFrame();
- /*
TileMap map = new TileMap(10, 10);
map.setTileResourceAt(TileImpl.PROPERTY_IMG, ImageReader.readImage("res/Water.png"), 2, 2);
@@ -57,27 +66,19 @@ public class GameFrame {
.displaySize(640, 480)
.borderColor(Color.black);
TileMapDisplay currentTileMapDisplay = new TileMapDisplay(displayOptions);
+
currentTileMapDisplay.setCurrentTileMap(map);
+ currentTileMapDisplay.setCurrentActiveTool(new TileSelectTool(currentTileMapDisplay));
+
System.out.println("TILE MAP DISPLAY WIDTH: " + currentTileMapDisplay.getPreferredSize().getWidth());
System.out.println("TILE MAP DISPLAY HEIGHT: " + currentTileMapDisplay.getPreferredSize().getHeight());
frame.add(currentTileMapDisplay);
- */
-
- ArrayList images = new ArrayList<>(6);
- for(int i = 0; i < 20; i++)
- {
- images.add(ImageReader.readImage("res/Water.png"));
- }
- TilesetDisplay display = new TilesetDisplay(32, images);
- frame.add(display);
+
+ frame.add(BorderLayout.CENTER, currentTileMapDisplay);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
-
- /*
- currentTileMapDisplay.repaint();
- */
}
-
+
}
diff --git a/src/com/aargonian/editor/ImageReader.java b/src/com/aargonian/editor/ImageReader.java
index 5ee26b0..546f683 100644
--- a/src/com/aargonian/editor/ImageReader.java
+++ b/src/com/aargonian/editor/ImageReader.java
@@ -12,12 +12,6 @@ public class ImageReader
{
public static final BufferedImage readImage(String imagePath)
{
- try {
- BufferedImage img = ImageIO.read(new File(imagePath));
- return img;
- } catch(IOException ex) {
- System.err.println("Exception Occurred: " + ex);
- }
- return null;
+ return ResourceLoader.loadImage(imagePath);
}
}
diff --git a/src/com/aargonian/editor/ResourceLoader.java b/src/com/aargonian/editor/ResourceLoader.java
new file mode 100644
index 0000000..aa9ec83
--- /dev/null
+++ b/src/com/aargonian/editor/ResourceLoader.java
@@ -0,0 +1,46 @@
+package com.aargonian.editor;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by aargonian on 7/17/17.
+ *
+ * A utility class for loading and caching common resources used by the editor. Primarily used to load images and sound
+ * files.
+ */
+public final class ResourceLoader
+{
+ private static final Map resourceCache = new HashMap<>(100);
+
+ public static final BufferedImage loadImage(String imagePath)
+ {
+ if(imagePath == null || imagePath.isEmpty())
+ return null;
+ if(resourceCache.containsKey(imagePath) && resourceCache.get(imagePath) != null)
+ return (BufferedImage)resourceCache.get(imagePath);
+ else
+ {
+ try
+ {
+ BufferedImage image = ImageIO.read(new File(imagePath));
+ resourceCache.put(imagePath, image);
+ return image;
+ }
+ catch(IOException ex)
+ {
+ System.err.println("Unable to load resource: " + imagePath);
+ System.err.println(ex);
+ for(StackTraceElement element : ex.getStackTrace())
+ {
+ System.err.println("\tat " + element);
+ }
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/com/aargonian/editor/TileMapDisplay.java b/src/com/aargonian/editor/TileMapDisplay.java
index ec60dbf..ae87693 100644
--- a/src/com/aargonian/editor/TileMapDisplay.java
+++ b/src/com/aargonian/editor/TileMapDisplay.java
@@ -2,66 +2,127 @@ package com.aargonian.editor;
import com.aargonian.com.aargonian.tile.TileImpl;
import com.aargonian.com.aargonian.tile.TileMap;
+import com.aargonian.com.aargonian.util.Pair;
import javax.swing.*;
import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.List;
/**
* Created by aargonian on 7/8/17.
*/
public class TileMapDisplay extends JComponent
{
- private TileMap tileMap;
- private final OptionsBuilder options;
private static final OptionsBuilder DEFAULT_OPTIONS =
new OptionsBuilder().borderColor(Color.black).displaySize(640, 480).tileSize(32, 32);
-
+ private final OptionsBuilder options;
+ private TileMap tileMap;
+ private EditorTool currentActiveTool;
+ private List> selectedTiles;
+
public TileMapDisplay()
{
this.options = DEFAULT_OPTIONS;
}
-
+
public TileMapDisplay(OptionsBuilder options)
{
this.options = options;
this.setPreferredSize(new Dimension(options.displayWidth(), options.displayHeight()));
}
-
+
public OptionsBuilder getOptions()
{
return options;
}
-
- public void setCurrentTileMap(TileMap map)
- {
- this.tileMap = map;
- }
-
+
public TileMap getCurrentTileMap()
{
return this.tileMap;
}
-
- public void paint(Graphics g)
+
+ public void setCurrentTileMap(TileMap map)
{
- Graphics2D g2 = (Graphics2D)g;
+ this.tileMap = map;
+ }
+
+ public void setSelectedTiles(List> tiles)
+ {
+ this.selectedTiles = tiles;
+ this.repaint();
+ }
+
+ public List> getSelectedTiles()
+ {
+ return this.selectedTiles;
+ }
+
+ public EditorTool getCurrentActiveTool()
+ {
+ return this.currentActiveTool;
+ }
+
+ public void setCurrentActiveTool(EditorTool tool)
+ {
+ if(this.currentActiveTool != null)
+ {
+ this.removeMouseListener(currentActiveTool);
+ this.removeMouseMotionListener(currentActiveTool);
+ this.removeKeyListener(currentActiveTool);
+ }
+
+ this.currentActiveTool = tool;
+ this.addMouseListener(tool);
+ this.addMouseMotionListener(tool);
+ this.addKeyListener(tool);
+ }
+
+ @Override
+ public void paintComponent(Graphics g)
+ {
+ Graphics2D g2 = (Graphics2D) g;
g2.setColor(options.borderColor());
g2.fillRect(0, 0, this.getWidth(), this.getHeight());
for(int x = 0; x < tileMap.getColumns(); x++)
{
for(int y = 0; y < tileMap.getRows(); y++)
{
- Image img = (Image)tileMap.getTileResourceAt(TileImpl.PROPERTY_IMG, x, y);
+ final int tileXLoc = x * options.tileWidth();
+ final int tileYLoc = y * options.tileHeight();
+
+ Image img = (Image) tileMap.getTileResourceAt(TileImpl.PROPERTY_IMG, x, y);
if(img != null)
- g2.drawImage(img,
- x*options.tileWidth(), y*options.tileHeight(), options.tileWidth(), options.tileHeight(),
- null);
+ {
+ g2.drawImage(img, tileXLoc, tileYLoc, options.tileWidth(), options.tileHeight(), null);
+ }
else
{
- GradientPaint defPaint = new GradientPaint(x*options.tileWidth(), y*options.tileHeight(),
- Color.white, (x+1)*options.tileWidth(), (y+1)*options.tileHeight, Color.black);
- g2.setPaint(defPaint);
- g2.fillRect(x*options.tileWidth(), y*options.tileHeight(), options.tileWidth(), options.tileHeight());
+ final int HALF_WIDTH = options.tileWidth() / 2;
+ final int HALF_HEIGHT = options.tileHeight() / 2;
+
+ g2.setColor(Color.lightGray);
+ g2.fillRect(tileXLoc, tileYLoc, HALF_WIDTH, HALF_HEIGHT);
+ g2.fillRect(tileXLoc + HALF_WIDTH, tileYLoc + HALF_HEIGHT, HALF_WIDTH, HALF_HEIGHT);
+
+ g2.setColor(Color.gray);
+ g2.fillRect(tileXLoc, tileYLoc + HALF_HEIGHT, HALF_WIDTH, HALF_HEIGHT);
+ g2.fillRect(tileXLoc + HALF_WIDTH, tileYLoc, HALF_WIDTH, HALF_HEIGHT);
+ }
+
+ if(selectedTiles != null)
+ {
+ for(Pair selectedTile : selectedTiles)
+ {
+ if(selectedTile.getFirst() == x && selectedTile.getSecond() == y)
+ {
+ g2.setColor(Color.YELLOW);
+ g2.fillRect(tileXLoc, tileYLoc, options.tileWidth(), options.tileHeight());
+ g2.setColor(Color.YELLOW.brighter());
+ g2.fillRect(tileXLoc + 2, tileYLoc + 2, options.tileWidth() - 2, options.tileHeight() - 2);
+ }
+ }
}
}
}
@@ -75,58 +136,81 @@ public class TileMapDisplay extends JComponent
private int displayHeight = 480;
private Color borderColor = Color.black;
private boolean fullscreen = false;
-
+
public OptionsBuilder tileWidth(int width)
{
this.tileWidth = width <= 0 ? 0 : width;
return this;
}
-
+
public OptionsBuilder tileHeight(int height)
{
this.tileHeight = height <= 0 ? 0 : height;
return this;
}
-
+
public OptionsBuilder tileSize(int width, int height)
{
return this.tileWidth(width).tileHeight(height);
}
-
+
public OptionsBuilder borderColor(Color c)
{
this.borderColor = c;
return this;
}
-
+
public OptionsBuilder displayWidth(int width)
{
this.displayWidth = width <= 0 ? 0 : width;
return this;
}
-
+
public OptionsBuilder displayHeight(int height)
{
this.displayHeight = height <= 0 ? 0 : height;
return this;
}
-
+
public OptionsBuilder displaySize(int width, int height)
{
return this.displayWidth(width).displayHeight(height);
}
-
+
public OptionsBuilder fullscreen(boolean value)
{
this.fullscreen = value;
return this;
}
-
- public int tileWidth() { return tileWidth; }
- public int tileHeight() { return tileHeight; }
- public int displayWidth() { return displayWidth; }
- public int displayHeight() { return displayHeight; }
- public Color borderColor() { return borderColor; }
- public boolean fulllscreen() { return fullscreen; }
+
+ public int tileWidth()
+ {
+ return tileWidth;
+ }
+
+ public int tileHeight()
+ {
+ return tileHeight;
+ }
+
+ public int displayWidth()
+ {
+ return displayWidth;
+ }
+
+ public int displayHeight()
+ {
+ return displayHeight;
+ }
+
+ public Color borderColor()
+ {
+ return borderColor;
+ }
+
+ public boolean fulllscreen()
+ {
+ return fullscreen;
+ }
}
}
diff --git a/src/com/aargonian/editor/TileSelectTool.java b/src/com/aargonian/editor/TileSelectTool.java
new file mode 100644
index 0000000..49c8ca5
--- /dev/null
+++ b/src/com/aargonian/editor/TileSelectTool.java
@@ -0,0 +1,44 @@
+package com.aargonian.editor;
+
+import com.aargonian.com.aargonian.tile.TileImpl;
+import com.aargonian.com.aargonian.util.Pair;
+
+import java.awt.event.MouseEvent;
+import java.util.Arrays;
+
+/**
+ * Created by aargonian on 7/16/17.
+ */
+public class TileSelectTool extends EditorTool
+{
+ private int currentTileX = 0;
+ private int currentTileY = 0;
+
+ public TileSelectTool(TileMapDisplay display)
+ {
+ super(display);
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e)
+ {
+ int tileX = e.getX()/super.getCurrentDisplay().getOptions().tileWidth();
+ int tileY = e.getY()/super.getCurrentDisplay().getOptions().tileHeight();
+ //getCurrentDisplay().setSelectedTiles(Arrays.asList(new Pair(tileX, tileY)));
+ getCurrentDisplay().getCurrentTileMap().setTileResourceAt(TileImpl.PROPERTY_IMG, ImageReader.readImage("res/Water.png"), tileX, tileY);
+ getCurrentDisplay().repaint();
+ }
+
+ @Override
+ public void mouseMoved(MouseEvent e)
+ {
+ int tileX = e.getX()/super.getCurrentDisplay().getOptions().tileWidth();
+ int tileY = e.getY()/super.getCurrentDisplay().getOptions().tileHeight();
+
+ if(tileX != currentTileX || tileY != currentTileY)
+ {
+ currentTileX = tileX;
+ currentTileY = tileY;
+ }
+ }
+}
diff --git a/src/com/aargonian/editor/TilesetDisplay.java b/src/com/aargonian/editor/TilesetDisplay.java
index 58cba08..4adc9f8 100644
--- a/src/com/aargonian/editor/TilesetDisplay.java
+++ b/src/com/aargonian/editor/TilesetDisplay.java
@@ -6,43 +6,53 @@ import java.util.ArrayList;
/**
* Created by aargonian on 7/9/17.
- *
+ *
* This class implements a display for a set of tiles.
*/
public final class TilesetDisplay extends JComponent
{
private final ArrayList images;
private int tileSize;
-
+
public TilesetDisplay(int tileSize, ArrayList tileImages)
{
if(tileImages == null)
+ {
throw new NullPointerException("Passed Image Set is Null.");
+ }
this.images = tileImages;
this.tileSize = tileSize;
+
+ //TODO: Remove this later?
+ int squareSize = ((int) (Math.sqrt(tileImages.size()))) * tileSize;
+ this.setPreferredSize(new Dimension(squareSize, squareSize));
}
-
+
public void addImageToDisplay(Image img)
{
images.add(img);
+ this.repaint();
}
-
+
public void removeImageFromDisplay(Image img)
{
images.remove(img);
+ this.repaint();
}
-
+
//Todo: Update this to use a scrollpane and avoid divide by zero.
@Override
public void paintComponent(Graphics g)
{
g.setColor(Color.gray);
- g.fillRect(0,0, getWidth(), getHeight());
- int columns = (int)(getWidth()/tileSize);
- if(columns != 0) { // Avoid divide by zero and simply display nothing.
- for (int i = 0; i < images.size(); i++) {
- g.drawImage(images.get(i),
- (i % columns) * tileSize, ((int) (i / columns)) * tileSize, tileSize, tileSize, null);
+ g.fillRect(0, 0, getWidth(), getHeight());
+ int columns = (int) (getWidth() / tileSize);
+ if(columns != 0) // Avoid divide by zero and simply display nothing.
+ {
+ for(int i = 0; i < images.size(); i++)
+ {
+ g.drawImage(images.get(i), (i % columns) * tileSize, ((int) (i / columns)) * tileSize, tileSize,
+ tileSize, null);
}
}
}