Methapolis  0.27
 All Classes Namespaces Files Functions Variables Enumerator
TileSpec.java
Go to the documentation of this file.
1 // This file is part of MicropolisJ.
2 // Copyright (C) 2013 Jason Long
3 // Portions Copyright (C) 1989-2007 Electronic Arts Inc.
4 //
5 // MicropolisJ is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU GPLv3, with additional terms.
7 // See the README file, included in this distribution, for details.
8 
9 package micropolisj.engine;
10 
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Properties;
16 
17 public class TileSpec {
18  int tileNumber;
19  TileSpec animNext;
20  TileSpec onPower;
21  TileSpec onShutdown;
22  boolean canBulldoze;
23  boolean canBurn;
24  boolean canConduct;
25  boolean overWater;
26  boolean zone;
27  public TileSpec owner;
28  public int ownerOffsetX;
29  public int ownerOffsetY;
30  BuildingInfo buildingInfo;
31 
32 
33 
34  Map<String, String> attributes;
35  List<String> images;
36 
37  private final static int POLLUTION_FACTOR = 25;
38 
39  protected TileSpec(int tileNumber) {
40  this.tileNumber = tileNumber;
41  this.attributes = new HashMap<String, String>();
42  this.images = new ArrayList<String>();
43  }
44 
45  public static TileSpec parse(int tileNumber, String inStr, Properties tilesRc) {
46  TileSpec ts = new TileSpec(tileNumber);
47  ts.load(inStr, tilesRc);
48  return ts;
49  }
50 
51  public String getAttribute(String key) {
52  return attributes.get(key);
53  }
54 
55  public boolean getBooleanAttribute(String key) {
56  String v = getAttribute(key);
57  return (v != null && v.equals("true"));
58  }
59 
60  public static class BuildingInfo {
61  int width;
62  int height;
63  short[] members;
64  // TODO: add player id here
65  }
66 
67  public BuildingInfo getBuildingInfo() {
68  return buildingInfo;
69  }
70 
71  private void resolveBuildingInfo(Map<String, TileSpec> tileMap) {
72  String tmp = getAttribute("building");
73  if(tmp == null) {
74  return;
75  }
76 
77  BuildingInfo bi = new BuildingInfo();
78 
79  String[] p2 = tmp.split("x");
80  bi.width = Integer.parseInt(p2[0]);
81  bi.height = Integer.parseInt(p2[1]);
82 
83  bi.members = new short[bi.width * bi.height];
84  int startTile = tileNumber;
85  if(bi.width >= 3) {
86  startTile--;
87  }
88  if(bi.height >= 3) {
89  startTile -= bi.width;
90  }
91 
92  for(int row = 0; row < bi.height; row++) {
93  for(int col = 0; col < bi.width; col++) {
94  bi.members[row * bi.width + col] = (short) startTile;
95  startTile++;
96  }
97  }
98 
99  this.buildingInfo = bi;
100  }
101 
103  if(buildingInfo != null) {
104  return new CityDimension(buildingInfo.width, buildingInfo.height);
105  }
106  else {
107  return null;
108  }
109  }
110 
111  public int getDescriptionNumber() {
112  String v = getAttribute("description");
113  if(v != null && v.startsWith("#")) {
114  return Integer.parseInt(v.substring(1));
115  }
116  if(owner != null) {
117  return owner.getDescriptionNumber();
118  }
119  return -1;
120  }
121 
122  public String[] getImages() {
123  return images.toArray(new String[0]);
124  }
125 
126  //custom: apply research value to TilePollution
127  //TODO Pollution dependent on owner of tile
128  private int usePollutionResearch(int pollutionValue, Micropolis city){
129  return Math.max(0, pollutionValue - city.playerInfo.researchData.environmentResearch * POLLUTION_FACTOR);
130  }
131 
132  public int getPollutionValue(Micropolis city) {
133  String v = getAttribute("pollution");
134  if(v != null) {
135  return usePollutionResearch(Integer.parseInt(v), city);
136  }
137  else if(owner != null) {
138  // pollution inherits from building tile
139  return owner.getPollutionValue(city);
140  }
141  else {
142  return 0;
143  }
144  }
145 
146  public int getPopulation() {
147  String v = getAttribute("population");
148  if(v != null) {
149  return Integer.parseInt(v);
150  }
151  else {
152  return 0;
153  }
154  }
155 
156  protected void load(String inStr, Properties tilesRc) {
157  Scanner in = new Scanner(inStr);
158 
159  while(in.hasMore()) {
160 
161  if(in.peekChar() == '(') {
162  in.eatChar('(');
163  String k = in.readAttributeKey();
164  String v = "true";
165  if(in.peekChar() == '=') {
166  in.eatChar('=');
167  v = in.readAttributeValue();
168  }
169  in.eatChar(')');
170 
171  if(!attributes.containsKey(k)) {
172  attributes.put(k, v);
173  String sup = tilesRc.getProperty(k);
174  if(sup != null) {
175  load(sup, tilesRc);
176  }
177  }
178  else {
179  attributes.put(k, v);
180  }
181  }
182 
183  else if(in.peekChar() == '|' || in.peekChar() == ',') {
184  in.eatChar(in.peekChar());
185  }
186 
187  else {
188  String v = in.readImageSpec();
189  images.add(v);
190  }
191  }
192 
193  this.canBulldoze = getBooleanAttribute("bulldozable");
194  this.canBurn = !getBooleanAttribute("noburn");
195  this.canConduct = getBooleanAttribute("conducts");
196  this.overWater = getBooleanAttribute("overwater");
197  this.zone = getBooleanAttribute("zone");
198  }
199 
200  static class Scanner {
201  String str;
202  int off = 0;
203 
204  Scanner(String str) {
205  this.str = str;
206  }
207 
208  private void skipWhitespace() {
209  while(off < str.length() && Character.isWhitespace(str.charAt(off))) {
210  off++;
211  }
212  }
213 
214  public int peekChar() {
215  skipWhitespace();
216  if(off < str.length()) {
217  return str.charAt(off);
218  }
219  else {
220  return -1;
221  }
222  }
223 
224  public void eatChar(int ch) {
225  skipWhitespace();
226  assert str.charAt(off) == ch;
227  off++;
228  }
229 
230  public String readAttributeKey() {
231  skipWhitespace();
232 
233  int start = off;
234  while(off < str.length() && (str.charAt(off) == '-' || Character.isLetterOrDigit(str.charAt(off)))) {
235  off++;
236  }
237 
238  if(off != start) {
239  return str.substring(start, off);
240  }
241  else {
242  return null;
243  }
244  }
245 
246  public String readAttributeValue() {
247  return readString();
248  }
249 
250  public String readImageSpec() {
251  return readString();
252  }
253 
254  protected String readString() {
255  skipWhitespace();
256 
257  int endQuote = 0; // any whitespace or certain punctuation
258  if(peekChar() == '"') {
259  off++;
260  endQuote = '"';
261  }
262 
263  int start = off;
264  while(off < str.length()) {
265  int c = str.charAt(off);
266  if(c == endQuote) {
267  int end = off;
268  off++;
269  return str.substring(start, end);
270  }
271  else if(endQuote == 0 && (Character.isWhitespace(c) || c == ')' || c == '|')) {
272  int end = off;
273  return str.substring(start, end);
274  }
275  off++;
276  }
277  return str.substring(start);
278  }
279 
280  public boolean hasMore() {
281  return peekChar() != -1;
282  }
283  }
284 
285  public String toString() {
286  return "{tile#" + tileNumber + "}";
287  }
288 
289  void resolveReferences(Map<String, TileSpec> tileMap) {
290  String tmp = this.getAttribute("becomes");
291  if(tmp != null) {
292  this.animNext = tileMap.get(tmp);
293  }
294  tmp = this.getAttribute("onpower");
295  if(tmp != null) {
296  this.onPower = tileMap.get(tmp);
297  }
298  tmp = this.getAttribute("onshutdown");
299  if(tmp != null) {
300  this.onShutdown = tileMap.get(tmp);
301  }
302  tmp = this.getAttribute("building-part");
303  if(tmp != null) {
304  this.handleBuildingPart(tmp, tileMap);
305  }
306 
307  resolveBuildingInfo(tileMap);
308  }
309 
310  private void handleBuildingPart(String text, Map<String, TileSpec> tileMap) {
311  String[] parts = text.split(",");
312  if(parts.length != 3) {
313  throw new Error("Invalid building-part specification");
314  }
315 
316  this.owner = tileMap.get(parts[0]);
317  this.ownerOffsetX = Integer.parseInt(parts[1]);
318  this.ownerOffsetY = Integer.parseInt(parts[2]);
319 
320  assert this.owner != null;
321  assert this.ownerOffsetX != 0 || this.ownerOffsetY != 0;
322  }
323 
324  public static String[] generateTileNames(Properties recipe) {
325  int ntiles = recipe.size();
326  String[] tileNames = new String[ntiles];
327  ntiles = 0;
328  for(int i = 0; recipe.containsKey(Integer.toString(i)); i++) {
329  tileNames[ntiles++] = Integer.toString(i);
330  }
331  int naturalNumberTiles = ntiles;
332 
333  for(Object n_obj : recipe.keySet()) {
334  String n = (String) n_obj;
335  if(n.matches("^\\d+$")) {
336  int x = Integer.parseInt(n);
337  if(x >= 0 && x < naturalNumberTiles) {
338  assert tileNames[x].equals(n);
339  continue;
340  }
341  }
342  assert ntiles < tileNames.length;
343  tileNames[ntiles++] = n;
344  }
345  assert ntiles == tileNames.length;
346  return tileNames;
347  }
348 }