Contains the backend classes that implement the actual city simulation.
+ diff --git a/src/micropolisj/gui/BudgetDialog.java b/src/micropolisj/gui/BudgetDialog.java new file mode 100644 index 0000000..86ad9b1 --- /dev/null +++ b/src/micropolisj/gui/BudgetDialog.java @@ -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; + } +} diff --git a/src/micropolisj/gui/ColorParser.java b/src/micropolisj/gui/ColorParser.java new file mode 100644 index 0000000..6cde0dc --- /dev/null +++ b/src/micropolisj/gui/ColorParser.java @@ -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); + } + } +} diff --git a/src/micropolisj/gui/DemandIndicator.java b/src/micropolisj/gui/DemandIndicator.java new file mode 100644 index 0000000..7b48735 --- /dev/null +++ b/src/micropolisj/gui/DemandIndicator.java @@ -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() { } +} diff --git a/src/micropolisj/gui/EvaluationPane.java b/src/micropolisj/gui/EvaluationPane.java new file mode 100644 index 0000000..7ad9d84 --- /dev/null +++ b/src/micropolisj/gui/EvaluationPane.java @@ -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); + } +} diff --git a/src/micropolisj/gui/GraphsPane.java b/src/micropolisj/gui/GraphsPane.java new file mode 100644 index 0000000..8e371ca --- /dev/null +++ b/src/micropolisj/gui/GraphsPane.java @@ -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"+ + mstrings.getString(msg.name()+".detail") + "
"); + 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); + } +} diff --git a/src/micropolisj/gui/OverlayMapView.java b/src/micropolisj/gui/OverlayMapView.java new file mode 100644 index 0000000..461ccb6 --- /dev/null +++ b/src/micropolisj/gui/OverlayMapView.java @@ -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; + ArrayListContains the front-end user interface that drives the game.
++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. +
+ diff --git a/strings/CityMessages.properties b/strings/CityMessages.properties new file mode 100644 index 0000000..3a1f1c8 --- /dev/null +++ b/strings/CityMessages.properties @@ -0,0 +1,138 @@ +!! 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. + +NEED_RES = More residential zones needed. +NEED_COM = More commercial zones needed. +NEED_IND = More industrial zones needed. +NEED_ROADS = More roads required. +NEED_RAILS = Inadequate rail system. +NEED_POWER = Build a Power Plant. +NEED_STADIUM = Residents demand a Stadium. +NEED_SEAPORT = Industry requires a Sea Port. +NEED_AIRPORT = Commerce requires an Airport. +HIGH_POLLUTION = Pollution very high. +HIGH_CRIME = Crime very high. +HIGH_TRAFFIC = Frequent traffic jams reported. +NEED_FIRESTATION = Citizens demand a Fire Department. +NEED_POLICE = Citizens demand a Police Department. +BLACKOUTS = Blackouts reported. Check power map. +HIGH_TAXES = Citizens upset. The tax rate is too high. +ROADS_NEED_FUNDING = Roads deteriorating, due to lack of funds. +FIRE_NEED_FUNDING = Fire departments need funding. +POLICE_NEED_FUNDING = Police departments need funding. +FIRE_REPORT = Fire reported ! +MONSTER_REPORT = A Monster has been sighted !! +TORNADO_REPORT = Tornado reported !! +EARTHQUAKE_REPORT = Major earthquake reported !! +PLANECRASH_REPORT = A plane has crashed ! +SHIPWRECK_REPORT = Shipwreck reported ! +TRAIN_CRASH_REPORT = A train crashed ! +COPTER_CRASH_REPORT = A helicopter crashed ! +HIGH_UNEMPLOYMENT = Unemployment rate is high. +OUT_OF_FUNDS_REPORT = YOUR CITY HAS GONE BROKE! +FIREBOMBING_REPORT = Firebombing reported ! +NEED_PARKS = Need more parks. +EXPLOSION_REPORT = Explosion detected ! +INSUFFICIENT_FUNDS = Insufficient funds to build that. +BULLDOZE_FIRST = Area must be bulldozed first. +POP_2K_REACHED = Population has reached 2,000. +POP_10K_REACHED = Population has reached 10,000. +POP_50K_REACHED = Population has reached 50,000. +POP_100K_REACHED = Population has reached 100,000. +POP_500K_REACHED = Population has reached 500,000. +BROWNOUTS_REPORT = Brownouts, build another Power Plant. +HEAVY_TRAFFIC_REPORT = Heavy Traffic reported. +FLOOD_REPORT = Flooding reported !! +MELTDOWN_REPORT = A Nuclear Meltdown has occurred !!! +RIOTING_REPORT = They're rioting in the streets !! + +NO_NUCLEAR_PLANTS = Cannot meltdown. Build a nuclear power plant first. + +HIGH_POLLUTION.title = POLLUTION ALERT! +HIGH_POLLUTION.color = #ff4f4f +HIGH_POLLUTION.detail = Pollution in your city has exceeded the maximum allowable amounts established by the Micropolis Pollution Agency. You are running the risk of grave ecological consequences.Copyright 2013 Jason Long
\
+ Portions Copyright 1989-2007 Electronic Arts Inc.
This is free software; you can redistribute it and/or modify it
\
+ under the terms of the GNU GPLv3; see the README file for details.
There is no warranty, to the extent permitted by law.
\ + +cty_file = CTY file +funds = ${0,number,integer} +citytime = {0,date,MMM yyyy} + +! +! Welcome screen +! +welcome.caption = Welcome to MicropolisJ +welcome.previous_map = Previous Map +welcome.next_map = Next Map +welcome.play_this_map = Play This Map +welcome.load_city = Load City +welcome.cancel = Cancel +welcome.quit = Quit + +! +! Menus +! +menu.zones = Zones +menu.zones.ALL = All +menu.zones.RESIDENTIAL = Residential +menu.zones.COMMERCIAL = Commercial +menu.zones.INDUSTRIAL = Industrial +menu.zones.TRANSPORT = Transportation +menu.overlays = Overlays +menu.overlays.POPDEN_OVERLAY = Population Density +menu.overlays.GROWTHRATE_OVERLAY = Rate of Growth +menu.overlays.LANDVALUE_OVERLAY = Land Value +menu.overlays.CRIME_OVERLAY = Crime Rate +menu.overlays.POLLUTE_OVERLAY = Pollution +menu.overlays.TRAFFIC_OVERLAY = Traffic Density +menu.overlays.POWER_OVERLAY = Power Grid +menu.overlays.FIRE_OVERLAY = Fire Coverage +menu.overlays.POLICE_OVERLAY = Police Coverage + +menu.game = Game +menu.game.new = New City... +menu.game.load = Load City... +menu.game.save = Save City +menu.game.save_as = Save City as... +menu.game.exit = Exit + +menu.options = Options +menu.options.auto_budget = Auto Budget +menu.options.auto_bulldoze = Auto Bulldoze +menu.options.disasters = Disasters +menu.options.sound = Sound + +menu.difficulty = Difficulty +menu.difficulty.0 = Easy +menu.difficulty.1 = Medium +menu.difficulty.2 = Hard + +menu.disasters = Disasters +menu.disasters.MONSTER = Monster +menu.disasters.FIRE = Fire +menu.disasters.FLOOD = Flood +menu.disasters.MELTDOWN = Meltdown +menu.disasters.TORNADO = Tornado +menu.disasters.EARTHQUAKE = Earthquake + +menu.speed = Speed +menu.speed.SUPER_FAST = Super Fast +menu.speed.FAST = Fast +menu.speed.NORMAL = Normal +menu.speed.SLOW = Slow +menu.speed.PAUSED = Paused + +menu.windows = Windows +menu.windows.budget = Budget +menu.windows.evaluation = Evaluation +menu.windows.graph = Graph + +menu.help = Help +menu.help.about = About + +! +! Tools +! +tool.BULLDOZER.name = BULLDOZER +tool.BULLDOZER.icon = /icdozr.png +tool.BULLDOZER.selected_icon = /icdozrhi.png +tool.BULLDOZER.tip = Bulldozer +tool.WIRE.name = WIRE +tool.WIRE.icon = /icwire.png +tool.WIRE.selected_icon = /icwirehi.png +tool.WIRE.tip = Build Powerlines +tool.PARK.name = PARK +tool.PARK.icon = /icpark.png +tool.PARK.selected_icon = /icparkhi.png +tool.PARK.tip = Build Parks +tool.ROADS.name = ROADS +tool.ROADS.icon = /icroad.png +tool.ROADS.selected_icon = /icroadhi.png +tool.ROADS.tip = Build Roads +tool.RAIL.name = RAIL +tool.RAIL.icon = /icrail.png +tool.RAIL.selected_icon = /icrailhi.png +tool.RAIL.tip = Build Tracks +tool.RESIDENTIAL.name = RESIDENTIAL +tool.RESIDENTIAL.icon = /icres.png +tool.RESIDENTIAL.selected_icon = /icreshi.png +tool.RESIDENTIAL.tip = Zone Residential +tool.COMMERCIAL.name = COMMERCIAL +tool.COMMERCIAL.icon = /iccom.png +tool.COMMERCIAL.selected_icon = /iccomhi.png +tool.COMMERCIAL.tip = Zone Commercial +tool.INDUSTRIAL.name = INDUSTRIAL +tool.INDUSTRIAL.icon = /icind.png +tool.INDUSTRIAL.selected_icon = /icindhi.png +tool.INDUSTRIAL.tip = Zone Industrial +tool.FIRE.name = FIRE +tool.FIRE.icon = /icfire.png +tool.FIRE.selected_icon = /icfirehi.png +tool.FIRE.tip = Build Fire Station +tool.POLICE.name = POLICE +tool.POLICE.icon = /icpol.png +tool.POLICE.selected_icon = /icpolhi.png +tool.POLICE.tip = Build Police Station +tool.POWERPLANT.name = POWERPLANT +tool.POWERPLANT.icon = /iccoal.png +tool.POWERPLANT.selected_icon = /iccoalhi.png +tool.POWERPLANT.tip = Build Coal Powerplant +tool.NUCLEAR.name = NUCLEAR +tool.NUCLEAR.icon = /icnuc.png +tool.NUCLEAR.selected_icon = /icnuchi.png +tool.NUCLEAR.tip = Build Nuclear Powerplant +tool.STADIUM.name = STADIUM +tool.STADIUM.icon = /icstad.png +tool.STADIUM.selected_icon = /icstadhi.png +tool.STADIUM.tip = Build Stadium +tool.SEAPORT.name = SEAPORT +tool.SEAPORT.icon = /icseap.png +tool.SEAPORT.selected_icon = /icseaphi.png +tool.SEAPORT.tip = Build Port +tool.AIRPORT.name = AIRPORT +tool.AIRPORT.icon = /icairp.png +tool.AIRPORT.selected_icon = /icairphi.png +tool.AIRPORT.tip = Build Airport +tool.QUERY.name = QUERY +tool.QUERY.icon = /icqry.png +tool.QUERY.selected_icon = /icqryhi.png +tool.QUERY.tip = Query Zone Status + +tool.BULLDOZER.border = #bf7900 +tool.WIRE.border = #ffff00 +tool.ROADS.border = #5d5d5d +tool.RAIL.border = #5d5d5d +tool.RESIDENTIAL.border = #00ff00 +tool.COMMERCIAL.border = #0000ff +tool.INDUSTRIAL.border = #ffff00 +tool.FIRE.border = #ff0000 +tool.POLICE.border = #0000ff +tool.STADIUM.border = #00ff00 +tool.PARK.border = #bf7900 +tool.SEAPORT.border = #0000ff +tool.POWERPLANT.border = #ffff00 +tool.NUCLEAR.border = #ffff00 +tool.AIRPORT.border = #bf7900 + +tool.BULLDOZER.bgcolor = rgba(0,0,0,0) +tool.WIRE.bgcolor = rgba(0,0,0,0.375) +tool.ROADS.bgcolor = rgba(255,255,255,0.375) +tool.RAIL.bgcolor = rgba(127,127,0,0.375) +tool.RESIDENTIAL.bgcolor = rgba(0,255,0,0.375) +tool.COMMERCIAL.bgcolor = rgba(0,0,255,0.375) +tool.INDUSTRIAL.bgcolor = rgba(255,255,0,0.375) +tool.FIRE.bgcolor = rgba(0,255,0,0.375) +tool.POLICE.bgcolor = rgba(0,255,0,0.375) +tool.POWERPLANT.bgcolor = rgba(93,93,93,0.375) +tool.NUCLEAR.bgcolor = rgba(93,93,93,0.375) +tool.STADIUM.bgcolor = rgba(93,93,93,0.375) +tool.SEAPORT.bgcolor = rgba(93,93,93,0.375) +tool.AIRPORT.bgcolor = rgba(93,93,93,0.375) +tool.PARK.bgcolor = rgba(0,255,0,0.375) + +! +! The Graphs pane (accessible through Window -> Graphs) +! +dismiss_graph = Dismiss Graph +ten_years = 10 YRS +onetwenty_years = 120 YRS + +graph_button.RESPOP = grres.png +graph_button.COMPOP = grcom.png +graph_button.INDPOP = grind.png +graph_button.MONEY = grmony.png +graph_button.CRIME = grcrim.png +graph_button.POLLUTION = grpoll.png + +graph_button.RESPOP.selected = grreshi.png +graph_button.COMPOP.selected = grcomhi.png +graph_button.INDPOP.selected = grindhi.png +graph_button.MONEY.selected = grmonyhi.png +graph_button.CRIME.selected = grcrimhi.png +graph_button.POLLUTION.selected = grpollhi.png + +graph_color.RESPOP = #00e600 +graph_color.COMPOP = #0000e6 +graph_color.INDPOP = #ffff00 +graph_color.MONEY = #007f00 +graph_color.CRIME = #7f0000 +graph_color.POLLUTION = #997f4c + +graph_label.RESPOP = Residential +graph_label.COMPOP = Commercial +graph_label.INDPOP = Industrial +graph_label.MONEY = Cash Flow +graph_label.CRIME = Crime +graph_label.POLLUTION = Pollution + +! +! The Evaluation Pane (accessible through Windows -> Evaluation) +! +dismiss-evaluation = Dismiss Evaluation +public-opinion = Public Opinion +public-opinion-1 = Is the mayor doing a good job? +public-opinion-2 = What are the worst problems? +public-opinion-yes = YES +public-opinion-no = NO +statistics-head = Statistics +city-score-head = Overall City Score (0 - 1000) +stats-population = Population: +stats-net-migration = Net Migration: +stats-last-year = (last year) +stats-assessed-value = Assessed Value: +stats-category = Category: +stats-game-level = Game Level: +city-score-current = Current Score: +city-score-change = Annual Change: + +! +! The mini-map, overlay legends +! +legend_image.POPDEN_OVERLAY = /legendmm.png +legend_image.GROWTHRATE_OVERLAY = /legendpm.png +legend_image.LANDVALUE_OVERLAY = /legendmm.png +legend_image.CRIME_OVERLAY = /legendmm.png +legend_image.POLLUTE_OVERLAY = /legendmm.png +legend_image.TRAFFIC_OVERLAY = /legendmm.png +legend_image.FIRE_OVERLAY = /legendmm.png +legend_image.POLICE_OVERLAY = /legendmm.png + +! +! Budget Dialog box, accessable through Windows -> Budget +! +budgetdlg.title = Budget +budgetdlg.funding_level_hdr = Funding Level +budgetdlg.allocation_hdr = Allocation +budgetdlg.road_fund = Trans. Fund +budgetdlg.police_fund = Police Fund +budgetdlg.fire_fund = Fire Fund +budgetdlg.continue = Continue With These Figures +budgetdlg.reset = Reset to Original Figures +budgetdlg.tax_rate_hdr = Tax Rate +budgetdlg.annual_receipts_hdr = Annual Receipts +budgetdlg.tax_revenue = Tax Revenue +budgetdlg.period_ending = Period Ending +budgetdlg.cash_begin = Cash, beginning of year +budgetdlg.taxes_collected = Taxes Collected +budgetdlg.capital_expenses = Capital Expenditures +budgetdlg.operating_expenses = Operating Expenses +budgetdlg.cash_end = Cash, end of year +budgetdlg.auto_budget = Auto Budget +budgetdlg.pause_game = Pause Game + +! +! Notification pane +! +notification.dismiss = Dismiss +notification.query_hdr = Query Zone Status +notification.zone_lbl = Zone: +notification.density_lbl = Density: +notification.value_lbl = Value: +notification.crime_lbl = Crime: +notification.pollution_lbl = Pollution: +notification.growth_lbl = Growth: diff --git a/strings/StatusMessages.properties b/strings/StatusMessages.properties new file mode 100644 index 0000000..29eaa55 --- /dev/null +++ b/strings/StatusMessages.properties @@ -0,0 +1,62 @@ +!! 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. + +zone.0 = Clear +zone.1 = Water +zone.2 = Trees +zone.3 = Rubble +zone.4 = Flood +zone.5 = Radioactive Waste +zone.6 = Fire +zone.7 = Road +zone.8 = Power +zone.9 = Rail +zone.10 = Residential +zone.11 = Commercial +zone.12 = Industrial +zone.13 = Seaport +zone.14 = Airport +zone.15 = Coal Power +zone.16 = Fire Department +zone.17 = Police Department +zone.18 = Stadium +zone.19 = Nuclear Power +zone.20 = Draw Bridge +zone.21 = Radar Dish +zone.22 = Fountain +zone.23 = Industrial +zone.24 = Steelers 38 Bears 3 +zone.25 = Draw Bridge +zone.26 = Ur 238 +zone.27 = + +! population density +status.1 = Low +status.2 = Medium +status.3 = High +status.4 = Very High +! land value +status.5 = Slum +status.6 = Lower Class +status.7 = Middle Class +status.8 = High +! crime level +status.9 = Safe +status.10 = Light +status.11 = Moderate +status.12 = Dangerous +! pollution +status.13 = None +status.14 = Moderate +status.15 = Heavy +status.16 = Very Heavy +! growth rate +status.17 = Declining +status.18 = Stable +status.19 = Slow Growth +status.20 = Fast Growth