Methapolis  0.27
 All Classes Namespaces Files Functions Variables Enumerator
ToolStroke.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.io.Serializable;
12 
13 import javax.swing.JOptionPane;
14 
15 import static micropolisj.engine.TileConstants.*;
16 import micropolisj.research.ResearchState;
17 import micropolisj.util.Utilities;
18 
19 public class ToolStroke implements Serializable {
20  transient Micropolis city;
21 
22  final MicropolisTool tool;
23  int xpos;
24  int ypos;
25  int xdest;
26  int ydest;
27  boolean inPreview;
28  int playerID;
29 
30  public ToolStroke(Micropolis city, MicropolisTool tool, int xpos, int ypos) {
31  this.city = city;
32  this.tool = tool;
33  this.xpos = xpos;
34  this.ypos = ypos;
35  this.xdest = xpos;
36  this.ydest = ypos;
37  playerID = city.getPlayerID();
38  // System.out.println(playerID);
39  }
40 
41  public void setCity(Micropolis engine) {
42  city = engine;
43  }
44 
45  public final ToolPreview getPreview() {
46  ToolEffect eff = new ToolEffect(city, playerID);
47  inPreview = true;
48  try {
49  applyArea(eff);
50  } finally {
51  inPreview = false;
52  }
53  return eff.preview;
54  }
55 
56  public final ToolResult apply() {
57  ToolEffect eff = new ToolEffect(city, playerID);
58  applyArea(eff);
59  return eff.apply(playerID);
60  }
61 
62  protected void applyArea(ToolEffectIfc eff) {
63  CityRect r = getBounds();
64 
65  for (int i = 0; i < r.height; i += tool.getHeight()) {
66  for (int j = 0; j < r.width; j += tool.getWidth()) {
67  apply1(new TranslatedToolEffect(eff, r.x + j, r.y + i));
68  }
69  }
70  }
71 
72 
73  boolean apply1(ToolEffectIfc eff) {
74  switch (tool) {
75  case PARK:
76  return applyParkTool(eff);
77 
78  case RESIDENTIAL:
79  return applyZone(eff, RESCLR);
80 
81  case COMMERCIAL:
82  return applyZone(eff, COMCLR);
83 
84  case INDUSTRIAL:
85  return applyZone(eff, INDCLR);
86 
87  case FIRE:
88  return applyZone(eff, FIRESTATION);
89 
90  case POLICE:
91  return applyZone(eff, POLICESTATION);
92 
93  case POWERPLANT:
94  return applyZone(eff, POWERPLANT);
95 
96  case STADIUM:
97  return applyZone(eff, STADIUM);
98 
99  case SEAPORT:
100  return applyZone(eff, PORT);
101 
102  case NUCLEAR:
103  return applyZone(eff, NUCLEAR);
104 
105  case AIRPORT:
106  return applyZone(eff, AIRPORT);
107 
108  // CUSTOM TOOLS
109  case UNIVERSITY:
110  return applyZone(eff, UNIVERSITY);
111 
112  case TEMPEL:
113  return applyZone(eff, TEMPEL);
114 
115  case ROCKET:
116  //TODO: fireMessage insufficient funds
117  if (city.getPlayerInfo(playerID).researchData.isRocketPossible()) {
118  // shoot rocket (aka monster) to location
119  if(city.getPlayerInfo(playerID).budget.totalFunds < MicropolisTool.ROCKET.getToolCost()) {
121  city.sendMessage(MicropolisMessage.INSUFFICIENT_FUNDS);
122  } else {
123  city.generateRocket(0, 0, xpos, ypos, playerID);
124  city.spend(MicropolisTool.ROCKET.getToolCost(), city.getPlayerInfo(playerID));
125  eff.toolResult(ToolResult.SUCCESS);
126  return true;
127  }
128  } else {
129  //JOptionPane.showMessageDialog(ResearchState.getInstance(), "You need some research before you can use rockets.");
130  city.sendMessage(MicropolisMessage.INSUFFICIENT_RESEARCH);
131  }
132  return false;
133 
134  default:
135  // not expected
136  throw new Error("unexpected tool: " + tool);
137  }
138  }
139 
140  public void dragTo(int xdest, int ydest) {
141  this.xdest = xdest;
142  this.ydest = ydest;
143  }
144 
145  public CityRect getBounds() {
146  CityRect r = new CityRect();
147 
148  r.x = xpos;
149  if (tool.getWidth() >= 3) {
150  r.x--;
151  }
152  if (xdest >= xpos) {
153  r.width = ((xdest - xpos) / tool.getWidth() + 1) * tool.getWidth();
154  } else {
155  r.width = ((xpos - xdest) / tool.getWidth() + 1) * tool.getHeight();
156  r.x += tool.getWidth() - r.width;
157  }
158 
159  r.y = ypos;
160  if (tool.getHeight() >= 3) {
161  r.y--;
162  }
163  if (ydest >= ypos) {
164  r.height = ((ydest - ypos) / tool.getHeight() + 1) * tool.getHeight();
165  } else {
166  r.height = ((ypos - ydest) / tool.getHeight() + 1) * tool.getHeight();
167  r.y += tool.getHeight() - r.height;
168  }
169 
170  return r;
171  }
172 
174  return new CityLocation(xpos, ypos);
175  }
176 
177 
178  boolean applyZone(ToolEffectIfc eff, int base) {
179  assert isZoneCenter(base);
180 
181  TileSpec.BuildingInfo bi = Tiles.get(base).getBuildingInfo();
182  if (bi == null) {
183  throw new Error("Cannot applyZone to #" + base);
184  }
185 
186  int cost = tool.getToolCost();
187  boolean canBuild = true;
188  for (int rowNum = 0; rowNum < bi.height; rowNum++) {
189  for (int columnNum = 0; columnNum < bi.width; columnNum++) {
190  int tileValue = eff.getTile(columnNum, rowNum);
191  tileValue = tileValue & LOMASK;
192 
193  if (tileValue != DIRT) {
194  if (city.autoBulldoze && canAutoBulldozeZ((char) tileValue)) {
195  cost++;
196  } else {
197  canBuild = false;
198  }
199  }
200  }
201  }
202  if (!canBuild) {
203  eff.toolResult(ToolResult.UH_OH);
204  return false;
205  }
206 
207  eff.spend(cost);
208 
209  int i = 0;
210  for (int rowNum = 0; rowNum < bi.height; rowNum++) {
211  for (int columnNum = 0; columnNum < bi.width; columnNum++) {
212  eff.setTile(columnNum, rowNum, bi.members[i]);
213  i++;
214  }
215  }
216 
217  fixBorder(eff, bi.width, bi.height);
218  return true;
219  }
220 
221  // compatible function
222  void fixBorder(int left, int top, int right, int bottom) {
223  ToolEffect eff = new ToolEffect(city, left, top, playerID);
224  fixBorder(eff, right + 1 - left, bottom + 1 - top);
225  eff.apply(playerID);
226  }
227 
228  void fixBorder(ToolEffectIfc eff, int width, int height) {
229  for (int x = 0; x < width; x++) {
230  fixZone(new TranslatedToolEffect(eff, x, 0));
231  fixZone(new TranslatedToolEffect(eff, x, height - 1));
232  }
233  for (int y = 1; y < height - 1; y++) {
234  fixZone(new TranslatedToolEffect(eff, 0, y));
235  fixZone(new TranslatedToolEffect(eff, width - 1, y));
236  }
237  }
238 
239  boolean applyParkTool(ToolEffectIfc eff) {
240  int cost = tool.getToolCost();
241 
242  if (eff.getTile(0, 0) != DIRT) {
243  // some sort of bulldozing is necessary
244  if (!city.autoBulldoze) {
245  eff.toolResult(ToolResult.UH_OH);
246  return false;
247  }
248 
249  // FIXME- use a canAutoBulldoze-style function here
250  if (isRubble(eff.getTile(0, 0))) {
251  // this tile can be auto-bulldozed
252  cost++;
253  } else {
254  // cannot be auto-bulldozed
255  eff.toolResult(ToolResult.UH_OH);
256  return false;
257  }
258  }
259 
260  int z = inPreview ? 0 : city.PRNG.nextInt(5);
261  int tile;
262  if (z < 4) {
263  tile = WOODS2 + z;
264  } else {
265  tile = FOUNTAIN;
266  }
267 
268  eff.spend(cost);
269  eff.setTile(0, 0, tile);
270 
271  return true;
272  }
273 
274  protected void fixZone(int xpos, int ypos) {
275  ToolEffect eff = new ToolEffect(city, xpos, ypos, playerID);
276  fixZone(eff);
277  eff.apply(playerID);
278  }
279 
280  protected void fixZone(ToolEffectIfc eff) {
281  fixSingle(eff);
282 
283  // "fix" the cells to the north, west, east, and south
284  fixSingle(new TranslatedToolEffect(eff, 0, -1));
285  fixSingle(new TranslatedToolEffect(eff, -1, 0));
286  fixSingle(new TranslatedToolEffect(eff, 1, 0));
287  fixSingle(new TranslatedToolEffect(eff, 0, 1));
288  }
289 
290  private void fixSingle(ToolEffectIfc eff) {
291  int tile = eff.getTile(0, 0);
292 
293  if (isRoadDynamic(tile)) {
294  // cleanup road
295  int adjTile = 0;
296 
297  // check road to north
298  if (roadConnectsSouth(eff.getTile(0, -1))) {
299  adjTile |= 1;
300  }
301 
302  // check road to east
303  if (roadConnectsWest(eff.getTile(1, 0))) {
304  adjTile |= 2;
305  }
306 
307  // check road to south
308  if (roadConnectsNorth(eff.getTile(0, 1))) {
309  adjTile |= 4;
310  }
311 
312  // check road to west
313  if (roadConnectsEast(eff.getTile(-1, 0))) {
314  adjTile |= 8;
315  }
316 
317  eff.setTile(0, 0, RoadTable[adjTile]);
318  } // endif on a road tile
319 
320  else if (isRailDynamic(tile)) {
321  // cleanup Rail
322  int adjTile = 0;
323 
324  // check rail to north
325  if (railConnectsSouth(eff.getTile(0, -1))) {
326  adjTile |= 1;
327  }
328 
329  // check rail to east
330  if (railConnectsWest(eff.getTile(1, 0))) {
331  adjTile |= 2;
332  }
333 
334  // check rail to south
335  if (railConnectsNorth(eff.getTile(0, 1))) {
336  adjTile |= 4;
337  }
338 
339  // check rail to west
340  if (railConnectsEast(eff.getTile(-1, 0))) {
341  adjTile |= 8;
342  }
343 
344  eff.setTile(0, 0, RailTable[adjTile]);
345  } // end if on a rail tile
346 
347  else if (isWireDynamic(tile)) {
348  // Cleanup Wire
349  int adjTile = 0;
350 
351  // check wire to north
352  if (wireConnectsSouth(eff.getTile(0, -1))) {
353  adjTile |= 1;
354  }
355 
356  // check wire to east
357  if (wireConnectsWest(eff.getTile(1, 0))) {
358  adjTile |= 2;
359  }
360 
361  // check wire to south
362  if (wireConnectsNorth(eff.getTile(0, 1))) {
363  adjTile |= 4;
364  }
365 
366  // check wire to west
367  if (wireConnectsEast(eff.getTile(-1, 0))) {
368  adjTile |= 8;
369  }
370 
371  eff.setTile(0, 0, WireTable[adjTile]);
372  } // end if on a rail tile
373 
374  return;
375  }
376 
377 }