1 | package jpacman.controller; |
2 | |
3 | import java.awt.BasicStroke; |
4 | import java.awt.Color; |
5 | import java.awt.Dimension; |
6 | import java.awt.Graphics; |
7 | import java.awt.Graphics2D; |
8 | import java.awt.Image; |
9 | import java.awt.Point; |
10 | import java.awt.Rectangle; |
11 | import java.awt.RenderingHints; |
12 | import java.awt.image.BufferedImage; |
13 | import java.io.IOException; |
14 | |
15 | import javax.swing.JPanel; |
16 | |
17 | import jpacman.model.Engine; |
18 | import jpacman.model.Guest; |
19 | |
20 | /** |
21 | * A simple way to paint a pacman board. Benefited from code by code from the |
22 | * Java 2D Arcs Demo. Other inspiration taken from course CS102 from |
23 | * Bilkent university, 2003. |
24 | * <p/> |
25 | * It should be fairly easy to create a nicer viewer, but since this is not the |
26 | * topic of the testing course we leave it at this. |
27 | * <p/> |
28 | * |
29 | * @author Arie van Deursen; Jul 28, 2003 |
30 | * @version $Id: BoardViewer.java,v 1.9 2008/02/04 23:00:12 arie Exp $ |
31 | */ |
32 | public class BoardViewer extends JPanel { |
33 | |
34 | /** Since a JPanel (through JComponent) is serializable, |
35 | * so is the BoardViewer which extends it, |
36 | * which is why it should have a serialVersionUID. |
37 | */ |
38 | static final long serialVersionUID = -4976741292570616918L; |
39 | |
40 | /** |
41 | * The interface to the model of the game, |
42 | * through the Engine Facade. |
43 | */ |
44 | private Engine engine; |
45 | |
46 | /** |
47 | * Buffered image used for drawing cells. |
48 | */ |
49 | private BufferedImage bimg = null; |
50 | |
51 | /** |
52 | * Width of an individual cell, in pixels. |
53 | */ |
54 | private static final int CELL_WIDTH = 20; |
55 | |
56 | /** |
57 | * Height of an individual cell, in pixels. |
58 | */ |
59 | private static final int CELL_HEIGHT = 20; |
60 | |
61 | /** |
62 | * The horizontal gap between cells, in pixels. |
63 | */ |
64 | public static final int CELL_HGAP = 6; |
65 | |
66 | /** |
67 | * The vertical gap between cells, in pixels. |
68 | */ |
69 | public static final int CELL_VGAP = 6; |
70 | |
71 | /** |
72 | * The factory containing Image objects for the various animated images. |
73 | */ |
74 | private ImageFactory imageFactory; |
75 | |
76 | /** |
77 | * Indicator for animation. |
78 | */ |
79 | private int animationCount; |
80 | |
81 | |
82 | /** |
83 | * Create a new board viewer and attach |
84 | * it to the underlying pacman model. |
85 | * |
86 | * @param theEngine |
87 | * The underlying pacman engine. |
88 | * @throws IOException If the images cannot be loaded. |
89 | */ |
90 | public BoardViewer(Engine theEngine) throws IOException { |
91 | engine = theEngine; |
92 | setBackground(Color.white); |
93 | animationCount = 0; |
94 | imageFactory = new ImageFactory(); |
95 | } |
96 | |
97 | /** |
98 | * @return The board width measured in cells, >= 0. |
99 | */ |
100 | private int worldWidth() { |
101 | return engine.boardWidth(); |
102 | } |
103 | |
104 | /** |
105 | * @return The board height measured in cells, >= 0. |
106 | */ |
107 | private int worldHeight() { |
108 | return engine.boardHeight(); |
109 | } |
110 | |
111 | /** |
112 | * The width of the board viewer in pixels. |
113 | * |
114 | * @return The width of the board viewer. |
115 | */ |
116 | public int windowWidth() { |
117 | return (cellWidth() + CELL_HGAP) * worldWidth() + 2 * CELL_HGAP; |
118 | } |
119 | |
120 | /** |
121 | * The height of the board viewer in pixels. |
122 | * |
123 | * @return The height of the board viewer. |
124 | */ |
125 | public int windowHeight() { |
126 | return |
127 | (cellHeight() + CELL_VGAP) * worldHeight() |
128 | + 2 * CELL_VGAP; |
129 | } |
130 | |
131 | /** |
132 | * The height of a cell (in pixels). |
133 | * @return Cell height in pixels |
134 | */ |
135 | public int cellHeight() { |
136 | return CELL_HEIGHT; |
137 | } |
138 | |
139 | /** |
140 | * Width of cell. |
141 | * @return Cell width in pixels. |
142 | */ |
143 | public int cellWidth() { |
144 | return CELL_WIDTH; |
145 | } |
146 | |
147 | /** |
148 | * JComponent method invoked when the board needs to be drawn. |
149 | * @param g The graphics to paint the board on. |
150 | */ |
151 | @Override |
152 | public void paint(Graphics g) { |
153 | Dimension d = getSize(); |
154 | Graphics2D g2 = createGraphics2D(d.width, d.height); |
155 | drawCells(g2); |
156 | g2.dispose(); |
157 | g.drawImage(bimg, 0, 0, this); |
158 | } |
159 | |
160 | /** |
161 | * Create a piece of graphics to display the board on. |
162 | * @param w desired width in pixels |
163 | * @param h desired height in pixels |
164 | * @return The graphics to display the board. |
165 | */ |
166 | private Graphics2D createGraphics2D(int w, int h) { |
167 | Graphics2D g2 = null; |
168 | if (bimg == null |
169 | || bimg.getWidth() != w |
170 | || bimg.getHeight() != h) { |
171 | bimg = (BufferedImage) createImage(w, h); |
172 | } |
173 | g2 = bimg.createGraphics(); |
174 | g2.setBackground(getBackground()); |
175 | g2.clearRect(0, 0, w, h); |
176 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, |
177 | RenderingHints.VALUE_ANTIALIAS_ON); |
178 | return g2; |
179 | } |
180 | |
181 | /** |
182 | * Draw an individual cell. |
183 | * @param x x-coordinate |
184 | * @param y y-coordinate |
185 | * @param g2 Graphics to draw on |
186 | */ |
187 | private void drawCell(int x, int y, Graphics2D g2) { |
188 | Dimension dim = new Dimension(cellWidth(), cellHeight()); |
189 | Point loc = new Point( |
190 | CELL_HGAP / 2 + (cellWidth() + CELL_HGAP) * x, |
191 | CELL_VGAP / 2 + (cellHeight() + CELL_VGAP) * y); |
192 | Rectangle rect = new Rectangle(loc, dim); |
193 | g2.setColor(Color.blue); |
194 | g2.draw(rect); |
195 | Color fill = null; |
196 | Image img = null; |
197 | |
198 | switch (engine.getGuestCode(x, y)) { |
199 | case Guest.WALL_TYPE: |
200 | fill = Color.ORANGE; |
201 | break; |
202 | case Guest.PLAYER_TYPE: |
203 | img = imageFactory.player( |
204 | engine.getPlayerLastDx(), |
205 | engine.getPlayerLastDy(), |
206 | animationCount); |
207 | break; |
208 | case Guest.FOOD_TYPE: |
209 | fill = Color.GREEN; |
210 | break; |
211 | case Guest.MONSTER_TYPE: |
212 | img = imageFactory.monster(animationCount); |
213 | break; |
214 | case Guest.EMPTY_TYPE: |
215 | fill = Color.BLACK; |
216 | break; |
217 | default: |
218 | assert false : "Illegal guest code"; |
219 | } |
220 | |
221 | if (img != null) { |
222 | g2.drawImage(img, loc.x, loc.y, this); |
223 | } else { |
224 | assert fill != null; |
225 | g2.setColor(fill); |
226 | g2.fill(rect); |
227 | } |
228 | } |
229 | |
230 | /** |
231 | * Draw all cells on the board. |
232 | * @param g2 The graphics to draw the cells on. |
233 | */ |
234 | private void drawCells(Graphics2D g2) { |
235 | final float strokeWidth = 5.0f; |
236 | g2.setStroke(new BasicStroke(strokeWidth)); |
237 | |
238 | for (int x = 0; x < worldWidth(); x++) { |
239 | for (int y = 0; y < worldHeight(); y++) { |
240 | drawCell(x, y, g2); |
241 | } |
242 | } |
243 | } |
244 | |
245 | /** |
246 | * Increment the animation counter, and redisplay, |
247 | * so that the next animation becomes visible. |
248 | */ |
249 | public void nextAnimation() { |
250 | animationCount = (animationCount + 1) |
251 | % (imageFactory.monsterAnimationCount() |
252 | * imageFactory.playerAnimationCount()); |
253 | repaint(); |
254 | } |
255 | } |