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:
parent
99e2b7dd01
commit
ed6795dfca
189 changed files with 13184 additions and 0 deletions
335
src/micropolisj/gui/BudgetDialog.java
Normal file
335
src/micropolisj/gui/BudgetDialog.java
Normal file
|
@ -0,0 +1,335 @@
|
|||
// 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 javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.*;
|
||||
|
||||
import micropolisj.engine.*;
|
||||
import static micropolisj.gui.MainWindow.formatFunds;
|
||||
import static micropolisj.gui.MainWindow.formatGameDate;
|
||||
|
||||
public class BudgetDialog extends JDialog
|
||||
{
|
||||
Micropolis engine;
|
||||
|
||||
JSpinner taxRateEntry;
|
||||
int origTaxRate;
|
||||
double origRoadPct;
|
||||
double origFirePct;
|
||||
double origPolicePct;
|
||||
|
||||
JLabel roadFundAlloc = new JLabel();
|
||||
JSpinner roadFundEntry;
|
||||
|
||||
JLabel policeFundAlloc = new JLabel();
|
||||
JSpinner policeFundEntry;
|
||||
|
||||
JLabel fireFundAlloc = new JLabel();
|
||||
JSpinner fireFundEntry;
|
||||
|
||||
JLabel taxRevenueLbl = new JLabel();
|
||||
|
||||
static ResourceBundle strings = MainWindow.strings;
|
||||
|
||||
JCheckBox autoBudgetBtn = new JCheckBox(strings.getString("budgetdlg.auto_budget"));
|
||||
JCheckBox pauseBtn = new JCheckBox(strings.getString("budgetdlg.pause_game"));
|
||||
|
||||
private void applyChange()
|
||||
{
|
||||
int newTaxRate = ((Number) taxRateEntry.getValue()).intValue();
|
||||
int newRoadPct = ((Number) roadFundEntry.getValue()).intValue();
|
||||
int newPolicePct = ((Number) policeFundEntry.getValue()).intValue();
|
||||
int newFirePct = ((Number) fireFundEntry.getValue()).intValue();
|
||||
|
||||
engine.cityTax = newTaxRate;
|
||||
engine.roadPercent = (double)newRoadPct / 100.0;
|
||||
engine.policePercent = (double)newPolicePct / 100.0;
|
||||
engine.firePercent = (double)newFirePct / 100.0;
|
||||
|
||||
loadBudgetNumbers(false);
|
||||
}
|
||||
|
||||
private void loadBudgetNumbers(boolean updateEntries)
|
||||
{
|
||||
BudgetNumbers b = engine.generateBudget();
|
||||
if (updateEntries)
|
||||
{
|
||||
taxRateEntry.setValue(b.taxRate);
|
||||
roadFundEntry.setValue((int)Math.round(b.roadPercent*100.0));
|
||||
policeFundEntry.setValue((int)Math.round(b.policePercent*100.0));
|
||||
fireFundEntry.setValue((int)Math.round(b.firePercent*100.0));
|
||||
}
|
||||
|
||||
taxRevenueLbl.setText(formatFunds(b.taxIncome));
|
||||
roadFundAlloc.setText(formatFunds(b.roadFunded));
|
||||
policeFundAlloc.setText(formatFunds(b.policeFunded));
|
||||
fireFundAlloc.setText(formatFunds(b.fireFunded));
|
||||
}
|
||||
|
||||
public BudgetDialog(Window owner, Micropolis engine)
|
||||
{
|
||||
super(owner);
|
||||
setTitle(strings.getString("budgetdlg.title"));
|
||||
|
||||
this.engine = engine;
|
||||
this.origTaxRate = engine.cityTax;
|
||||
this.origRoadPct = engine.roadPercent;
|
||||
this.origFirePct = engine.firePercent;
|
||||
this.origPolicePct = engine.policePercent;
|
||||
|
||||
// give text fields of the fund-level spinners a minimum size
|
||||
taxRateEntry = new JSpinner(new SpinnerNumberModel(7,0,20,1));
|
||||
roadFundEntry = new JSpinner(new SpinnerNumberModel(100,0,100,1));
|
||||
fireFundEntry = new JSpinner(new SpinnerNumberModel(1,0,100,1));
|
||||
policeFundEntry = new JSpinner(new SpinnerNumberModel(10,0,100,1));
|
||||
|
||||
ChangeListener change = new ChangeListener() {
|
||||
public void stateChanged(ChangeEvent ev) {
|
||||
applyChange();
|
||||
}
|
||||
};
|
||||
taxRateEntry.addChangeListener(change);
|
||||
roadFundEntry.addChangeListener(change);
|
||||
fireFundEntry.addChangeListener(change);
|
||||
policeFundEntry.addChangeListener(change);
|
||||
|
||||
Box mainBox = new Box(BoxLayout.Y_AXIS);
|
||||
mainBox.setBorder(BorderFactory.createEmptyBorder(8,8,8,8));
|
||||
add(mainBox, BorderLayout.CENTER);
|
||||
|
||||
mainBox.add(makeTaxPane());
|
||||
|
||||
JSeparator sep = new JSeparator(SwingConstants.HORIZONTAL);
|
||||
mainBox.add(sep);
|
||||
|
||||
JPanel fundingRatesPane = new JPanel(new GridBagLayout());
|
||||
fundingRatesPane.setBorder(BorderFactory.createEmptyBorder(8,0,8,0));
|
||||
mainBox.add(fundingRatesPane);
|
||||
|
||||
GridBagConstraints c0 = new GridBagConstraints();
|
||||
c0.gridx = 0;
|
||||
c0.weightx = 0.25;
|
||||
c0.anchor = GridBagConstraints.WEST;
|
||||
GridBagConstraints c1 = new GridBagConstraints();
|
||||
c1.gridx = 1;
|
||||
c1.weightx = 0.25;
|
||||
c1.anchor = GridBagConstraints.EAST;
|
||||
GridBagConstraints c2 = new GridBagConstraints();
|
||||
c2.gridx = 2;
|
||||
c2.weightx = 0.5;
|
||||
c2.anchor = GridBagConstraints.EAST;
|
||||
|
||||
c1.gridy = c2.gridy = 0;
|
||||
fundingRatesPane.add(new JLabel(strings.getString("budgetdlg.funding_level_hdr")), c1);
|
||||
fundingRatesPane.add(new JLabel(strings.getString("budgetdlg.allocation_hdr")), c2);
|
||||
|
||||
c0.gridy = c1.gridy = c2.gridy = 1;
|
||||
fundingRatesPane.add(new JLabel(strings.getString("budgetdlg.road_fund")), c0);
|
||||
fundingRatesPane.add(roadFundEntry, c1);
|
||||
fundingRatesPane.add(roadFundAlloc, c2);
|
||||
|
||||
c0.gridy = c1.gridy = c2.gridy = 2;
|
||||
fundingRatesPane.add(new JLabel(strings.getString("budgetdlg.police_fund")), c0);
|
||||
fundingRatesPane.add(policeFundEntry, c1);
|
||||
fundingRatesPane.add(policeFundAlloc, c2);
|
||||
|
||||
c0.gridy = c1.gridy = c2.gridy = 3;
|
||||
fundingRatesPane.add(new JLabel(strings.getString("budgetdlg.fire_fund")), c0);
|
||||
fundingRatesPane.add(fireFundEntry, c1);
|
||||
fundingRatesPane.add(fireFundAlloc, c2);
|
||||
|
||||
JSeparator sep1 = new JSeparator(SwingConstants.HORIZONTAL);
|
||||
mainBox.add(sep1);
|
||||
|
||||
JPanel balancePane = new JPanel(new GridBagLayout());
|
||||
balancePane.setBorder(BorderFactory.createEmptyBorder(8,24,8,24));
|
||||
mainBox.add(balancePane);
|
||||
|
||||
makeBalancePane(balancePane);
|
||||
|
||||
JSeparator sep2 = new JSeparator(SwingConstants.HORIZONTAL);
|
||||
mainBox.add(sep2);
|
||||
|
||||
JPanel optionsPane = new JPanel(new GridBagLayout());
|
||||
optionsPane.setBorder(BorderFactory.createEmptyBorder(8,0,0,0));
|
||||
mainBox.add(optionsPane);
|
||||
|
||||
c0.anchor = c1.anchor = GridBagConstraints.WEST;
|
||||
c0.gridy = c1.gridy = 0;
|
||||
c0.weightx = c1.weightx = 0.5;
|
||||
optionsPane.add(autoBudgetBtn, c0);
|
||||
optionsPane.add(pauseBtn, c1);
|
||||
|
||||
autoBudgetBtn.setSelected(engine.autoBudget);
|
||||
pauseBtn.setSelected(engine.simSpeed == Speed.PAUSED);
|
||||
|
||||
JPanel buttonPane = new JPanel();
|
||||
add(buttonPane, BorderLayout.SOUTH);
|
||||
|
||||
JButton continueBtn = new JButton(strings.getString("budgetdlg.continue"));
|
||||
continueBtn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
onContinueClicked();
|
||||
}});
|
||||
buttonPane.add(continueBtn);
|
||||
|
||||
JButton resetBtn = new JButton(strings.getString("budgetdlg.reset"));
|
||||
resetBtn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
onResetClicked();
|
||||
}});
|
||||
buttonPane.add(resetBtn);
|
||||
|
||||
loadBudgetNumbers(true);
|
||||
setAutoRequestFocus(false);
|
||||
pack();
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setLocationRelativeTo(owner);
|
||||
}
|
||||
|
||||
private JComponent makeTaxPane()
|
||||
{
|
||||
JPanel pane = new JPanel(new GridBagLayout());
|
||||
pane.setBorder(BorderFactory.createEmptyBorder(0,0,8,0));
|
||||
|
||||
GridBagConstraints c0 = new GridBagConstraints();
|
||||
GridBagConstraints c1 = new GridBagConstraints();
|
||||
GridBagConstraints c2 = new GridBagConstraints();
|
||||
|
||||
c0.gridx = 0;
|
||||
c0.anchor = GridBagConstraints.WEST;
|
||||
c0.weightx = 0.25;
|
||||
c1.gridx = 1;
|
||||
c1.anchor = GridBagConstraints.EAST;
|
||||
c1.weightx = 0.25;
|
||||
c2.gridx = 2;
|
||||
c2.anchor = GridBagConstraints.EAST;
|
||||
c2.weightx = 0.5;
|
||||
|
||||
c0.gridy = c1.gridy = c2.gridy = 0;
|
||||
pane.add(new JLabel(strings.getString("budgetdlg.tax_rate_hdr")), c1);
|
||||
pane.add(new JLabel(strings.getString("budgetdlg.annual_receipts_hdr")), c2);
|
||||
|
||||
c0.gridy = c1.gridy = c2.gridy = 1;
|
||||
pane.add(new JLabel(strings.getString("budgetdlg.tax_revenue")), c0);
|
||||
pane.add(taxRateEntry, c1);
|
||||
pane.add(taxRevenueLbl, c2);
|
||||
|
||||
return pane;
|
||||
}
|
||||
|
||||
private void onContinueClicked()
|
||||
{
|
||||
if (autoBudgetBtn.isSelected() != engine.autoBudget) {
|
||||
engine.toggleAutoBudget();
|
||||
}
|
||||
if (pauseBtn.isSelected() && engine.simSpeed != Speed.PAUSED) {
|
||||
engine.setSpeed(Speed.PAUSED);
|
||||
}
|
||||
else if (!pauseBtn.isSelected() && engine.simSpeed == Speed.PAUSED) {
|
||||
engine.setSpeed(Speed.NORMAL);
|
||||
}
|
||||
|
||||
dispose();
|
||||
}
|
||||
|
||||
private void onResetClicked()
|
||||
{
|
||||
engine.cityTax = this.origTaxRate;
|
||||
engine.roadPercent = this.origRoadPct;
|
||||
engine.firePercent = this.origFirePct;
|
||||
engine.policePercent = this.origPolicePct;
|
||||
loadBudgetNumbers(true);
|
||||
}
|
||||
|
||||
private JComponent makeBalancePane(JPanel balancePane)
|
||||
{
|
||||
GridBagConstraints c0 = new GridBagConstraints();
|
||||
GridBagConstraints c1 = new GridBagConstraints();
|
||||
|
||||
c0.anchor = GridBagConstraints.WEST;
|
||||
c0.weightx = 0.5;
|
||||
c0.gridx = 0;
|
||||
c0.gridy = 0;
|
||||
|
||||
JLabel thLbl = new JLabel(strings.getString("budgetdlg.period_ending"));
|
||||
Font origFont = thLbl.getFont();
|
||||
Font headFont = origFont.deriveFont(Font.ITALIC);
|
||||
thLbl.setFont(headFont);
|
||||
thLbl.setForeground(Color.MAGENTA);
|
||||
balancePane.add(thLbl, c0);
|
||||
|
||||
c0.gridy++;
|
||||
balancePane.add(new JLabel(strings.getString("budgetdlg.cash_begin")), c0);
|
||||
c0.gridy++;
|
||||
balancePane.add(new JLabel(strings.getString("budgetdlg.taxes_collected")), c0);
|
||||
c0.gridy++;
|
||||
balancePane.add(new JLabel(strings.getString("budgetdlg.capital_expenses")), c0);
|
||||
c0.gridy++;
|
||||
balancePane.add(new JLabel(strings.getString("budgetdlg.operating_expenses")), c0);
|
||||
c0.gridy++;
|
||||
balancePane.add(new JLabel(strings.getString("budgetdlg.cash_end")), c0);
|
||||
|
||||
c1.anchor = GridBagConstraints.EAST;
|
||||
c1.weightx = 0.25;
|
||||
c1.gridx = 0;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
if (i + 1 >= engine.financialHistory.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Micropolis.FinancialHistory f = engine.financialHistory.get(i);
|
||||
Micropolis.FinancialHistory fPrior = engine.financialHistory.get(i+1);
|
||||
int cashFlow = f.totalFunds - fPrior.totalFunds;
|
||||
int capExpenses = -(cashFlow - f.taxIncome + f.operatingExpenses);
|
||||
|
||||
c1.gridx++;
|
||||
c1.gridy = 0;
|
||||
|
||||
thLbl = new JLabel(formatGameDate(f.cityTime-1));
|
||||
thLbl.setFont(headFont);
|
||||
thLbl.setForeground(Color.MAGENTA);
|
||||
balancePane.add(thLbl, c1);
|
||||
|
||||
c1.gridy++;
|
||||
JLabel previousBalanceLbl = new JLabel();
|
||||
previousBalanceLbl.setText(formatFunds(fPrior.totalFunds));
|
||||
balancePane.add(previousBalanceLbl, c1);
|
||||
|
||||
c1.gridy++;
|
||||
JLabel taxIncomeLbl = new JLabel();
|
||||
taxIncomeLbl.setText(formatFunds(f.taxIncome));
|
||||
balancePane.add(taxIncomeLbl, c1);
|
||||
|
||||
c1.gridy++;
|
||||
JLabel capExpensesLbl = new JLabel();
|
||||
capExpensesLbl.setText(formatFunds(capExpenses));
|
||||
balancePane.add(capExpensesLbl, c1);
|
||||
|
||||
c1.gridy++;
|
||||
JLabel opExpensesLbl = new JLabel();
|
||||
opExpensesLbl.setText(formatFunds(f.operatingExpenses));
|
||||
balancePane.add(opExpensesLbl, c1);
|
||||
|
||||
c1.gridy++;
|
||||
JLabel newBalanceLbl = new JLabel();
|
||||
newBalanceLbl.setText(formatFunds(f.totalFunds));
|
||||
balancePane.add(newBalanceLbl, c1);
|
||||
}
|
||||
|
||||
return balancePane;
|
||||
}
|
||||
}
|
35
src/micropolisj/gui/ColorParser.java
Normal file
35
src/micropolisj/gui/ColorParser.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
// 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.Color;
|
||||
|
||||
public class ColorParser
|
||||
{
|
||||
private ColorParser() {}
|
||||
|
||||
static Color parseColor(String str)
|
||||
{
|
||||
if (str.startsWith("#") && str.length() == 7) {
|
||||
return new Color(Integer.parseInt(str.substring(1), 16));
|
||||
}
|
||||
else if (str.startsWith("rgba(") && str.endsWith(")")) {
|
||||
String [] parts = str.substring(5,str.length()-1).split(",");
|
||||
int r = Integer.parseInt(parts[0]);
|
||||
int g = Integer.parseInt(parts[1]);
|
||||
int b = Integer.parseInt(parts[2]);
|
||||
double aa = Double.parseDouble(parts[3]);
|
||||
int a = Math.min(255, (int)Math.floor(aa*256.0));
|
||||
return new Color(r,g,b,a);
|
||||
}
|
||||
else {
|
||||
throw new Error("invalid color format: "+str);
|
||||
}
|
||||
}
|
||||
}
|
146
src/micropolisj/gui/DemandIndicator.java
Normal file
146
src/micropolisj/gui/DemandIndicator.java
Normal file
|
@ -0,0 +1,146 @@
|
|||
// 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.image.*;
|
||||
import java.net.URL;
|
||||
import javax.swing.*;
|
||||
|
||||
import micropolisj.engine.*;
|
||||
|
||||
public class DemandIndicator extends JComponent
|
||||
implements Micropolis.Listener
|
||||
{
|
||||
Micropolis engine;
|
||||
|
||||
public DemandIndicator()
|
||||
{
|
||||
}
|
||||
|
||||
public void setEngine(Micropolis newEngine)
|
||||
{
|
||||
if (engine != null) { //old engine
|
||||
engine.removeListener(this);
|
||||
}
|
||||
|
||||
engine = newEngine;
|
||||
|
||||
if (engine != null) { //new engine
|
||||
engine.addListener(this);
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
static final BufferedImage backgroundImage = loadImage("/demandg.png");
|
||||
static BufferedImage loadImage(String resourceName)
|
||||
{
|
||||
URL iconUrl = MicropolisDrawingArea.class.getResource(resourceName);
|
||||
Image refImage = new ImageIcon(iconUrl).getImage();
|
||||
|
||||
BufferedImage bi = new BufferedImage(refImage.getWidth(null), refImage.getHeight(null),
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D gr = bi.createGraphics();
|
||||
gr.drawImage(refImage, 0, 0, null);
|
||||
|
||||
return bi;
|
||||
}
|
||||
|
||||
static final Dimension MY_SIZE = new Dimension(
|
||||
backgroundImage.getWidth(),
|
||||
backgroundImage.getHeight()
|
||||
);
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize()
|
||||
{
|
||||
return MY_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return MY_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMaximumSize()
|
||||
{
|
||||
return MY_SIZE;
|
||||
}
|
||||
|
||||
static final int UPPER_EDGE = 19;
|
||||
static final int LOWER_EDGE = 28;
|
||||
static final int MAX_LENGTH = 16;
|
||||
|
||||
public void paintComponent(Graphics gr1)
|
||||
{
|
||||
Graphics2D gr = (Graphics2D) gr1;
|
||||
gr.drawImage(backgroundImage, 0, 0, null);
|
||||
|
||||
if (engine == null)
|
||||
return;
|
||||
|
||||
int resValve = engine.getResValve();
|
||||
int ry0 = resValve <= 0 ? LOWER_EDGE : UPPER_EDGE;
|
||||
int ry1 = ry0 - resValve/100;
|
||||
|
||||
if (ry1 - ry0 > MAX_LENGTH) { ry1 = ry0 + MAX_LENGTH; }
|
||||
if (ry1 - ry0 < -MAX_LENGTH) { ry1 = ry0 - MAX_LENGTH; }
|
||||
|
||||
int comValve = engine.getComValve();
|
||||
int cy0 = comValve <= 0 ? LOWER_EDGE : UPPER_EDGE;
|
||||
int cy1 = cy0 - comValve/100;
|
||||
|
||||
int indValve = engine.getIndValve();
|
||||
int iy0 = indValve <= 0 ? LOWER_EDGE : UPPER_EDGE;
|
||||
int iy1 = iy0 - indValve/100;
|
||||
|
||||
if (ry0 != ry1)
|
||||
{
|
||||
Rectangle resRect = new Rectangle(8, Math.min(ry0,ry1), 6, Math.abs(ry1-ry0));
|
||||
gr.setColor(Color.GREEN);
|
||||
gr.fill(resRect);
|
||||
gr.setColor(Color.BLACK);
|
||||
gr.draw(resRect);
|
||||
}
|
||||
|
||||
if (cy0 != cy1)
|
||||
{
|
||||
Rectangle comRect = new Rectangle(17, Math.min(cy0,cy1), 6, Math.abs(cy1-cy0));
|
||||
gr.setColor(Color.BLUE);
|
||||
gr.fill(comRect);
|
||||
gr.setColor(Color.BLACK);
|
||||
gr.draw(comRect);
|
||||
}
|
||||
|
||||
if (iy0 != iy1)
|
||||
{
|
||||
Rectangle indRect = new Rectangle(26, Math.min(iy0,iy1), 6, Math.abs(iy1-iy0));
|
||||
gr.setColor(Color.YELLOW);
|
||||
gr.fill(indRect);
|
||||
gr.setColor(Color.BLACK);
|
||||
gr.draw(indRect);
|
||||
}
|
||||
}
|
||||
|
||||
//implements Micropolis.Listener
|
||||
public void demandChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
//implements Micropolis.Listener
|
||||
public void cityMessage(MicropolisMessage m, CityLocation p, boolean x) { }
|
||||
public void citySound(Sound sound, CityLocation p) { }
|
||||
public void censusChanged() { }
|
||||
public void evaluationChanged() { }
|
||||
public void fundsChanged() { }
|
||||
public void optionsChanged() { }
|
||||
}
|
298
src/micropolisj/gui/EvaluationPane.java
Normal file
298
src/micropolisj/gui/EvaluationPane.java
Normal file
|
@ -0,0 +1,298 @@
|
|||
// 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.text.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
import micropolisj.engine.*;
|
||||
import static micropolisj.gui.MainWindow.formatFunds;
|
||||
|
||||
public class EvaluationPane extends JPanel
|
||||
implements Micropolis.Listener
|
||||
{
|
||||
Micropolis engine;
|
||||
|
||||
JLabel yesLbl;
|
||||
JLabel noLbl;
|
||||
JLabel [] voterProblemLbl;
|
||||
JLabel [] voterCountLbl;
|
||||
JLabel popLbl;
|
||||
JLabel deltaLbl;
|
||||
JLabel assessLbl;
|
||||
JLabel cityClassLbl;
|
||||
JLabel gameLevelLbl;
|
||||
JLabel scoreLbl;
|
||||
JLabel scoreDeltaLbl;
|
||||
|
||||
static ResourceBundle cstrings = ResourceBundle.getBundle("micropolisj.CityStrings");
|
||||
static ResourceBundle gstrings = MainWindow.strings;
|
||||
|
||||
public EvaluationPane(Micropolis _engine)
|
||||
{
|
||||
super(new BorderLayout());
|
||||
|
||||
JButton dismissBtn = new JButton(gstrings.getString("dismiss-evaluation"));
|
||||
dismissBtn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
onDismissClicked();
|
||||
}});
|
||||
add(dismissBtn, BorderLayout.SOUTH);
|
||||
|
||||
Box b1 = new Box(BoxLayout.X_AXIS);
|
||||
add(b1, BorderLayout.CENTER);
|
||||
|
||||
b1.add(makePublicOpinionPane());
|
||||
b1.add(new JSeparator(SwingConstants.VERTICAL));
|
||||
b1.add(makeStatisticsPane());
|
||||
|
||||
assert _engine != null;
|
||||
setEngine(_engine);
|
||||
}
|
||||
|
||||
public void setEngine(Micropolis newEngine)
|
||||
{
|
||||
if (engine != null) { //old engine
|
||||
engine.removeListener(this);
|
||||
}
|
||||
engine = newEngine;
|
||||
if (engine != null) { //new engine
|
||||
engine.addListener(this);
|
||||
loadEvaluation();
|
||||
}
|
||||
}
|
||||
|
||||
private void onDismissClicked()
|
||||
{
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private JComponent makePublicOpinionPane()
|
||||
{
|
||||
JPanel me = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints c1 = new GridBagConstraints();
|
||||
GridBagConstraints c2 = new GridBagConstraints();
|
||||
GridBagConstraints c3 = new GridBagConstraints();
|
||||
|
||||
// c1 is for the full-width headers
|
||||
c1.gridx = c1.gridy = 0;
|
||||
c1.gridwidth = 2;
|
||||
c1.gridheight = 1;
|
||||
c1.weightx = 1.0;
|
||||
c1.fill = GridBagConstraints.NONE;
|
||||
c1.anchor = GridBagConstraints.NORTH;
|
||||
|
||||
JLabel headerLbl = new JLabel(gstrings.getString("public-opinion"));
|
||||
Font curFont = headerLbl.getFont();
|
||||
headerLbl.setFont(
|
||||
curFont.deriveFont(curFont.getStyle() | Font.BOLD, (float)(curFont.getSize() * 1.2))
|
||||
);
|
||||
me.add(headerLbl, c1);
|
||||
|
||||
c1.gridy = 1;
|
||||
c1.insets = new Insets(3, 0, 3, 0);
|
||||
me.add(new JLabel(gstrings.getString("public-opinion-1")), c1);
|
||||
|
||||
c1.gridy = 4;
|
||||
me.add(new JLabel(gstrings.getString("public-opinion-2")), c1);
|
||||
|
||||
c2.gridx = 0;
|
||||
c2.gridy = 2;
|
||||
c2.gridwidth = c2.gridheight = 1;
|
||||
c2.weightx = 1.0;
|
||||
c2.anchor = GridBagConstraints.EAST;
|
||||
c2.insets = new Insets(0, 0, 0, 4);
|
||||
|
||||
me.add(new JLabel(gstrings.getString("public-opinion-yes")), c2);
|
||||
|
||||
c2.gridy = 3;
|
||||
me.add(new JLabel(gstrings.getString("public-opinion-no")), c2);
|
||||
|
||||
c3.gridx = 1;
|
||||
c3.gridwidth = c3.gridheight = 1;
|
||||
c3.weightx = 1.0;
|
||||
c3.anchor = GridBagConstraints.WEST;
|
||||
c3.insets = new Insets(0, 4, 0, 0);
|
||||
|
||||
c3.gridy = 2;
|
||||
yesLbl = new JLabel();
|
||||
me.add(yesLbl, c3);
|
||||
|
||||
c3.gridy = 3;
|
||||
noLbl = new JLabel();
|
||||
me.add(noLbl, c3);
|
||||
|
||||
c2.gridy = c3.gridy = 5;
|
||||
|
||||
final int NUM_PROBS = 4;
|
||||
voterProblemLbl = new JLabel[NUM_PROBS];
|
||||
voterCountLbl = new JLabel[NUM_PROBS];
|
||||
for (int i = 0; i < NUM_PROBS; i++) {
|
||||
voterProblemLbl[i] = new JLabel();
|
||||
me.add(voterProblemLbl[i], c2);
|
||||
|
||||
voterCountLbl[i] = new JLabel();
|
||||
me.add(voterCountLbl[i], c3);
|
||||
|
||||
c2.gridy = ++c3.gridy;
|
||||
}
|
||||
|
||||
// add glue so that everything will align towards the top
|
||||
c1.gridy = 999;
|
||||
c1.weighty = 1.0;
|
||||
me.add(new JLabel(), c1);
|
||||
|
||||
return me;
|
||||
}
|
||||
|
||||
private JComponent makeStatisticsPane()
|
||||
{
|
||||
JPanel me = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints c1 = new GridBagConstraints();
|
||||
GridBagConstraints c2 = new GridBagConstraints();
|
||||
GridBagConstraints c3 = new GridBagConstraints();
|
||||
|
||||
c1.gridx = c1.gridy = 0;
|
||||
c1.gridwidth = 2;
|
||||
c1.gridheight = 1;
|
||||
c1.weightx = 1.0;
|
||||
c1.fill = GridBagConstraints.NONE;
|
||||
c1.anchor = GridBagConstraints.NORTH;
|
||||
c1.insets = new Insets(0,0,3,0);
|
||||
|
||||
JLabel headerLbl = new JLabel(gstrings.getString("statistics-head"));
|
||||
Font curFont = headerLbl.getFont();
|
||||
headerLbl.setFont(
|
||||
curFont.deriveFont(curFont.getStyle() | Font.BOLD, (float)(curFont.getSize() * 1.2))
|
||||
);
|
||||
me.add(headerLbl, c1);
|
||||
|
||||
c1.gridy = 20;
|
||||
c1.insets = new Insets(9, 0, 3, 0);
|
||||
c1.fill = GridBagConstraints.VERTICAL;
|
||||
JLabel header2Lbl = new JLabel(gstrings.getString("city-score-head"));
|
||||
me.add(header2Lbl, c1);
|
||||
|
||||
c2.gridx = 0;
|
||||
c2.gridwidth = c2.gridheight = 1;
|
||||
c2.weightx = 0.5;
|
||||
c2.anchor = GridBagConstraints.EAST;
|
||||
c2.insets = new Insets(0, 0, 0, 4);
|
||||
|
||||
c3.gridx = 1;
|
||||
c3.gridwidth = c3.gridheight = 1;
|
||||
c3.weightx = 0.5;
|
||||
c3.anchor = GridBagConstraints.WEST;
|
||||
c3.insets = new Insets(0, 4, 0, 0);
|
||||
|
||||
c2.gridy = c3.gridy = 1;
|
||||
me.add(new JLabel(gstrings.getString("stats-population")), c2);
|
||||
popLbl = new JLabel();
|
||||
me.add(popLbl, c3);
|
||||
|
||||
c2.gridy = ++c3.gridy;
|
||||
me.add(new JLabel(gstrings.getString("stats-net-migration")), c2);
|
||||
deltaLbl = new JLabel();
|
||||
me.add(deltaLbl, c3);
|
||||
|
||||
c2.gridy = ++c3.gridy;
|
||||
me.add(new JLabel(gstrings.getString("stats-last-year")), c2);
|
||||
|
||||
c2.gridy = ++c3.gridy;
|
||||
me.add(new JLabel(gstrings.getString("stats-assessed-value")), c2);
|
||||
assessLbl = new JLabel();
|
||||
me.add(assessLbl, c3);
|
||||
|
||||
c2.gridy = ++c3.gridy;
|
||||
me.add(new JLabel(gstrings.getString("stats-category")), c2);
|
||||
cityClassLbl = new JLabel();
|
||||
me.add(cityClassLbl, c3);
|
||||
|
||||
c2.gridy = ++c3.gridy;
|
||||
me.add(new JLabel(gstrings.getString("stats-game-level")), c2);
|
||||
gameLevelLbl = new JLabel();
|
||||
me.add(gameLevelLbl, c3);
|
||||
|
||||
c2.gridy = c3.gridy = 21;
|
||||
me.add(new JLabel(gstrings.getString("city-score-current")), c2);
|
||||
scoreLbl = new JLabel();
|
||||
me.add(scoreLbl, c3);
|
||||
|
||||
c2.gridy = ++c3.gridy;
|
||||
me.add(new JLabel(gstrings.getString("city-score-change")), c2);
|
||||
scoreDeltaLbl = new JLabel();
|
||||
me.add(scoreDeltaLbl, c3);
|
||||
|
||||
// add glue so that everything will align towards the top
|
||||
c1.gridy = 999;
|
||||
c1.weighty = 1.0;
|
||||
c1.insets = new Insets(0,0,0,0);
|
||||
me.add(new JLabel(), c1);
|
||||
|
||||
return me;
|
||||
}
|
||||
|
||||
//implements Micropolis.Listener
|
||||
public void cityMessage(MicropolisMessage message, CityLocation loc, boolean isPic) {}
|
||||
public void citySound(Sound sound, CityLocation loc) {}
|
||||
public void censusChanged() {}
|
||||
public void demandChanged() {}
|
||||
public void fundsChanged() {}
|
||||
public void optionsChanged() {}
|
||||
|
||||
//implements Micropolis.Listener
|
||||
public void evaluationChanged()
|
||||
{
|
||||
loadEvaluation();
|
||||
}
|
||||
|
||||
private void loadEvaluation()
|
||||
{
|
||||
NumberFormat pctFmt = NumberFormat.getPercentInstance();
|
||||
yesLbl.setText(pctFmt.format(0.01 * engine.evaluation.cityYes));
|
||||
noLbl.setText(pctFmt.format(0.01 * engine.evaluation.cityNo));
|
||||
|
||||
for (int i = 0; i < voterProblemLbl.length; i++) {
|
||||
CityProblem p = i < engine.evaluation.problemOrder.length ? engine.evaluation.problemOrder[i] : null;
|
||||
int numVotes = p != null ? engine.evaluation.problemVotes.get(p) : 0;
|
||||
|
||||
if (numVotes != 0) {
|
||||
voterProblemLbl[i].setText(cstrings.getString("problem."+p.name()));
|
||||
voterCountLbl[i].setText(pctFmt.format(0.01 * numVotes));
|
||||
voterProblemLbl[i].setVisible(true);
|
||||
voterCountLbl[i].setVisible(true);
|
||||
} else {
|
||||
voterProblemLbl[i].setVisible(false);
|
||||
voterCountLbl[i].setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
NumberFormat nf = NumberFormat.getInstance();
|
||||
popLbl.setText(nf.format(engine.evaluation.cityPop));
|
||||
deltaLbl.setText(nf.format(engine.evaluation.deltaCityPop));
|
||||
assessLbl.setText(formatFunds(engine.evaluation.cityAssValue));
|
||||
cityClassLbl.setText(getCityClassName(engine.evaluation.cityClass));
|
||||
gameLevelLbl.setText(getGameLevelName(engine.gameLevel));
|
||||
scoreLbl.setText(nf.format(engine.evaluation.cityScore));
|
||||
scoreDeltaLbl.setText(nf.format(engine.evaluation.deltaCityScore));
|
||||
}
|
||||
|
||||
static String getCityClassName(int cityClass)
|
||||
{
|
||||
return cstrings.getString("class."+cityClass);
|
||||
}
|
||||
|
||||
static String getGameLevelName(int gameLevel)
|
||||
{
|
||||
return cstrings.getString("level."+gameLevel);
|
||||
}
|
||||
}
|
320
src/micropolisj/gui/GraphsPane.java
Normal file
320
src/micropolisj/gui/GraphsPane.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1365
src/micropolisj/gui/MainWindow.java
Normal file
1365
src/micropolisj/gui/MainWindow.java
Normal file
File diff suppressed because it is too large
Load diff
44
src/micropolisj/gui/MessagesPane.java
Normal file
44
src/micropolisj/gui/MessagesPane.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
// 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.util.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import micropolisj.engine.*;
|
||||
|
||||
public class MessagesPane extends JTextPane
|
||||
{
|
||||
static ResourceBundle cityMessageStrings = ResourceBundle.getBundle("micropolisj.CityMessages");
|
||||
|
||||
public MessagesPane()
|
||||
{
|
||||
setEditable(false);
|
||||
}
|
||||
|
||||
public void appendCityMessage(MicropolisMessage message)
|
||||
{
|
||||
appendMessageText(cityMessageStrings.getString(message.name()));
|
||||
}
|
||||
|
||||
void appendMessageText(String messageText)
|
||||
{
|
||||
try {
|
||||
StyledDocument doc = getStyledDocument();
|
||||
if (doc.getLength() != 0) {
|
||||
doc.insertString(doc.getLength(), "\n", null);
|
||||
}
|
||||
doc.insertString(doc.getLength(), messageText, null);
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
throw new Error("unexpected", e);
|
||||
}
|
||||
}
|
||||
}
|
379
src/micropolisj/gui/MicropolisDrawingArea.java
Normal file
379
src/micropolisj/gui/MicropolisDrawingArea.java
Normal file
|
@ -0,0 +1,379 @@
|
|||
// 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.image.*;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.Timer;
|
||||
|
||||
import micropolisj.engine.*;
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public class MicropolisDrawingArea extends JComponent
|
||||
implements Scrollable, MapListener
|
||||
{
|
||||
Micropolis m;
|
||||
boolean blinkUnpoweredZones = true;
|
||||
HashSet<Point> unpoweredZones = new HashSet<>();
|
||||
boolean blink;
|
||||
Timer blinkTimer;
|
||||
ToolPreview toolPreview;
|
||||
int shakeStep;
|
||||
|
||||
static final Dimension PREFERRED_VIEWPORT_SIZE = new Dimension(640,640);
|
||||
|
||||
public MicropolisDrawingArea(Micropolis engine)
|
||||
{
|
||||
this.m = engine;
|
||||
m.addMapListener(this);
|
||||
|
||||
addAncestorListener(new AncestorListener() {
|
||||
public void ancestorAdded(AncestorEvent evt) {
|
||||
startBlinkTimer();
|
||||
}
|
||||
public void ancestorRemoved(AncestorEvent evt) {
|
||||
stopBlinkTimer();
|
||||
}
|
||||
public void ancestorMoved(AncestorEvent evt) {}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
assert this.m != null;
|
||||
|
||||
return new Dimension(TILE_WIDTH*m.getWidth(),TILE_HEIGHT*m.getHeight());
|
||||
}
|
||||
|
||||
public void setEngine(Micropolis newEngine)
|
||||
{
|
||||
assert newEngine != null;
|
||||
|
||||
if (this.m != null) { //old engine
|
||||
this.m.removeMapListener(this);
|
||||
}
|
||||
this.m = newEngine;
|
||||
if (this.m != null) { //new engine
|
||||
this.m.addMapListener(this);
|
||||
}
|
||||
|
||||
// size may have changed
|
||||
invalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
static Image [] tileImages = loadTileImages("/tiles.png");
|
||||
public static final int TILE_WIDTH = 16;
|
||||
public static final int TILE_HEIGHT = 16;
|
||||
|
||||
static Image [] loadTileImages(String resourceName)
|
||||
{
|
||||
URL iconUrl = MicropolisDrawingArea.class.getResource(resourceName);
|
||||
Image refImage = new ImageIcon(iconUrl).getImage();
|
||||
|
||||
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
GraphicsDevice dev = env.getDefaultScreenDevice();
|
||||
GraphicsConfiguration conf = dev.getDefaultConfiguration();
|
||||
|
||||
Image [] images = new Image[refImage.getHeight(null) / TILE_HEIGHT];
|
||||
for (int i = 0; i < images.length; i++)
|
||||
{
|
||||
BufferedImage bi = conf.createCompatibleImage(TILE_WIDTH, TILE_HEIGHT, Transparency.OPAQUE);
|
||||
Graphics2D gr = bi.createGraphics();
|
||||
gr.drawImage(refImage, 0, 0, TILE_WIDTH, TILE_HEIGHT,
|
||||
0, i * TILE_HEIGHT,
|
||||
TILE_WIDTH, (i+1)*TILE_HEIGHT,
|
||||
null);
|
||||
|
||||
images[i] = bi;
|
||||
}
|
||||
return images;
|
||||
}
|
||||
|
||||
static Map<SpriteKind, Map<Integer, Image> > spriteImages;
|
||||
static {
|
||||
spriteImages = new EnumMap<SpriteKind, Map<Integer,Image> >(SpriteKind.class);
|
||||
for (SpriteKind kind : SpriteKind.values())
|
||||
{
|
||||
HashMap<Integer,Image> imgs = new HashMap<>();
|
||||
for (int i = 0; i < kind.numFrames; i++) {
|
||||
Image img = loadSpriteImage(kind, i);
|
||||
if (img != null) {
|
||||
imgs.put(i, img);
|
||||
}
|
||||
}
|
||||
spriteImages.put(kind, imgs);
|
||||
}
|
||||
}
|
||||
|
||||
static Image loadSpriteImage(SpriteKind kind, int frameNo)
|
||||
{
|
||||
String resourceName = "/obj"+kind.objectId+"-"+frameNo+".png";
|
||||
URL iconUrl = MicropolisDrawingArea.class.getResource(resourceName);
|
||||
if (iconUrl == null)
|
||||
return null;
|
||||
|
||||
return new ImageIcon(iconUrl).getImage();
|
||||
}
|
||||
|
||||
void drawSprite(Graphics gr, Sprite sprite)
|
||||
{
|
||||
assert sprite.isVisible();
|
||||
|
||||
Image img = spriteImages.get(sprite.kind).get(sprite.frame-1);
|
||||
if (img != null) {
|
||||
gr.drawImage(img, sprite.x + sprite.offx, sprite.y + sprite.offy, null);
|
||||
}
|
||||
else {
|
||||
gr.setColor(Color.RED);
|
||||
gr.fillRect(sprite.x, sprite.y, 16, 16);
|
||||
gr.setColor(Color.WHITE);
|
||||
gr.drawString(Integer.toString(sprite.frame-1),sprite.x,sprite.y);
|
||||
}
|
||||
}
|
||||
|
||||
public void paintComponent(Graphics gr)
|
||||
{
|
||||
final int width = m.getWidth();
|
||||
final int height = m.getHeight();
|
||||
|
||||
Rectangle clipRect = gr.getClipBounds();
|
||||
int minX = Math.max(0, clipRect.x / TILE_WIDTH);
|
||||
int minY = Math.max(0, clipRect.y / TILE_HEIGHT);
|
||||
int maxX = Math.min(width, 1 + (clipRect.x + clipRect.width-1) / TILE_WIDTH);
|
||||
int maxY = Math.min(height, 1 + (clipRect.y + clipRect.height-1) / TILE_HEIGHT);
|
||||
|
||||
for (int y = minY; y < maxY; y++)
|
||||
{
|
||||
for (int x = minX; x < maxX; x++)
|
||||
{
|
||||
int cell = m.getTile(x,y);
|
||||
int tile = (cell & LOMASK) % tileImages.length;
|
||||
if (blinkUnpoweredZones &&
|
||||
(cell & ZONEBIT) != 0 &&
|
||||
(cell & PWRBIT) == 0)
|
||||
{
|
||||
unpoweredZones.add(new Point(x,y));
|
||||
if (blink)
|
||||
tile = LIGHTNINGBOLT;
|
||||
}
|
||||
|
||||
gr.drawImage(tileImages[tile],
|
||||
x*TILE_WIDTH + (shakeStep != 0 ? getShakeModifier(y) : 0),
|
||||
y*TILE_HEIGHT,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
for (Sprite sprite : m.allSprites())
|
||||
{
|
||||
if (sprite.isVisible())
|
||||
{
|
||||
drawSprite(gr, sprite);
|
||||
}
|
||||
}
|
||||
|
||||
if (toolPreview != null)
|
||||
{
|
||||
int x0 = toolPreview.rect.x * TILE_WIDTH;
|
||||
int x1 = (toolPreview.rect.x + toolPreview.rect.width) * TILE_WIDTH;
|
||||
int y0 = toolPreview.rect.y * TILE_HEIGHT;
|
||||
int y1 = (toolPreview.rect.y + toolPreview.rect.height) * TILE_HEIGHT;
|
||||
|
||||
gr.setColor(Color.BLACK);
|
||||
gr.drawLine(x0-1,y0-1,x0-1,y1-1);
|
||||
gr.drawLine(x0-1,y0-1,x1-1,y0-1);
|
||||
gr.drawLine(x1+3,y0-3,x1+3,y1+3);
|
||||
gr.drawLine(x0-3,y1+3,x1+3,y1+3);
|
||||
|
||||
gr.setColor(Color.WHITE);
|
||||
gr.drawLine(x0-4,y0-4,x1+3,y0-4);
|
||||
gr.drawLine(x0-4,y0-4,x0-4,y1+3);
|
||||
gr.drawLine(x1, y0-1,x1, y1 );
|
||||
gr.drawLine(x0-1,y1, x1, y1 );
|
||||
|
||||
gr.setColor(toolPreview.borderColor);
|
||||
gr.drawRect(x0-3,y0-3,x1-x0+5,y1-y0+5);
|
||||
gr.drawRect(x0-2,y0-2,x1-x0+3,y1-y0+3);
|
||||
|
||||
if (toolPreview.fillColor != null) {
|
||||
gr.setColor(toolPreview.fillColor);
|
||||
gr.fillRect(x0,y0,x1-x0,y1-y0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ToolPreview
|
||||
{
|
||||
Rectangle rect;
|
||||
Color borderColor;
|
||||
Color fillColor;
|
||||
}
|
||||
|
||||
public void setToolPreview(Rectangle newRect, Color toolColor)
|
||||
{
|
||||
ToolPreview tp = new ToolPreview();
|
||||
tp.rect = newRect;
|
||||
tp.borderColor = toolColor;
|
||||
setToolPreview(tp);
|
||||
}
|
||||
|
||||
public void setToolPreview(ToolPreview newPreview)
|
||||
{
|
||||
if (toolPreview == newPreview)
|
||||
return;
|
||||
if (toolPreview != null && toolPreview.equals(newPreview))
|
||||
return;
|
||||
|
||||
if (toolPreview != null)
|
||||
{
|
||||
repaint(new Rectangle(
|
||||
toolPreview.rect.x*TILE_WIDTH - 4,
|
||||
toolPreview.rect.y*TILE_HEIGHT - 4,
|
||||
toolPreview.rect.width*TILE_WIDTH + 8,
|
||||
toolPreview.rect.height*TILE_HEIGHT + 8
|
||||
));
|
||||
}
|
||||
toolPreview = newPreview;
|
||||
if (toolPreview != null)
|
||||
{
|
||||
repaint(new Rectangle(
|
||||
toolPreview.rect.x*TILE_WIDTH - 4,
|
||||
toolPreview.rect.y*TILE_HEIGHT - 4,
|
||||
toolPreview.rect.width*TILE_WIDTH + 8,
|
||||
toolPreview.rect.height*TILE_HEIGHT + 8
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public Dimension getPreferredScrollableViewportSize()
|
||||
{
|
||||
return PREFERRED_VIEWPORT_SIZE;
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
|
||||
{
|
||||
if (orientation == SwingConstants.VERTICAL)
|
||||
return visibleRect.height;
|
||||
else
|
||||
return visibleRect.width;
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public boolean getScrollableTracksViewportWidth()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public boolean getScrollableTracksViewportHeight()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
|
||||
{
|
||||
if (orientation == SwingConstants.VERTICAL)
|
||||
return TILE_HEIGHT * 3;
|
||||
else
|
||||
return TILE_WIDTH * 3;
|
||||
}
|
||||
|
||||
private Rectangle getSpriteBounds(Sprite sprite, int x, int y)
|
||||
{
|
||||
return new Rectangle(x+sprite.offx, y+sprite.offy, sprite.width, sprite.height);
|
||||
}
|
||||
|
||||
public Rectangle getTileBounds(int xpos, int ypos)
|
||||
{
|
||||
return new Rectangle(xpos*TILE_WIDTH, ypos * TILE_HEIGHT,
|
||||
TILE_WIDTH, TILE_HEIGHT);
|
||||
}
|
||||
|
||||
//implements MapListener
|
||||
public void mapOverlayDataChanged(MapState overlayDataType)
|
||||
{
|
||||
}
|
||||
|
||||
//implements MapListener
|
||||
public void spriteMoved(Sprite sprite)
|
||||
{
|
||||
repaint(getSpriteBounds(sprite, sprite.lastX, sprite.lastY));
|
||||
repaint(getSpriteBounds(sprite, sprite.x, sprite.y));
|
||||
}
|
||||
|
||||
//implements MapListener
|
||||
public void tileChanged(int xpos, int ypos)
|
||||
{
|
||||
repaint(getTileBounds(xpos, ypos));
|
||||
}
|
||||
|
||||
//implements MapListener
|
||||
public void wholeMapChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
void doBlink()
|
||||
{
|
||||
if (!unpoweredZones.isEmpty())
|
||||
{
|
||||
blink = !blink;
|
||||
for (Point loc : unpoweredZones)
|
||||
{
|
||||
repaint(getTileBounds(loc.x, loc.y));
|
||||
}
|
||||
unpoweredZones.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void startBlinkTimer()
|
||||
{
|
||||
assert blinkTimer == null;
|
||||
|
||||
ActionListener callback = new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt)
|
||||
{
|
||||
doBlink();
|
||||
}
|
||||
};
|
||||
|
||||
blinkTimer = new Timer(500, callback);
|
||||
blinkTimer.start();
|
||||
}
|
||||
|
||||
void stopBlinkTimer()
|
||||
{
|
||||
if (blinkTimer != null) {
|
||||
blinkTimer.stop();
|
||||
blinkTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void shake(int i)
|
||||
{
|
||||
shakeStep = i;
|
||||
repaint();
|
||||
}
|
||||
|
||||
static final int SHAKE_STEPS = 40;
|
||||
int getShakeModifier(int row)
|
||||
{
|
||||
return (int)Math.round(4.0 * Math.sin((double)(shakeStep+row/2)/2.0));
|
||||
}
|
||||
}
|
225
src/micropolisj/gui/NewCityDialog.java
Normal file
225
src/micropolisj/gui/NewCityDialog.java
Normal file
|
@ -0,0 +1,225 @@
|
|||
// 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.io.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
|
||||
import micropolisj.engine.*;
|
||||
import static micropolisj.gui.MainWindow.EXTENSION;
|
||||
|
||||
public class NewCityDialog extends JDialog
|
||||
{
|
||||
Micropolis engine;
|
||||
JButton previousMapBtn;
|
||||
Stack<Micropolis> previousMaps = new Stack<Micropolis>();
|
||||
Stack<Micropolis> nextMaps = new Stack<Micropolis>();
|
||||
OverlayMapView mapPane;
|
||||
HashMap<Integer,JRadioButton> levelBtns = new HashMap<Integer,JRadioButton>();
|
||||
|
||||
static final ResourceBundle strings = MainWindow.strings;
|
||||
|
||||
public NewCityDialog(MainWindow owner, boolean showCancelOption)
|
||||
{
|
||||
super(owner);
|
||||
setTitle(strings.getString("welcome.caption"));
|
||||
setModal(true);
|
||||
|
||||
assert owner != null;
|
||||
|
||||
JPanel p1 = new JPanel(new BorderLayout());
|
||||
p1.setBorder(BorderFactory.createEmptyBorder(10,20,10,20));
|
||||
getContentPane().add(p1, BorderLayout.CENTER);
|
||||
|
||||
engine = new Micropolis();
|
||||
new MapGenerator(engine).generateNewCity();
|
||||
|
||||
mapPane = new OverlayMapView(engine);
|
||||
mapPane.setBorder(BorderFactory.createLoweredBevelBorder());
|
||||
p1.add(mapPane, BorderLayout.CENTER);
|
||||
|
||||
JPanel p2 = new JPanel(new BorderLayout());
|
||||
p1.add(p2, BorderLayout.EAST);
|
||||
|
||||
Box levelBox = new Box(BoxLayout.Y_AXIS);
|
||||
levelBox.setBorder(BorderFactory.createEmptyBorder(0,10,0,10));
|
||||
p2.add(levelBox, BorderLayout.CENTER);
|
||||
|
||||
levelBox.add(Box.createVerticalGlue());
|
||||
JRadioButton radioBtn;
|
||||
for (int lev = GameLevel.MIN_LEVEL; lev <= GameLevel.MAX_LEVEL; lev++)
|
||||
{
|
||||
final int x = lev;
|
||||
radioBtn = new JRadioButton(strings.getString("menu.difficulty."+lev));
|
||||
radioBtn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
setGameLevel(x);
|
||||
}});
|
||||
levelBox.add(radioBtn);
|
||||
levelBtns.put(lev, radioBtn);
|
||||
}
|
||||
levelBox.add(Box.createVerticalGlue());
|
||||
setGameLevel(GameLevel.MIN_LEVEL);
|
||||
|
||||
JPanel buttonPane = new JPanel();
|
||||
getContentPane().add(buttonPane, BorderLayout.SOUTH);
|
||||
|
||||
JButton btn;
|
||||
btn = new JButton(strings.getString("welcome.previous_map"));
|
||||
btn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
onPreviousMapClicked();
|
||||
}});
|
||||
btn.setEnabled(false);
|
||||
buttonPane.add(btn);
|
||||
previousMapBtn = btn;
|
||||
|
||||
btn = new JButton(strings.getString("welcome.play_this_map"));
|
||||
btn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
onPlayClicked();
|
||||
}});
|
||||
buttonPane.add(btn);
|
||||
getRootPane().setDefaultButton(btn);
|
||||
|
||||
btn = new JButton(strings.getString("welcome.next_map"));
|
||||
btn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
onNextMapClicked();
|
||||
}});
|
||||
buttonPane.add(btn);
|
||||
|
||||
btn = new JButton(strings.getString("welcome.load_city"));
|
||||
btn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
onLoadCityClicked();
|
||||
}});
|
||||
buttonPane.add(btn);
|
||||
|
||||
if (showCancelOption) {
|
||||
btn = new JButton(strings.getString("welcome.cancel"));
|
||||
btn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
onCancelClicked();
|
||||
}});
|
||||
buttonPane.add(btn);
|
||||
}
|
||||
else {
|
||||
btn = new JButton(strings.getString("welcome.quit"));
|
||||
btn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
onQuitClicked();
|
||||
}});
|
||||
buttonPane.add(btn);
|
||||
}
|
||||
|
||||
pack();
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setLocationRelativeTo(owner);
|
||||
}
|
||||
|
||||
private void onPreviousMapClicked()
|
||||
{
|
||||
if (previousMaps.isEmpty())
|
||||
return;
|
||||
|
||||
nextMaps.push(engine);
|
||||
engine = previousMaps.pop();
|
||||
mapPane.setEngine(engine);
|
||||
|
||||
previousMapBtn.setEnabled(!previousMaps.isEmpty());
|
||||
}
|
||||
|
||||
private void onNextMapClicked()
|
||||
{
|
||||
if (nextMaps.isEmpty())
|
||||
{
|
||||
Micropolis m = new Micropolis();
|
||||
new MapGenerator(m).generateNewCity();
|
||||
nextMaps.add(m);
|
||||
}
|
||||
|
||||
previousMaps.push(engine);
|
||||
engine = nextMaps.pop();
|
||||
mapPane.setEngine(engine);
|
||||
|
||||
previousMapBtn.setEnabled(true);
|
||||
}
|
||||
|
||||
private void onLoadCityClicked()
|
||||
{
|
||||
try
|
||||
{
|
||||
JFileChooser fc = new JFileChooser();
|
||||
FileNameExtensionFilter filter1 = new FileNameExtensionFilter(strings.getString("cty_file"), EXTENSION);
|
||||
fc.setFileFilter(filter1);
|
||||
|
||||
int rv = fc.showOpenDialog(this);
|
||||
if (rv == JFileChooser.APPROVE_OPTION) {
|
||||
File file = fc.getSelectedFile();
|
||||
Micropolis newEngine = new Micropolis();
|
||||
newEngine.load(file);
|
||||
startPlaying(newEngine, file);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace(System.err);
|
||||
JOptionPane.showMessageDialog(this, e, strings.getString("main.error_caption"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
void startPlaying(Micropolis newEngine, File file)
|
||||
{
|
||||
MainWindow win = (MainWindow) getOwner();
|
||||
win.setEngine(newEngine);
|
||||
win.currentFile = file;
|
||||
dispose();
|
||||
}
|
||||
|
||||
private void onPlayClicked()
|
||||
{
|
||||
engine.setGameLevel(getSelectedGameLevel());
|
||||
startPlaying(engine, null);
|
||||
}
|
||||
|
||||
private void onCancelClicked()
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
|
||||
private void onQuitClicked()
|
||||
{
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private int getSelectedGameLevel()
|
||||
{
|
||||
for (int lev : levelBtns.keySet())
|
||||
{
|
||||
if (levelBtns.get(lev).isSelected()) {
|
||||
return lev;
|
||||
}
|
||||
}
|
||||
return GameLevel.MIN_LEVEL;
|
||||
}
|
||||
|
||||
private void setGameLevel(int level)
|
||||
{
|
||||
for (int lev : levelBtns.keySet())
|
||||
{
|
||||
levelBtns.get(lev).setSelected(lev == level);
|
||||
}
|
||||
}
|
||||
}
|
176
src/micropolisj/gui/NotificationPane.java
Normal file
176
src/micropolisj/gui/NotificationPane.java
Normal file
|
@ -0,0 +1,176 @@
|
|||
// 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.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
import micropolisj.engine.*;
|
||||
import static micropolisj.gui.ColorParser.parseColor;
|
||||
|
||||
public class NotificationPane extends JPanel
|
||||
{
|
||||
JLabel headerLbl;
|
||||
JViewport mapViewport;
|
||||
MicropolisDrawingArea mapView;
|
||||
JPanel mainPane;
|
||||
JComponent infoPane;
|
||||
|
||||
static final Dimension VIEWPORT_SIZE = new Dimension(160,160);
|
||||
static final Color QUERY_COLOR = new Color(255,165,0);
|
||||
static final ResourceBundle strings = MainWindow.strings;
|
||||
static final ResourceBundle mstrings = ResourceBundle.getBundle("micropolisj.CityMessages");
|
||||
static final ResourceBundle s_strings = ResourceBundle.getBundle("micropolisj.StatusMessages");
|
||||
|
||||
public NotificationPane(Micropolis engine)
|
||||
{
|
||||
super(new BorderLayout());
|
||||
setVisible(false);
|
||||
|
||||
headerLbl = new JLabel();
|
||||
headerLbl.setOpaque(true);
|
||||
headerLbl.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
headerLbl.setBorder(BorderFactory.createRaisedBevelBorder());
|
||||
add(headerLbl, BorderLayout.NORTH);
|
||||
|
||||
JButton dismissBtn = new JButton(strings.getString("notification.dismiss"));
|
||||
dismissBtn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
onDismissClicked();
|
||||
}});
|
||||
add(dismissBtn, BorderLayout.SOUTH);
|
||||
|
||||
mainPane = new JPanel(new BorderLayout());
|
||||
add(mainPane, BorderLayout.CENTER);
|
||||
|
||||
JPanel viewportContainer = new JPanel(new BorderLayout());
|
||||
viewportContainer.setBorder(
|
||||
BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createEmptyBorder(8,4,8,4),
|
||||
BorderFactory.createLineBorder(Color.BLACK)
|
||||
));
|
||||
mainPane.add(viewportContainer, BorderLayout.WEST);
|
||||
|
||||
mapViewport = new JViewport();
|
||||
mapViewport.setPreferredSize(VIEWPORT_SIZE);
|
||||
mapViewport.setMaximumSize(VIEWPORT_SIZE);
|
||||
mapViewport.setMinimumSize(VIEWPORT_SIZE);
|
||||
viewportContainer.add(mapViewport, BorderLayout.CENTER);
|
||||
|
||||
mapView = new MicropolisDrawingArea(engine);
|
||||
mapViewport.setView(mapView);
|
||||
}
|
||||
|
||||
private void onDismissClicked()
|
||||
{
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
void setPicture(Micropolis engine, int xpos, int ypos)
|
||||
{
|
||||
Dimension sz = VIEWPORT_SIZE;
|
||||
|
||||
mapView.setEngine(engine);
|
||||
Rectangle r = mapView.getTileBounds(xpos,ypos);
|
||||
|
||||
mapViewport.setViewPosition(new Point(
|
||||
r.x + r.width/2 - sz.width/2,
|
||||
r.y + r.height/2 - sz.height/2
|
||||
));
|
||||
}
|
||||
|
||||
public void showMessage(Micropolis engine, MicropolisMessage msg, int xpos, int ypos)
|
||||
{
|
||||
setPicture(engine, xpos, ypos);
|
||||
|
||||
if (infoPane != null) {
|
||||
mainPane.remove(infoPane);
|
||||
infoPane = null;
|
||||
}
|
||||
|
||||
headerLbl.setText(mstrings.getString(msg.name()+".title"));
|
||||
headerLbl.setBackground(parseColor(mstrings.getString(msg.name()+".color")));
|
||||
|
||||
JLabel myLabel = new JLabel("<html><p>"+
|
||||
mstrings.getString(msg.name()+".detail") + "</p></html>");
|
||||
myLabel.setPreferredSize(new Dimension(1,1));
|
||||
|
||||
infoPane = myLabel;
|
||||
mainPane.add(myLabel, BorderLayout.CENTER);
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public void showZoneStatus(Micropolis engine, int xpos, int ypos, ZoneStatus zone)
|
||||
{
|
||||
headerLbl.setText(strings.getString("notification.query_hdr"));
|
||||
headerLbl.setBackground(QUERY_COLOR);
|
||||
|
||||
String buildingStr = s_strings.getString("zone."+zone.building);
|
||||
String popDensityStr = s_strings.getString("status."+zone.popDensity);
|
||||
String landValueStr = s_strings.getString("status."+zone.landValue);
|
||||
String crimeLevelStr = s_strings.getString("status."+zone.crimeLevel);
|
||||
String pollutionStr = s_strings.getString("status."+zone.pollution);
|
||||
String growthRateStr = s_strings.getString("status."+zone.growthRate);
|
||||
|
||||
setPicture(engine, xpos, ypos);
|
||||
|
||||
if (infoPane != null) {
|
||||
mainPane.remove(infoPane);
|
||||
infoPane = null;
|
||||
}
|
||||
|
||||
JPanel p = new JPanel(new GridBagLayout());
|
||||
mainPane.add(p, BorderLayout.CENTER);
|
||||
infoPane = p;
|
||||
|
||||
GridBagConstraints c1 = new GridBagConstraints();
|
||||
GridBagConstraints c2 = new GridBagConstraints();
|
||||
|
||||
c1.gridx = 0;
|
||||
c2.gridx = 1;
|
||||
c1.gridy = c2.gridy = 0;
|
||||
c1.anchor = GridBagConstraints.WEST;
|
||||
c2.anchor = GridBagConstraints.WEST;
|
||||
c1.insets = new Insets(0,0,0,8);
|
||||
c2.weightx = 1.0;
|
||||
|
||||
p.add(new JLabel(strings.getString("notification.zone_lbl")), c1);
|
||||
p.add(new JLabel(buildingStr), c2);
|
||||
|
||||
c1.gridy = ++c2.gridy;
|
||||
p.add(new JLabel(strings.getString("notification.density_lbl")), c1);
|
||||
p.add(new JLabel(popDensityStr), c2);
|
||||
|
||||
c1.gridy = ++c2.gridy;
|
||||
p.add(new JLabel(strings.getString("notification.value_lbl")), c1);
|
||||
p.add(new JLabel(landValueStr), c2);
|
||||
|
||||
c1.gridy = ++c2.gridy;
|
||||
p.add(new JLabel(strings.getString("notification.crime_lbl")), c1);
|
||||
p.add(new JLabel(crimeLevelStr), c2);
|
||||
|
||||
c1.gridy = ++c2.gridy;
|
||||
p.add(new JLabel(strings.getString("notification.pollution_lbl")), c1);
|
||||
p.add(new JLabel(pollutionStr), c2);
|
||||
|
||||
c1.gridy = ++c2.gridy;
|
||||
p.add(new JLabel(strings.getString("notification.growth_lbl")), c1);
|
||||
p.add(new JLabel(growthRateStr), c2);
|
||||
|
||||
c1.gridy++;
|
||||
c1.gridwidth = 2;
|
||||
c1.weighty = 1.0;
|
||||
p.add(new JLabel(), c1);
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
509
src/micropolisj/gui/OverlayMapView.java
Normal file
509
src/micropolisj/gui/OverlayMapView.java
Normal file
|
@ -0,0 +1,509 @@
|
|||
// 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.image.*;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
import micropolisj.engine.*;
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public class OverlayMapView extends JComponent
|
||||
implements Scrollable, MapListener
|
||||
{
|
||||
Micropolis engine;
|
||||
ArrayList<ConnectedView> views = new ArrayList<>();
|
||||
MapState mapState = MapState.ALL;
|
||||
|
||||
public OverlayMapView(Micropolis _engine)
|
||||
{
|
||||
assert _engine != null;
|
||||
|
||||
MouseAdapter mouse = new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent ev)
|
||||
{
|
||||
onMousePressed(ev);
|
||||
}
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent ev)
|
||||
{
|
||||
onMouseDragged(ev);
|
||||
}
|
||||
};
|
||||
addMouseListener(mouse);
|
||||
addMouseMotionListener(mouse);
|
||||
|
||||
setEngine(_engine);
|
||||
}
|
||||
|
||||
public void setEngine(Micropolis newEngine)
|
||||
{
|
||||
assert newEngine != null;
|
||||
|
||||
if (engine != null) { //old engine
|
||||
engine.removeMapListener(this);
|
||||
}
|
||||
engine = newEngine;
|
||||
if (engine != null) { //new engine
|
||||
engine.addMapListener(this);
|
||||
}
|
||||
|
||||
invalidate(); //map size may have changed
|
||||
repaint();
|
||||
engine.calculateCenterMass();
|
||||
dragViewToCityCenter();
|
||||
}
|
||||
|
||||
public MapState getMapState()
|
||||
{
|
||||
return mapState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return new Dimension(
|
||||
getInsets().left + getInsets().right + TILE_WIDTH*engine.getWidth(),
|
||||
getInsets().top + getInsets().bottom + TILE_HEIGHT*engine.getHeight()
|
||||
);
|
||||
}
|
||||
|
||||
public void setMapState(MapState newState)
|
||||
{
|
||||
if (mapState == newState)
|
||||
return;
|
||||
|
||||
mapState = newState;
|
||||
repaint();
|
||||
}
|
||||
|
||||
static BufferedImage tileArrayImage = loadImage("/tilessm.png");
|
||||
static final int TILE_WIDTH = 3;
|
||||
static final int TILE_HEIGHT = 3;
|
||||
static final int TILE_OFFSET_Y = 3;
|
||||
|
||||
static BufferedImage loadImage(String resourceName)
|
||||
{
|
||||
URL iconUrl = MicropolisDrawingArea.class.getResource(resourceName);
|
||||
Image refImage = new ImageIcon(iconUrl).getImage();
|
||||
|
||||
BufferedImage bi = new BufferedImage(refImage.getWidth(null), refImage.getHeight(null),
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D gr = bi.createGraphics();
|
||||
gr.drawImage(refImage, 0, 0, null);
|
||||
|
||||
return bi;
|
||||
}
|
||||
|
||||
static final Color VAL_LOW = new Color(0xbfbfbf);
|
||||
static final Color VAL_MEDIUM = new Color(0xffff00);
|
||||
static final Color VAL_HIGH = new Color(0xff7f00);
|
||||
static final Color VAL_VERYHIGH = new Color(0xff0000);
|
||||
static final Color VAL_PLUS = new Color(0x007f00);
|
||||
static final Color VAL_VERYPLUS = new Color(0x00e600);
|
||||
static final Color VAL_MINUS = new Color(0xff7f00);
|
||||
static final Color VAL_VERYMINUS = new Color(0xffff00);
|
||||
|
||||
private Color getCI(int x)
|
||||
{
|
||||
if (x < 50)
|
||||
return null;
|
||||
else if (x < 100)
|
||||
return VAL_LOW;
|
||||
else if (x < 150)
|
||||
return VAL_MEDIUM;
|
||||
else if (x < 200)
|
||||
return VAL_HIGH;
|
||||
else
|
||||
return VAL_VERYHIGH;
|
||||
}
|
||||
|
||||
private Color getCI_rog(int x)
|
||||
{
|
||||
if (x > 100)
|
||||
return VAL_VERYPLUS;
|
||||
else if (x > 20)
|
||||
return VAL_PLUS;
|
||||
else if (x < -100)
|
||||
return VAL_VERYMINUS;
|
||||
else if (x < -20)
|
||||
return VAL_MINUS;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private void drawLandMap(Graphics gr)
|
||||
{
|
||||
int [][] A = engine.landValueMem;
|
||||
|
||||
for (int y = 0; y < A.length; y++) {
|
||||
for (int x = 0; x < A[y].length; x++) {
|
||||
maybeDrawRect(gr, getCI(A[y][x]),x*6,y*6,6,6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPollutionMap(Graphics gr)
|
||||
{
|
||||
int [][] A = engine.pollutionMem;
|
||||
|
||||
for (int y = 0; y < A.length; y++) {
|
||||
for (int x = 0; x < A[y].length; x++) {
|
||||
maybeDrawRect(gr, getCI(10 + A[y][x]),x*6,y*6,6,6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawCrimeMap(Graphics gr)
|
||||
{
|
||||
int [][] A = engine.crimeMem;
|
||||
|
||||
for (int y = 0; y < A.length; y++) {
|
||||
for (int x = 0; x < A[y].length; x++) {
|
||||
maybeDrawRect(gr, getCI(A[y][x]),x*6,y*6,6,6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawTrafficMap(Graphics gr)
|
||||
{
|
||||
int [][] A = engine.trfDensity;
|
||||
|
||||
for (int y = 0; y < A.length; y++) {
|
||||
for (int x = 0; x < A[y].length; x++) {
|
||||
maybeDrawRect(gr, getCI(A[y][x]),x*6,y*6,6,6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPopDensity(Graphics gr)
|
||||
{
|
||||
int [][] A = engine.popDensity;
|
||||
|
||||
for (int y = 0; y < A.length; y++) {
|
||||
for (int x = 0; x < A[y].length; x++) {
|
||||
maybeDrawRect(gr, getCI(A[y][x]),x*6,y*6,6,6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawRateOfGrowth(Graphics gr)
|
||||
{
|
||||
int [][] A = engine.rateOGMem;
|
||||
|
||||
for (int y = 0; y < A.length; y++) {
|
||||
for (int x = 0; x < A[y].length; x++) {
|
||||
maybeDrawRect(gr, getCI_rog(A[y][x]),x*24,y*24,24,24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawFireRadius(Graphics gr)
|
||||
{
|
||||
int [][] A = engine.fireRate;
|
||||
|
||||
for (int y = 0; y < A.length; y++) {
|
||||
for (int x = 0; x < A[y].length; x++) {
|
||||
maybeDrawRect(gr, getCI(A[y][x]),x*24,y*24,24,24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPoliceRadius(Graphics gr)
|
||||
{
|
||||
int [][] A = engine.policeMapEffect;
|
||||
|
||||
for (int y = 0; y < A.length; y++) {
|
||||
for (int x = 0; x < A[y].length; x++) {
|
||||
maybeDrawRect(gr, getCI(A[y][x]),x*24,y*24,24,24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeDrawRect(Graphics gr, Color col, int x, int y, int width, int height)
|
||||
{
|
||||
if (col != null) {
|
||||
gr.setColor(col);
|
||||
gr.fillRect(x,y,width,height);
|
||||
}
|
||||
}
|
||||
|
||||
static final int UNPOWERED = 0x6666e6; //lightblue
|
||||
static final int POWERED = 0xff0000; //red
|
||||
static final int CONDUCTIVE = 0xbfbfbf; //lightgray
|
||||
|
||||
private int checkPower(BufferedImage img, int x, int y, int rawTile)
|
||||
{
|
||||
int pix;
|
||||
|
||||
if ((rawTile & LOMASK) <= 63) {
|
||||
return rawTile & LOMASK;
|
||||
}
|
||||
else if ((rawTile & ZONEBIT) != 0) {
|
||||
// zone
|
||||
pix = ((rawTile & PWRBIT) != 0) ? POWERED : UNPOWERED;
|
||||
}
|
||||
else if ((rawTile & CONDBIT) != 0) {
|
||||
pix = CONDUCTIVE;
|
||||
}
|
||||
else {
|
||||
return DIRT;
|
||||
}
|
||||
|
||||
for (int yy = 0; yy < TILE_HEIGHT; yy++)
|
||||
{
|
||||
for (int xx = 0; xx < TILE_WIDTH; xx++)
|
||||
{
|
||||
img.setRGB(x*TILE_WIDTH+xx,y*TILE_HEIGHT+yy, pix);
|
||||
}
|
||||
}
|
||||
return -1; //this special value tells caller to skip the tile bitblt,
|
||||
//since it was performed here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics gr)
|
||||
{
|
||||
final int width = engine.getWidth();
|
||||
final int height = engine.getHeight();
|
||||
|
||||
BufferedImage img = new BufferedImage(width*TILE_WIDTH, height*TILE_HEIGHT,
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
final Insets INSETS = getInsets();
|
||||
Rectangle clipRect = gr.getClipBounds();
|
||||
int minX = Math.max(0, (clipRect.x - INSETS.left) / TILE_WIDTH);
|
||||
int minY = Math.max(0, (clipRect.y - INSETS.top) / TILE_HEIGHT);
|
||||
int maxX = Math.min(width, 1 + (clipRect.x - INSETS.left + clipRect.width-1) / TILE_WIDTH);
|
||||
int maxY = Math.min(height, 1 + (clipRect.y - INSETS.top + clipRect.height-1) / TILE_HEIGHT);
|
||||
|
||||
for (int y = minY; y < maxY; y++)
|
||||
{
|
||||
for (int x = minX; x < maxX; x++)
|
||||
{
|
||||
int tile = engine.getTile(x,y) & LOMASK;
|
||||
switch (mapState) {
|
||||
case RESIDENTIAL:
|
||||
if (tile >= COMBASE) { tile = DIRT; }
|
||||
break;
|
||||
case COMMERCIAL:
|
||||
if (tile > COMLAST || (tile >= RESBASE && tile < COMBASE)) { tile = DIRT; }
|
||||
break;
|
||||
case INDUSTRIAL:
|
||||
if ((tile >= RESBASE && tile < INDBASE) ||
|
||||
(tile >= PORTBASE && tile < SMOKEBASE) ||
|
||||
(tile >= TINYEXP && tile < 884) ||
|
||||
tile >= FOOTBALLGAME1)
|
||||
{ tile = DIRT; }
|
||||
break;
|
||||
case POWER_OVERLAY:
|
||||
tile = checkPower(img, x, y, engine.getTile(x,y));
|
||||
break;
|
||||
case TRANSPORT:
|
||||
case TRAFFIC_OVERLAY:
|
||||
if (tile >= RESBASE ||
|
||||
(tile >= 207 && tile <= LVPOWER10) ||
|
||||
tile == 223) { tile = DIRT; }
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
if (tile != -1) {
|
||||
for (int yy = 0; yy < TILE_HEIGHT; yy++)
|
||||
{
|
||||
for (int xx = 0; xx < TILE_WIDTH; xx++)
|
||||
{
|
||||
img.setRGB(x*TILE_WIDTH+xx,y*TILE_HEIGHT+yy,
|
||||
tileArrayImage.getRGB(xx,tile*TILE_OFFSET_Y+yy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gr.drawImage(img, INSETS.left, INSETS.top, null);
|
||||
|
||||
gr = gr.create();
|
||||
gr.translate(INSETS.left, INSETS.top);
|
||||
|
||||
switch (mapState) {
|
||||
case POLICE_OVERLAY:
|
||||
drawPoliceRadius(gr); break;
|
||||
case FIRE_OVERLAY:
|
||||
drawFireRadius(gr); break;
|
||||
case LANDVALUE_OVERLAY:
|
||||
drawLandMap(gr); break;
|
||||
case CRIME_OVERLAY:
|
||||
drawCrimeMap(gr); break;
|
||||
case POLLUTE_OVERLAY:
|
||||
drawPollutionMap(gr); break;
|
||||
case TRAFFIC_OVERLAY:
|
||||
drawTrafficMap(gr); break;
|
||||
case GROWTHRATE_OVERLAY:
|
||||
drawRateOfGrowth(gr); break;
|
||||
case POPDEN_OVERLAY:
|
||||
drawPopDensity(gr); break;
|
||||
default:
|
||||
}
|
||||
|
||||
for (ConnectedView cv : views)
|
||||
{
|
||||
Rectangle rect = getViewRect(cv);
|
||||
gr.setColor(Color.WHITE);
|
||||
gr.drawRect(rect.x-2,rect.y-2,rect.width+2,rect.height+2);
|
||||
|
||||
gr.setColor(Color.BLACK);
|
||||
gr.drawRect(rect.x-0,rect.y-0,rect.width+2,rect.height+2);
|
||||
|
||||
gr.setColor(Color.YELLOW);
|
||||
gr.drawRect(rect.x-1,rect.y-1,rect.width+2,rect.height+2);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle getViewRect(ConnectedView cv)
|
||||
{
|
||||
Rectangle rawRect = cv.scrollPane.getViewport().getViewRect();
|
||||
return new Rectangle(
|
||||
rawRect.x * 3 / 16,
|
||||
rawRect.y * 3 / 16,
|
||||
rawRect.width * 3 / 16,
|
||||
rawRect.height * 3 / 16
|
||||
);
|
||||
}
|
||||
|
||||
private void dragViewTo(Point p)
|
||||
{
|
||||
if (views.isEmpty())
|
||||
return;
|
||||
|
||||
ConnectedView cv = views.get(0);
|
||||
Dimension d = cv.scrollPane.getViewport().getExtentSize();
|
||||
Dimension mapSize = cv.scrollPane.getViewport().getViewSize();
|
||||
|
||||
Point np = new Point(
|
||||
p.x * 16 / 3 - d.width / 2,
|
||||
p.y * 16 / 3 - d.height / 2
|
||||
);
|
||||
np.x = Math.max(0, Math.min(np.x, mapSize.width - d.width));
|
||||
np.y = Math.max(0, Math.min(np.y, mapSize.height - d.height));
|
||||
|
||||
cv.scrollPane.getViewport().setViewPosition(np);
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public Dimension getPreferredScrollableViewportSize()
|
||||
{
|
||||
return new Dimension(120,120);
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
|
||||
{
|
||||
if (orientation == SwingConstants.VERTICAL)
|
||||
return visibleRect.height;
|
||||
else
|
||||
return visibleRect.width;
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public boolean getScrollableTracksViewportWidth()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public boolean getScrollableTracksViewportHeight()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//implements Scrollable
|
||||
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
|
||||
{
|
||||
if (orientation == SwingConstants.VERTICAL)
|
||||
return TILE_HEIGHT;
|
||||
else
|
||||
return TILE_WIDTH;
|
||||
}
|
||||
|
||||
//implements MapListener
|
||||
public void mapOverlayDataChanged(MapState overlayDataType)
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
//implements MapListener
|
||||
public void spriteMoved(Sprite sprite)
|
||||
{
|
||||
}
|
||||
|
||||
//implements MapListener
|
||||
public void tileChanged(int xpos, int ypos)
|
||||
{
|
||||
Rectangle r = new Rectangle(xpos*TILE_WIDTH, ypos * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
|
||||
repaint(r);
|
||||
}
|
||||
|
||||
//implements MapListener
|
||||
public void wholeMapChanged()
|
||||
{
|
||||
repaint();
|
||||
engine.calculateCenterMass();
|
||||
dragViewToCityCenter();
|
||||
}
|
||||
|
||||
public void dragViewToCityCenter()
|
||||
{
|
||||
dragViewTo(new Point(TILE_WIDTH * engine.centerMassX + 1,
|
||||
TILE_HEIGHT * engine.centerMassY + 1));
|
||||
}
|
||||
|
||||
class ConnectedView implements ChangeListener
|
||||
{
|
||||
JScrollPane scrollPane;
|
||||
|
||||
ConnectedView(MicropolisDrawingArea view, JScrollPane scrollPane)
|
||||
{
|
||||
this.scrollPane = scrollPane;
|
||||
scrollPane.getViewport().addChangeListener(this);
|
||||
}
|
||||
|
||||
public void stateChanged(ChangeEvent ev)
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public void connectView(MicropolisDrawingArea view, JScrollPane scrollPane)
|
||||
{
|
||||
ConnectedView cv = new ConnectedView(view, scrollPane);
|
||||
views.add(cv);
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void onMousePressed(MouseEvent ev)
|
||||
{
|
||||
if (ev.getButton() == MouseEvent.BUTTON1)
|
||||
dragViewTo(ev.getPoint());
|
||||
}
|
||||
|
||||
private void onMouseDragged(MouseEvent ev)
|
||||
{
|
||||
if ((ev.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0)
|
||||
return;
|
||||
|
||||
dragViewTo(ev.getPoint());
|
||||
}
|
||||
}
|
9
src/micropolisj/gui/package.html
Normal file
9
src/micropolisj/gui/package.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<html><head></head>
|
||||
<body>
|
||||
<p>Contains the front-end user interface that drives the game.</p>
|
||||
<p>
|
||||
Most of the funtionality is tied in by the MainWindow class.
|
||||
The MicropolisDrawingArea class provides the city renderer.
|
||||
The OverlapMapView class provides the mini-map.
|
||||
</p>
|
||||
</body></html>
|
Reference in a new issue