Importing source code for MicropolisJ, the Java rewrite of Micropolis.

This edition of Micropolis, written for the Java desktop platform,
is fairly feature complete. I believe the only missing functionality
is that of loading the built-in scenarios, and this can be implemented
if there is any demand for it.

I will soon update the home page at http://code.google.com/p/micropolis/
with downloadable packages of this edition of the software.


git-svn-id: https://micropolis.googlecode.com/svn/trunk/micropolis-java@528 d9718cc8-9f43-0410-858b-315f434eb58c
This commit is contained in:
jason@long.name 2013-02-14 21:02:42 +00:00
parent 99e2b7dd01
commit ed6795dfca
189 changed files with 13184 additions and 0 deletions

View file

@ -0,0 +1,320 @@
// This file is part of MicropolisJ.
// Copyright (C) 2013 Jason Long
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
//
// MicropolisJ is free software; you can redistribute it and/or modify
// it under the terms of the GNU GPLv3, with additional terms.
// See the README file, included in this distribution, for details.
package micropolisj.gui;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.text.*;
import java.util.*;
import javax.swing.*;
import micropolisj.engine.*;
import static micropolisj.gui.ColorParser.parseColor;
public class GraphsPane extends JPanel
implements Micropolis.Listener
{
Micropolis engine;
JToggleButton tenYearsBtn;
JToggleButton onetwentyYearsBtn;
GraphArea graphArea;
static enum TimePeriod
{
TEN_YEARS,
ONETWENTY_YEARS;
}
static enum GraphData
{
RESPOP,
COMPOP,
INDPOP,
MONEY,
CRIME,
POLLUTION;
}
EnumMap<GraphData,JToggleButton> dataBtns = new EnumMap<>(GraphData.class);
static ResourceBundle strings = MainWindow.strings;
static final int LEFT_MARGIN = 4;
static final int RIGHT_MARGIN = 4;
static final int TOP_MARGIN = 2;
static final int BOTTOM_MARGIN = 2;
static final int LEGEND_PADDING = 6;
public GraphsPane(Micropolis engine)
{
super(new BorderLayout());
assert engine != null;
this.engine = engine;
engine.addListener(this);
JButton dismissBtn = new JButton(strings.getString("dismiss_graph"));
dismissBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
onDismissClicked();
}});
add(dismissBtn, BorderLayout.SOUTH);
JPanel b1 = new JPanel(new BorderLayout());
add(b1, BorderLayout.CENTER);
JPanel toolsPane = new JPanel(new GridBagLayout());
b1.add(toolsPane, BorderLayout.WEST);
GridBagConstraints c = new GridBagConstraints();
c.gridx = c.gridy = 0;
c.gridwidth = 2;
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(1,1,1,1);
tenYearsBtn = new JToggleButton(strings.getString("ten_years"));
tenYearsBtn.setMargin(new Insets(0,0,0,0));
tenYearsBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
setTimePeriod(TimePeriod.TEN_YEARS);
}});
toolsPane.add(tenYearsBtn, c);
c.gridy++;
onetwentyYearsBtn = new JToggleButton(strings.getString("onetwenty_years"));
onetwentyYearsBtn.setMargin(new Insets(0,0,0,0));
onetwentyYearsBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
setTimePeriod(TimePeriod.ONETWENTY_YEARS);
}});
toolsPane.add(onetwentyYearsBtn, c);
c.gridx = 0;
c.gridy = 2;
c.gridwidth = 1;
c.anchor = GridBagConstraints.NORTH;
c.weightx = 0.5;
toolsPane.add(makeDataBtn(GraphData.RESPOP), c);
c.gridy = 3;
toolsPane.add(makeDataBtn(GraphData.COMPOP), c);
c.gridy = 4;
toolsPane.add(makeDataBtn(GraphData.INDPOP), c);
c.gridx = 1;
c.gridy = 2;
toolsPane.add(makeDataBtn(GraphData.MONEY), c);
c.gridy = 3;
toolsPane.add(makeDataBtn(GraphData.CRIME), c);
c.gridy = 4;
toolsPane.add(makeDataBtn(GraphData.POLLUTION), c);
graphArea = new GraphArea();
b1.add(graphArea, BorderLayout.CENTER);
setTimePeriod(TimePeriod.TEN_YEARS);
dataBtns.get(GraphData.MONEY).setSelected(true);
dataBtns.get(GraphData.POLLUTION).setSelected(true);
}
public void setEngine(Micropolis newEngine)
{
if (engine != null) { //old engine
engine.removeListener(this);
}
engine = newEngine;
if (engine != null) { //new engine
engine.addListener(this);
graphArea.repaint();
}
}
private void onDismissClicked()
{
setVisible(false);
}
//implements Micropolis.Listener
public void cityMessage(MicropolisMessage message, CityLocation loc, boolean isPic) {}
public void citySound(Sound sound, CityLocation loc) {}
public void demandChanged() {}
public void evaluationChanged() {}
public void fundsChanged() {}
public void optionsChanged() {}
//implements Micropolis.Listener
public void censusChanged()
{
graphArea.repaint();
}
private JToggleButton makeDataBtn(GraphData graph)
{
String icon1name = strings.getString("graph_button."+graph.name());
String icon2name = strings.getString("graph_button."+graph.name()+".selected");
ImageIcon icon1 = new ImageIcon(getClass().getResource("/"+icon1name));
ImageIcon icon2 = new ImageIcon(getClass().getResource("/"+icon2name));
JToggleButton btn = new JToggleButton();
btn.setIcon(icon1);
btn.setSelectedIcon(icon2);
btn.setBorder(null);
btn.setBorderPainted(false);
btn.setFocusPainted(false);
btn.setContentAreaFilled(false);
btn.setMargin(new Insets(0,0,0,0));
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
graphArea.repaint();
}});
dataBtns.put(graph, btn);
return btn;
}
int getHistoryValue(GraphData graph, int pos)
{
assert pos >= 0 && pos < 240;
switch(graph) {
case RESPOP: return engine.history.res[pos];
case COMPOP: return engine.history.com[pos];
case INDPOP: return engine.history.ind[pos];
case MONEY: return engine.history.money[pos];
case CRIME: return engine.history.crime[pos];
case POLLUTION: return engine.history.pollution[pos];
default: throw new Error("unexpected");
}
}
void setTimePeriod(TimePeriod period)
{
tenYearsBtn.setSelected(period == TimePeriod.TEN_YEARS);
onetwentyYearsBtn.setSelected(period == TimePeriod.ONETWENTY_YEARS);
graphArea.repaint();
}
class GraphArea extends JComponent
{
GraphArea()
{
setBorder(BorderFactory.createLoweredBevelBorder());
}
@Override
public void paintComponent(Graphics gr1)
{
Graphics2D gr = (Graphics2D)gr1;
FontMetrics fm = gr.getFontMetrics();
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gr.setColor(Color.WHITE);
gr.fill(gr.getClipBounds());
// determine length of longest label
int maxLabelWidth = 0;
for (GraphData gd : GraphData.values())
{
String labelStr = strings.getString("graph_label."+gd.name());
int adv = fm.stringWidth(labelStr);
if (adv > maxLabelWidth) {
maxLabelWidth = adv;
}
}
int leftEdge = getInsets().left + LEFT_MARGIN;
int topEdge = getInsets().top + TOP_MARGIN + fm.getHeight()*2;
int bottomEdge = getHeight() - getInsets().bottom - getInsets().top - BOTTOM_MARGIN;
int rightEdge = getWidth() - getInsets().right - getInsets().left - RIGHT_MARGIN - maxLabelWidth - LEGEND_PADDING;
// draw graph lower, upper borders
gr.setColor(Color.BLACK);
gr.drawLine(leftEdge,topEdge,rightEdge,topEdge);
gr.drawLine(leftEdge,bottomEdge,rightEdge,bottomEdge);
// draw vertical bars and label the dates
boolean isOneTwenty = onetwentyYearsBtn.isSelected();
int unitPeriod = isOneTwenty ? 12*Micropolis.CENSUSRATE : Micropolis.CENSUSRATE;
int hashPeriod = isOneTwenty ? 10*unitPeriod : 12*unitPeriod;
int startTime = ((engine.history.cityTime / unitPeriod) - 119) * unitPeriod;
double x_interval = (rightEdge - leftEdge) / 120.0;
for (int i = 0; i < 120; i++) {
int t = startTime + i * unitPeriod; // t might be negative
if (t % hashPeriod == 0) {
// year
int year = 1900+(t/(12*Micropolis.CENSUSRATE));
int numHashes = t/hashPeriod;
int x = (int)Math.round(leftEdge+i*x_interval);
int y = getInsets().top + TOP_MARGIN +
(numHashes % 2 == 0 ? fm.getHeight() : 0) +
fm.getAscent();
gr.drawString(Integer.toString(year), x, y);
gr.drawLine(x,topEdge,x,bottomEdge);
}
}
int H = isOneTwenty ? 239 : 119;
final HashMap<GraphData, Path2D.Double> paths = new HashMap<>();
for (GraphData gd : GraphData.values())
{
if (dataBtns.get(gd).isSelected()) {
Path2D.Double path = new Path2D.Double();
for (int i = 0; i < 120; i++) {
double xp = leftEdge + i * x_interval;
double yp = bottomEdge - getHistoryValue(gd,H-i) * (bottomEdge-topEdge) / 256.0;
if (i == 0) {
path.moveTo(xp, yp);
} else {
path.lineTo(xp, yp);
}
}
paths.put(gd, path);
}
}
GraphData [] myGraphs = paths.keySet().toArray(new GraphData[0]);
Arrays.sort(myGraphs, new Comparator<GraphData>() {
public int compare(GraphData a, GraphData b) {
double y0 = paths.get(a).getCurrentPoint().getY();
double y1 = paths.get(b).getCurrentPoint().getY();
return -Double.compare(y0,y1);
}});
int lbottom = bottomEdge;
for (GraphData gd : myGraphs)
{
String labelStr = strings.getString("graph_label."+gd.name());
String colStr = strings.getString("graph_color."+gd.name());
Color col = parseColor(colStr);
Path2D.Double path = paths.get(gd);
gr.setColor(col);
gr.setStroke(new BasicStroke(2));
gr.draw(path);
int x = rightEdge + LEGEND_PADDING;
int y = (int)Math.round(path.getCurrentPoint().getY()+fm.getAscent()/2);
y = Math.min(lbottom, y);
lbottom = y - fm.getAscent();
gr.setColor(col);
gr.drawString(labelStr, x-1, y);
gr.drawString(labelStr, x, y-1);
gr.setColor(Color.BLACK);
gr.drawString(labelStr, x, y);
}
}
}
}