behaviors: move terrain-related behaviors from MapScanner

git-svn-id: https://micropolis.googlecode.com/svn/trunk/micropolis-java@871 d9718cc8-9f43-0410-858b-315f434eb58c
This commit is contained in:
jason@long.name 2013-10-04 12:54:03 +00:00
parent 4717c93039
commit 81c05604df
4 changed files with 381 additions and 348 deletions

View file

@ -30,12 +30,6 @@ class MapScanner extends TileBehavior
public static enum B
{
FIRE,
FLOOD,
RADIOACTIVE,
ROAD,
RAIL,
EXPLOSION,
RESIDENTIAL,
HOSPITAL_CHURCH,
COMMERCIAL,
@ -50,31 +44,10 @@ class MapScanner extends TileBehavior
SEAPORT;
}
/**
* Activate the tile identified by xpos and ypos properties.
*/
@Override
public void apply()
{
switch (behavior) {
case FIRE:
doFire();
return;
case FLOOD:
doFlood();
return;
case RADIOACTIVE:
doRadioactiveTile();
return;
case ROAD:
doRoad();
return;
case RAIL:
doRail();
return;
case EXPLOSION:
// clear AniRubble
city.setTile(xpos, ypos, (char)(RUBBLE + PRNG.nextInt(4) + BULLBIT));
return;
case RESIDENTIAL:
doResidential();
return;
@ -116,320 +89,6 @@ class MapScanner extends TileBehavior
}
}
/**
* Called when the current tile is a radioactive tile.
*/
void doRadioactiveTile()
{
if (PRNG.nextInt(4096) == 0)
{
// radioactive decay
city.setTile(xpos, ypos, DIRT);
}
}
static int [] TRAFFIC_DENSITY_TAB = { ROADBASE, LTRFBASE, HTRFBASE };
/**
* Called when the current tile is a road tile.
*/
void doRoad()
{
city.roadTotal++;
if (city.roadEffect < 30)
{
// deteriorating roads
if (PRNG.nextInt(512) == 0)
{
if (!isConductive(rawTile))
{
if (city.roadEffect < PRNG.nextInt(32))
{
if (isOverWater(rawTile))
city.setTile(xpos, ypos, RIVER);
else
city.setTile(xpos, ypos, (char)(RUBBLE + PRNG.nextInt(4) + BULLBIT));
return;
}
}
}
}
if (!isCombustible(rawTile)) //bridge
{
city.roadTotal += 4;
if (doBridge())
return;
}
int tden;
if ((rawTile & LOMASK) < LTRFBASE)
tden = 0;
else if ((rawTile & LOMASK) < HTRFBASE)
tden = 1;
else {
city.roadTotal++;
tden = 2;
}
int trafficDensity = city.getTrafficDensity(xpos, ypos);
int newLevel = trafficDensity < 64 ? 0 :
trafficDensity < 192 ? 1 : 2;
assert newLevel >= 0 && newLevel < TRAFFIC_DENSITY_TAB.length;
if (tden != newLevel)
{
int z = (((rawTile & LOMASK) - ROADBASE) & 15) + TRAFFIC_DENSITY_TAB[newLevel];
z += rawTile & ALLBITS;
city.setTile(xpos, ypos, (char) z);
}
}
/**
* Called when the current tile is an active fire.
*/
void doFire()
{
city.firePop++;
// one in four times
if (PRNG.nextInt(4) != 0) {
return;
}
final int [] DX = { 0, 1, 0, -1 };
final int [] DY = { -1, 0, 1, 0 };
for (int dir = 0; dir < 4; dir++)
{
if (PRNG.nextInt(8) == 0)
{
int xtem = xpos + DX[dir];
int ytem = ypos + DY[dir];
if (!city.testBounds(xtem, ytem))
continue;
int c = city.map[ytem][xtem];
if (isCombustible(c)) {
if (isZoneCenter(c)) {
city.killZone(xtem, ytem, c);
if ((c & LOMASK) > IZB) { //explode
city.makeExplosion(xtem, ytem);
}
}
city.setTile(xtem, ytem, (char)(FIRE + PRNG.nextInt(4)));
}
}
}
int cov = city.fireRate[ypos/8][xpos/8]; //fire station coverage
int rate = cov > 100 ? 1 :
cov > 20 ? 2 :
cov != 0 ? 3 : 10;
if (PRNG.nextInt(rate+1) == 0) {
city.setTile(xpos, ypos, (char)(RUBBLE + PRNG.nextInt(4) + BULLBIT));
}
}
/**
* Called when the current tile is a flooding tile.
*/
void doFlood()
{
final int [] DX = { 0, 1, 0, -1 };
final int [] DY = { -1, 0, 1, 0 };
if (city.floodCnt != 0)
{
for (int z = 0; z < 4; z++)
{
if (PRNG.nextInt(8) == 0) {
int xx = xpos + DX[z];
int yy = ypos + DY[z];
if (city.testBounds(xx, yy)) {
int c = city.getTile(xx, yy);
int t = c & LOMASK;
if (isCombustible(c) || c == DIRT ||
(t >= WOODS5 && t < FLOOD))
{
if (isZoneCenter(c)) {
city.killZone(xx, yy, c);
}
city.setTile(xx, yy, (char)(FLOOD + PRNG.nextInt(3)));
}
}
}
}
}
else {
if (PRNG.nextInt(16) == 0) {
city.setTile(xpos, ypos, DIRT);
}
}
}
/**
* Called when the current tile is railroad.
*/
void doRail()
{
city.railTotal++;
city.generateTrain(xpos, ypos);
if (city.roadEffect < 30) { // deteriorating rail
if (PRNG.nextInt(512) == 0) {
if (!isConductive(rawTile)) {
if (city.roadEffect < PRNG.nextInt(32)) {
if (isOverWater(rawTile)) {
city.setTile(xpos,ypos,RIVER);
} else {
city.setTile(xpos,ypos,(char)(RUBBLE + PRNG.nextInt(4)+BULLBIT));
}
}
}
}
}
}
/**
* Called when the current tile is a road bridge over water.
* Handles the draw bridge. For the draw bridge to appear,
* there must be a boat on the water, the boat must be
* within a certain distance of the bridge, it must be where
* the map generator placed 'channel' tiles (these are tiles
* that look just like regular river tiles but have a different
* numeric value), and you must be a little lucky.
*
* @return true if the draw bridge is open; false otherwise
*/
boolean doBridge()
{
final int HDx[] = { -2, 2, -2, -1, 0, 1, 2 };
final int HDy[] = { -1, -1, 0, 0, 0, 0, 0 };
final char HBRTAB[] = {
HBRDG1 | BULLBIT, HBRDG3 | BULLBIT,
HBRDG0 | BULLBIT, RIVER,
BRWH | BULLBIT, RIVER,
HBRDG2 | BULLBIT };
final char HBRTAB2[] = {
RIVER, RIVER,
HBRIDGE | BULLBIT, HBRIDGE | BULLBIT,
HBRIDGE | BULLBIT, HBRIDGE | BULLBIT,
HBRIDGE | BULLBIT };
final int VDx[] = { 0, 1, 0, 0, 0, 0, 1 };
final int VDy[] = { -2, -2, -1, 0, 1, 2, 2 };
final char VBRTAB[] = {
VBRDG0 | BULLBIT, VBRDG1 | BULLBIT,
RIVER, BRWV | BULLBIT,
RIVER, VBRDG2 | BULLBIT,
VBRDG3 | BULLBIT };
final char VBRTAB2[] = {
VBRIDGE | BULLBIT, RIVER,
VBRIDGE | BULLBIT, VBRIDGE | BULLBIT,
VBRIDGE | BULLBIT, VBRIDGE | BULLBIT,
RIVER };
if (tile == BRWV) {
// vertical bridge, open
if (PRNG.nextInt(4) == 0 && getBoatDis() > 340/16) {
//close the bridge
applyBridgeChange(VDx, VDy, VBRTAB, VBRTAB2);
}
return true;
}
else if (tile == BRWH) {
// horizontal bridge, open
if (PRNG.nextInt(4) == 0 && getBoatDis() > 340/16) {
// close the bridge
applyBridgeChange(HDx, HDy, HBRTAB, HBRTAB2);
}
return true;
}
if (getBoatDis() < 300/16 && PRNG.nextInt(8) == 0) {
if ((rawTile & 1) != 0) {
// vertical bridge
if (xpos < city.getWidth()-1) {
// look for CHANNEL tile to right of
// bridge. the CHANNEL tiles are only
// found in the very center of the
// river
if (city.getTile(xpos+1,ypos) == CHANNEL) {
// vertical bridge, open it up
applyBridgeChange(VDx, VDy, VBRTAB2, VBRTAB);
return true;
}
}
return false;
}
else {
// horizontal bridge
if (ypos > 0) {
// look for CHANNEL tile just above
// bridge. the CHANNEL tiles are only
// found in the very center of the
// river
if (city.getTile(xpos, ypos-1) == CHANNEL) {
// open it up
applyBridgeChange(HDx, HDy, HBRTAB2, HBRTAB);
return true;
}
}
return false;
}
}
return false;
}
/**
* Helper function for doBridge- it toggles the draw-bridge.
*/
private void applyBridgeChange(int [] Dx, int [] Dy, char [] fromTab, char [] toTab)
{
//FIXME- a closed bridge with traffic on it is not
// correctly handled by this subroutine, because the
// the tiles representing traffic on a bridge do not match
// the expected tile values of fromTab
for (int z = 0; z < 7; z++) {
int x = xpos + Dx[z];
int y = ypos + Dy[z];
if (city.testBounds(x,y)) {
if ((city.map[y][x] & LOMASK) == (fromTab[z] & LOMASK) ||
(city.map[y][x] == CHANNEL)
) {
city.setTile(x, y, toTab[z]);
}
}
}
}
/**
* Calculate how far away the boat currently is from the
* current tile.
*/
int getBoatDis()
{
int dist = 99999;
for (Sprite s : city.sprites)
{
if (s.isVisible() && s.kind == SpriteKind.SHI)
{
int x = s.x / 16;
int y = s.y / 16;
int d = Math.abs(xpos-x) + Math.abs(ypos-y);
dist = Math.min(d, dist);
}
}
return dist;
}
boolean checkZonePower()
{
boolean zonePwrFlag = setZonePower();

View file

@ -1395,12 +1395,12 @@ public class Micropolis
HashMap<String,TileBehavior> bb;
bb = new HashMap<String,TileBehavior>();
bb.put("FIRE", new MapScanner(this, MapScanner.B.FIRE));
bb.put("FLOOD", new MapScanner(this, MapScanner.B.FLOOD));
bb.put("RADIOACTIVE", new MapScanner(this, MapScanner.B.RADIOACTIVE));
bb.put("ROAD", new MapScanner(this, MapScanner.B.ROAD));
bb.put("RAIL", new MapScanner(this, MapScanner.B.RAIL));
bb.put("EXPLOSION", new MapScanner(this, MapScanner.B.EXPLOSION));
bb.put("FIRE", new TerrainBehavior(this, TerrainBehavior.B.FIRE));
bb.put("FLOOD", new TerrainBehavior(this, TerrainBehavior.B.FLOOD));
bb.put("RADIOACTIVE", new TerrainBehavior(this, TerrainBehavior.B.RADIOACTIVE));
bb.put("ROAD", new TerrainBehavior(this, TerrainBehavior.B.ROAD));
bb.put("RAIL", new TerrainBehavior(this, TerrainBehavior.B.RAIL));
bb.put("EXPLOSION", new TerrainBehavior(this, TerrainBehavior.B.EXPLOSION));
bb.put("RESIDENTIAL", new MapScanner(this, MapScanner.B.RESIDENTIAL));
bb.put("HOSPITAL_CHURCH", new MapScanner(this, MapScanner.B.HOSPITAL_CHURCH));
bb.put("COMMERCIAL", new MapScanner(this, MapScanner.B.COMMERCIAL));

View file

@ -0,0 +1,371 @@
// 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.engine;
import static micropolisj.engine.TileConstants.*;
class TerrainBehavior extends TileBehavior
{
final B behavior;
TerrainBehavior(Micropolis city, B behavior)
{
super(city);
this.behavior = behavior;
}
static enum B
{
FIRE,
FLOOD,
RADIOACTIVE,
ROAD,
RAIL,
EXPLOSION;
}
@Override
public void apply()
{
switch (behavior) {
case FIRE:
doFire();
return;
case FLOOD:
doFlood();
return;
case RADIOACTIVE:
doRadioactiveTile();
return;
case ROAD:
doRoad();
return;
case RAIL:
doRail();
return;
case EXPLOSION:
// clear AniRubble
city.setTile(xpos, ypos, (char)(RUBBLE + PRNG.nextInt(4) + BULLBIT));
return;
default:
assert false;
}
}
void doFire()
{
city.firePop++;
// one in four times
if (PRNG.nextInt(4) != 0) {
return;
}
final int [] DX = { 0, 1, 0, -1 };
final int [] DY = { -1, 0, 1, 0 };
for (int dir = 0; dir < 4; dir++)
{
if (PRNG.nextInt(8) == 0)
{
int xtem = xpos + DX[dir];
int ytem = ypos + DY[dir];
if (!city.testBounds(xtem, ytem))
continue;
int c = city.map[ytem][xtem];
if (isCombustible(c)) {
if (isZoneCenter(c)) {
city.killZone(xtem, ytem, c);
if ((c & LOMASK) > IZB) { //explode
city.makeExplosion(xtem, ytem);
}
}
city.setTile(xtem, ytem, (char)(FIRE + PRNG.nextInt(4)));
}
}
}
int cov = city.fireRate[ypos/8][xpos/8]; //fire station coverage
int rate = cov > 100 ? 1 :
cov > 20 ? 2 :
cov != 0 ? 3 : 10;
if (PRNG.nextInt(rate+1) == 0) {
city.setTile(xpos, ypos, (char)(RUBBLE + PRNG.nextInt(4) + BULLBIT));
}
}
/**
* Called when the current tile is a flooding tile.
*/
void doFlood()
{
final int [] DX = { 0, 1, 0, -1 };
final int [] DY = { -1, 0, 1, 0 };
if (city.floodCnt != 0)
{
for (int z = 0; z < 4; z++)
{
if (PRNG.nextInt(8) == 0) {
int xx = xpos + DX[z];
int yy = ypos + DY[z];
if (city.testBounds(xx, yy)) {
int c = city.getTile(xx, yy);
int t = c & LOMASK;
if (isCombustible(c) || c == DIRT ||
(t >= WOODS5 && t < FLOOD))
{
if (isZoneCenter(c)) {
city.killZone(xx, yy, c);
}
city.setTile(xx, yy, (char)(FLOOD + PRNG.nextInt(3)));
}
}
}
}
}
else {
if (PRNG.nextInt(16) == 0) {
city.setTile(xpos, ypos, DIRT);
}
}
}
/**
* Called when the current tile is a radioactive tile.
*/
void doRadioactiveTile()
{
if (PRNG.nextInt(4096) == 0)
{
// radioactive decay
city.setTile(xpos, ypos, DIRT);
}
}
static int [] TRAFFIC_DENSITY_TAB = { ROADBASE, LTRFBASE, HTRFBASE };
/**
* Called when the current tile is a road tile.
*/
void doRoad()
{
city.roadTotal++;
if (city.roadEffect < 30)
{
// deteriorating roads
if (PRNG.nextInt(512) == 0)
{
if (!isConductive(rawTile))
{
if (city.roadEffect < PRNG.nextInt(32))
{
if (isOverWater(rawTile))
city.setTile(xpos, ypos, RIVER);
else
city.setTile(xpos, ypos, (char)(RUBBLE + PRNG.nextInt(4) + BULLBIT));
return;
}
}
}
}
if (!isCombustible(rawTile)) //bridge
{
city.roadTotal += 4;
if (doBridge())
return;
}
int tden;
if ((rawTile & LOMASK) < LTRFBASE)
tden = 0;
else if ((rawTile & LOMASK) < HTRFBASE)
tden = 1;
else {
city.roadTotal++;
tden = 2;
}
int trafficDensity = city.getTrafficDensity(xpos, ypos);
int newLevel = trafficDensity < 64 ? 0 :
trafficDensity < 192 ? 1 : 2;
assert newLevel >= 0 && newLevel < TRAFFIC_DENSITY_TAB.length;
if (tden != newLevel)
{
int z = (((rawTile & LOMASK) - ROADBASE) & 15) + TRAFFIC_DENSITY_TAB[newLevel];
z += rawTile & ALLBITS;
city.setTile(xpos, ypos, (char) z);
}
}
/**
* Called when the current tile is railroad.
*/
void doRail()
{
city.railTotal++;
city.generateTrain(xpos, ypos);
if (city.roadEffect < 30) { // deteriorating rail
if (PRNG.nextInt(512) == 0) {
if (!isConductive(rawTile)) {
if (city.roadEffect < PRNG.nextInt(32)) {
if (isOverWater(rawTile)) {
city.setTile(xpos,ypos,RIVER);
} else {
city.setTile(xpos,ypos,(char)(RUBBLE + PRNG.nextInt(4)+BULLBIT));
}
}
}
}
}
}
/**
* Called when the current tile is a road bridge over water.
* Handles the draw bridge. For the draw bridge to appear,
* there must be a boat on the water, the boat must be
* within a certain distance of the bridge, it must be where
* the map generator placed 'channel' tiles (these are tiles
* that look just like regular river tiles but have a different
* numeric value), and you must be a little lucky.
*
* @return true if the draw bridge is open; false otherwise
*/
boolean doBridge()
{
final int HDx[] = { -2, 2, -2, -1, 0, 1, 2 };
final int HDy[] = { -1, -1, 0, 0, 0, 0, 0 };
final char HBRTAB[] = {
HBRDG1 | BULLBIT, HBRDG3 | BULLBIT,
HBRDG0 | BULLBIT, RIVER,
BRWH | BULLBIT, RIVER,
HBRDG2 | BULLBIT };
final char HBRTAB2[] = {
RIVER, RIVER,
HBRIDGE | BULLBIT, HBRIDGE | BULLBIT,
HBRIDGE | BULLBIT, HBRIDGE | BULLBIT,
HBRIDGE | BULLBIT };
final int VDx[] = { 0, 1, 0, 0, 0, 0, 1 };
final int VDy[] = { -2, -2, -1, 0, 1, 2, 2 };
final char VBRTAB[] = {
VBRDG0 | BULLBIT, VBRDG1 | BULLBIT,
RIVER, BRWV | BULLBIT,
RIVER, VBRDG2 | BULLBIT,
VBRDG3 | BULLBIT };
final char VBRTAB2[] = {
VBRIDGE | BULLBIT, RIVER,
VBRIDGE | BULLBIT, VBRIDGE | BULLBIT,
VBRIDGE | BULLBIT, VBRIDGE | BULLBIT,
RIVER };
if (tile == BRWV) {
// vertical bridge, open
if (PRNG.nextInt(4) == 0 && getBoatDis() > 340/16) {
//close the bridge
applyBridgeChange(VDx, VDy, VBRTAB, VBRTAB2);
}
return true;
}
else if (tile == BRWH) {
// horizontal bridge, open
if (PRNG.nextInt(4) == 0 && getBoatDis() > 340/16) {
// close the bridge
applyBridgeChange(HDx, HDy, HBRTAB, HBRTAB2);
}
return true;
}
if (getBoatDis() < 300/16 && PRNG.nextInt(8) == 0) {
if ((rawTile & 1) != 0) {
// vertical bridge
if (xpos < city.getWidth()-1) {
// look for CHANNEL tile to right of
// bridge. the CHANNEL tiles are only
// found in the very center of the
// river
if (city.getTile(xpos+1,ypos) == CHANNEL) {
// vertical bridge, open it up
applyBridgeChange(VDx, VDy, VBRTAB2, VBRTAB);
return true;
}
}
return false;
}
else {
// horizontal bridge
if (ypos > 0) {
// look for CHANNEL tile just above
// bridge. the CHANNEL tiles are only
// found in the very center of the
// river
if (city.getTile(xpos, ypos-1) == CHANNEL) {
// open it up
applyBridgeChange(HDx, HDy, HBRTAB2, HBRTAB);
return true;
}
}
return false;
}
}
return false;
}
/**
* Helper function for doBridge- it toggles the draw-bridge.
*/
private void applyBridgeChange(int [] Dx, int [] Dy, char [] fromTab, char [] toTab)
{
//FIXME- a closed bridge with traffic on it is not
// correctly handled by this subroutine, because the
// the tiles representing traffic on a bridge do not match
// the expected tile values of fromTab
for (int z = 0; z < 7; z++) {
int x = xpos + Dx[z];
int y = ypos + Dy[z];
if (city.testBounds(x,y)) {
if ((city.map[y][x] & LOMASK) == (fromTab[z] & LOMASK) ||
(city.map[y][x] == CHANNEL)
) {
city.setTile(x, y, toTab[z]);
}
}
}
}
/**
* Calculate how far away the boat currently is from the
* current tile.
*/
int getBoatDis()
{
int dist = 99999;
for (Sprite s : city.sprites)
{
if (s.isVisible() && s.kind == SpriteKind.SHI)
{
int x = s.x / 16;
int y = s.y / 16;
int d = Math.abs(xpos-x) + Math.abs(ypos-y);
dist = Math.min(d, dist);
}
}
return dist;
}
}

View file

@ -35,5 +35,8 @@ public abstract class TileBehavior
apply();
}
/**
* Activate the tile identified by xpos and ypos properties.
*/
public abstract void apply();
}