Methapolis  0.27
 All Classes Namespaces Files Functions Variables Enumerator
OverlayMapView.java
Go to the documentation of this file.
1 // This file is part of MicropolisJ.
2 // Copyright (C) 2013 Jason Long
3 // Portions Copyright (C) 1989-2007 Electronic Arts Inc.
4 //
5 // MicropolisJ is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU GPLv3, with additional terms.
7 // See the README file, included in this distribution, for details.
8 
9 package micropolisj.gui;
10 
11 import java.awt.*;
12 import java.awt.event.*;
13 import java.awt.image.*;
14 import java.net.URL;
15 import java.util.*;
16 import javax.swing.*;
17 import javax.swing.event.*;
18 
19 import micropolisj.engine.*;
20 import static micropolisj.engine.TileConstants.*;
21 
22 public class OverlayMapView extends JComponent
23  implements Scrollable, MapListener
24 {
25  Micropolis engine;
26  ArrayList<ConnectedView> views = new ArrayList<ConnectedView>();
27  MapState mapState = MapState.ALL;
28 
29  public OverlayMapView(Micropolis _engine)
30  {
31  assert _engine != null;
32 
33  MouseAdapter mouse = new MouseAdapter() {
34  @Override
35  public void mousePressed(MouseEvent ev)
36  {
37  onMousePressed(ev);
38  }
39  @Override
40  public void mouseDragged(MouseEvent ev)
41  {
42  onMouseDragged(ev);
43  }
44  };
45  addMouseListener(mouse);
46  addMouseMotionListener(mouse);
47 
48  setEngine(_engine);
49  }
50 
52  {
53  return engine;
54  }
55 
56  public void setEngine(Micropolis newEngine)
57  {
58  assert newEngine != null;
59 
60  if (engine != null) { //old engine
61  engine.removeMapListener(this);
62  }
63  engine = newEngine;
64  if (engine != null) { //new engine
65  engine.addMapListener(this);
66  }
67 
68  invalidate(); //map size may have changed
69  repaint();
70  engine.calculateCenterMass();
72  }
73 
75  {
76  return mapState;
77  }
78 
79  @Override
80  public Dimension getPreferredSize()
81  {
82  return new Dimension(
83  getInsets().left + getInsets().right + TILE_WIDTH*engine.getWidth(),
84  getInsets().top + getInsets().bottom + TILE_HEIGHT*engine.getHeight()
85  );
86  }
87 
88  public void setMapState(MapState newState)
89  {
90  if (mapState == newState)
91  return;
92 
93  mapState = newState;
94  repaint();
95  }
96 
97  static BufferedImage tileArrayImage = loadImage("/tilessm.png");
98  static final int TILE_WIDTH = 3;
99  static final int TILE_HEIGHT = 3;
100  static final int TILE_OFFSET_Y = 3;
101 
102  static BufferedImage loadImage(String resourceName)
103  {
104  URL iconUrl = MicropolisDrawingArea.class.getResource(resourceName);
105  Image refImage = new ImageIcon(iconUrl).getImage();
106 
107  BufferedImage bi = new BufferedImage(refImage.getWidth(null), refImage.getHeight(null),
108  BufferedImage.TYPE_INT_RGB);
109  Graphics2D gr = bi.createGraphics();
110  gr.drawImage(refImage, 0, 0, null);
111 
112  return bi;
113  }
114 
115  static final Color VAL_LOW = new Color(0xbfbfbf);
116  static final Color VAL_MEDIUM = new Color(0xffff00);
117  static final Color VAL_HIGH = new Color(0xff7f00);
118  static final Color VAL_VERYHIGH = new Color(0xff0000);
119  static final Color VAL_PLUS = new Color(0x007f00);
120  static final Color VAL_VERYPLUS = new Color(0x00e600);
121  static final Color VAL_MINUS = new Color(0xff7f00);
122  static final Color VAL_VERYMINUS = new Color(0xffff00);
123 
124  private Color getCI(int x)
125  {
126  if (x < 50)
127  return null;
128  else if (x < 100)
129  return VAL_LOW;
130  else if (x < 150)
131  return VAL_MEDIUM;
132  else if (x < 200)
133  return VAL_HIGH;
134  else
135  return VAL_VERYHIGH;
136  }
137 
138  private Color getCI_rog(int x)
139  {
140  if (x > 100)
141  return VAL_VERYPLUS;
142  else if (x > 20)
143  return VAL_PLUS;
144  else if (x < -100)
145  return VAL_VERYMINUS;
146  else if (x < -20)
147  return VAL_MINUS;
148  else
149  return null;
150  }
151 
152  private void drawPollutionMap(Graphics gr)
153  {
154  int [][] A = engine.pollutionMem;
155 
156  for (int y = 0; y < A.length; y++) {
157  for (int x = 0; x < A[y].length; x++) {
158  maybeDrawRect(gr, getCI(10 + A[y][x]),x*6,y*6,6,6);
159  }
160  }
161  }
162 
163  private void drawCrimeMap(Graphics gr)
164  {
165  int [][] A = engine.crimeMem;
166 
167  for (int y = 0; y < A.length; y++) {
168  for (int x = 0; x < A[y].length; x++) {
169  maybeDrawRect(gr, getCI(A[y][x]),x*6,y*6,6,6);
170  }
171  }
172  }
173 
174  private void drawPopDensity(Graphics gr)
175  {
176  int [][] A = engine.popDensity;
177 
178  for (int y = 0; y < A.length; y++) {
179  for (int x = 0; x < A[y].length; x++) {
180  maybeDrawRect(gr, getCI(A[y][x]),x*6,y*6,6,6);
181  }
182  }
183  }
184 
185  private void drawRateOfGrowth(Graphics gr)
186  {
187  int [][] A = engine.rateOGMem;
188 
189  for (int y = 0; y < A.length; y++) {
190  for (int x = 0; x < A[y].length; x++) {
191  maybeDrawRect(gr, getCI_rog(A[y][x]),x*24,y*24,24,24);
192  }
193  }
194  }
195 
196  private void drawFireRadius(Graphics gr)
197  {
198  int [][] A = engine.fireRate;
199 
200  for (int y = 0; y < A.length; y++) {
201  for (int x = 0; x < A[y].length; x++) {
202  maybeDrawRect(gr, getCI(A[y][x]),x*24,y*24,24,24);
203  }
204  }
205  }
206 
207  private void drawPoliceRadius(Graphics gr)
208  {
209  int [][] A = engine.policeMapEffect;
210 
211  for (int y = 0; y < A.length; y++) {
212  for (int x = 0; x < A[y].length; x++) {
213  maybeDrawRect(gr, getCI(A[y][x]),x*24,y*24,24,24);
214  }
215  }
216  }
217 
218  private void maybeDrawRect(Graphics gr, Color col, int x, int y, int width, int height)
219  {
220  if (col != null) {
221  gr.setColor(col);
222  gr.fillRect(x,y,width,height);
223  }
224  }
225 
226  static final int UNPOWERED = 0x6666e6; //lightblue
227  static final int POWERED = 0xff0000; //red
228  static final int CONDUCTIVE = 0xbfbfbf; //lightgray
229 
230  private int checkPower(BufferedImage img, int x, int y, int rawTile)
231  {
232  int pix;
233 
234  if ((rawTile & LOMASK) <= 63) {
235  return rawTile & LOMASK;
236  }
237  else if (isZoneCenter(rawTile)) {
238  // zone
239  pix = ((rawTile & PWRBIT) != 0) ? POWERED : UNPOWERED;
240  }
241  else if (isConductive(rawTile)) {
242  pix = CONDUCTIVE;
243  }
244  else {
245  return DIRT;
246  }
247 
248  for (int yy = 0; yy < TILE_HEIGHT; yy++)
249  {
250  for (int xx = 0; xx < TILE_WIDTH; xx++)
251  {
252  img.setRGB(x*TILE_WIDTH+xx,y*TILE_HEIGHT+yy, pix);
253  }
254  }
255  return -1; //this special value tells caller to skip the tile bitblt,
256  //since it was performed here
257  }
258 
259  private int checkLandValueOverlay(BufferedImage img, int xpos, int ypos, int tile)
260  {
261  int v = engine.getLandValue(xpos, ypos);
262  Color c = getCI(v);
263  if (c == null) {
264  return tile;
265  }
266 
267  int pix = c.getRGB();
268  for (int yy = 0; yy < TILE_HEIGHT; yy++) {
269  for (int xx = 0; xx < TILE_WIDTH; xx++) {
270  img.setRGB(
271  xpos*TILE_WIDTH+xx,
272  ypos*TILE_HEIGHT+yy,
273  pix);
274  }
275  }
276  return CLEAR;
277  }
278 
279  private int checkTrafficOverlay(BufferedImage img, int xpos, int ypos, int tile)
280  {
281  int d = engine.getTrafficDensity(xpos, ypos);
282  Color c = getCI(d);
283  if (c == null) {
284  return tile;
285  }
286 
287  int pix = c.getRGB();
288  for (int yy = 0; yy < TILE_HEIGHT; yy++) {
289  for (int xx = 0; xx < TILE_WIDTH; xx++) {
290  img.setRGB(
291  xpos*TILE_WIDTH+xx,
292  ypos*TILE_HEIGHT+yy,
293  pix);
294  }
295  }
296  return CLEAR;
297  }
298 
299  @Override
300  public void paintComponent(Graphics gr)
301  {
302  final int width = engine.getWidth();
303  final int height = engine.getHeight();
304 
305  BufferedImage img = new BufferedImage(width*TILE_WIDTH, height*TILE_HEIGHT,
306  BufferedImage.TYPE_INT_RGB);
307 
308  final Insets INSETS = getInsets();
309  Rectangle clipRect = gr.getClipBounds();
310  int minX = Math.max(0, (clipRect.x - INSETS.left) / TILE_WIDTH);
311  int minY = Math.max(0, (clipRect.y - INSETS.top) / TILE_HEIGHT);
312  int maxX = Math.min(width, 1 + (clipRect.x - INSETS.left + clipRect.width-1) / TILE_WIDTH);
313  int maxY = Math.min(height, 1 + (clipRect.y - INSETS.top + clipRect.height-1) / TILE_HEIGHT);
314 
315  for (int y = minY; y < maxY; y++)
316  {
317  for (int x = minX; x < maxX; x++)
318  {
319  int tile = engine.getTile(x,y);
320  switch (mapState) {
321  case RESIDENTIAL:
322  if (isZoneAny(tile) &&
323  !isResidentialZoneAny(tile))
324  {
325  tile = DIRT;
326  }
327  break;
328  case COMMERCIAL:
329  if (isZoneAny(tile) &&
330  !isCommercialZone(tile))
331  {
332  tile = DIRT;
333  }
334  break;
335  case INDUSTRIAL:
336  if (isZoneAny(tile) &&
337  !isIndustrialZone(tile))
338  {
339  tile = DIRT;
340  }
341  break;
342  case POWER_OVERLAY:
343  tile = checkPower(img, x, y, engine.getTile(x,y));
344  break;
345  case TRANSPORT:
346  case TRAFFIC_OVERLAY:
347  if (isConstructed(tile)
348  && !isRoadAny(tile)
349  && !isRailAny(tile))
350  {
351  tile = DIRT;
352  }
353  if (mapState == MapState.TRAFFIC_OVERLAY)
354  {
355  tile = checkTrafficOverlay(img, x, y, tile);
356  }
357  break;
358 
359  case LANDVALUE_OVERLAY:
360  tile = checkLandValueOverlay(img, x, y, tile);
361  break;
362 
363  default:
364  }
365 
366  // tile == -1 means it's already been drawn
367  // in the checkPower function
368 
369  if (tile != -1) {
370  paintTile(img, x, y, tile);
371  }
372  }
373  }
374 
375  gr.drawImage(img, INSETS.left, INSETS.top, null);
376 
377  gr = gr.create();
378  gr.translate(INSETS.left, INSETS.top);
379 
380  switch (mapState) {
381  case POLICE_OVERLAY:
382  drawPoliceRadius(gr); break;
383  case FIRE_OVERLAY:
384  drawFireRadius(gr); break;
385  case CRIME_OVERLAY:
386  drawCrimeMap(gr); break;
387  case POLLUTE_OVERLAY:
388  drawPollutionMap(gr); break;
389  case GROWTHRATE_OVERLAY:
390  drawRateOfGrowth(gr); break;
391  case POPDEN_OVERLAY:
392  drawPopDensity(gr); break;
393  default:
394  }
395 
396  for (ConnectedView cv : views)
397  {
398  Rectangle rect = getViewRect(cv);
399  gr.setColor(Color.WHITE);
400  gr.drawRect(rect.x-2,rect.y-2,rect.width+2,rect.height+2);
401 
402  gr.setColor(Color.BLACK);
403  gr.drawRect(rect.x-0,rect.y-0,rect.width+2,rect.height+2);
404 
405  gr.setColor(Color.YELLOW);
406  gr.drawRect(rect.x-1,rect.y-1,rect.width+2,rect.height+2);
407  }
408  }
409 
410  void paintTile(BufferedImage img, int x, int y, int tile)
411  {
412  assert tile >= 0;
413 
414  for (int yy = 0; yy < TILE_HEIGHT; yy++)
415  {
416  for (int xx = 0; xx < TILE_WIDTH; xx++)
417  {
418  img.setRGB(x*TILE_WIDTH+xx,y*TILE_HEIGHT+yy,
419  tileArrayImage.getRGB(xx,tile*TILE_OFFSET_Y+yy));
420  }
421  }
422  }
423 
424  Rectangle getViewRect(ConnectedView cv)
425  {
426  Rectangle rawRect = cv.scrollPane.getViewport().getViewRect();
427  return new Rectangle(
428  rawRect.x * 3 / cv.view.getTileSize(),
429  rawRect.y * 3 / cv.view.getTileSize(),
430  rawRect.width * 3 / cv.view.getTileSize(),
431  rawRect.height * 3 / cv.view.getTileSize()
432  );
433  }
434 
435  private void dragViewTo(Point p)
436  {
437  if (views.isEmpty())
438  return;
439 
440  ConnectedView cv = views.get(0);
441  Dimension d = cv.scrollPane.getViewport().getExtentSize();
442  Dimension mapSize = cv.scrollPane.getViewport().getViewSize();
443 
444  Point np = new Point(
445  p.x * cv.view.getTileSize() / 3 - d.width / 2,
446  p.y * cv.view.getTileSize() / 3 - d.height / 2
447  );
448  np.x = Math.max(0, Math.min(np.x, mapSize.width - d.width));
449  np.y = Math.max(0, Math.min(np.y, mapSize.height - d.height));
450 
451  cv.scrollPane.getViewport().setViewPosition(np);
452  }
453 
454  //implements Scrollable
456  {
457  return new Dimension(120,120);
458  }
459 
460  //implements Scrollable
461  public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
462  {
463  if (orientation == SwingConstants.VERTICAL)
464  return visibleRect.height;
465  else
466  return visibleRect.width;
467  }
468 
469  //implements Scrollable
471  {
472  return false;
473  }
474 
475  //implements Scrollable
477  {
478  return false;
479  }
480 
481  //implements Scrollable
482  public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
483  {
484  if (orientation == SwingConstants.VERTICAL)
485  return TILE_HEIGHT;
486  else
487  return TILE_WIDTH;
488  }
489 
490  //implements MapListener
491  public void mapOverlayDataChanged(MapState overlayDataType)
492  {
493  repaint();
494  }
495 
496  //implements MapListener
497  public void spriteMoved(Sprite sprite)
498  {
499  }
500 
501  //implements MapListener
502  public void tileChanged(int xpos, int ypos)
503  {
504  Rectangle r = new Rectangle(xpos*TILE_WIDTH, ypos * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
505  repaint(r);
506  }
507 
508  //implements MapListener
509  public void wholeMapChanged()
510  {
511  repaint();
512  engine.calculateCenterMass();
514  }
515 
516  public void dragViewToCityCenter()
517  {
518  dragViewTo(new Point(TILE_WIDTH * engine.getPlayerInfo().centerMassX + 1,
519  TILE_HEIGHT * engine.getPlayerInfo().centerMassY + 1));
520  }
521 
522  class ConnectedView implements ChangeListener
523  {
525  JScrollPane scrollPane;
526 
527  ConnectedView(MicropolisDrawingArea view, JScrollPane scrollPane)
528  {
529  this.view = view;
530  this.scrollPane = scrollPane;
531  scrollPane.getViewport().addChangeListener(this);
532  }
533 
534  public void stateChanged(ChangeEvent ev)
535  {
536  repaint();
537  }
538  }
539 
540  public void connectView(MicropolisDrawingArea view, JScrollPane scrollPane)
541  {
542  ConnectedView cv = new ConnectedView(view, scrollPane);
543  views.add(cv);
544  repaint();
545  }
546 
547  private void onMousePressed(MouseEvent ev)
548  {
549  if (ev.getButton() == MouseEvent.BUTTON1)
550  dragViewTo(ev.getPoint());
551  }
552 
553  private void onMouseDragged(MouseEvent ev)
554  {
555  if ((ev.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0)
556  return;
557 
558  dragViewTo(ev.getPoint());
559  }
560 }