mirror of
https://github.com/tonytins/citylimits.git
synced 2025-03-15 12:21:22 +00:00
1038 lines
26 KiB
C++
1038 lines
26 KiB
C++
|
/* zone.cpp
|
||
|
*
|
||
|
* Micropolis, Unix Version. This game was released for the Unix platform
|
||
|
* in or about 1990 and has been modified for inclusion in the One Laptop
|
||
|
* Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If
|
||
|
* you need assistance with this program, you may contact:
|
||
|
* http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org.
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||
|
* your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but
|
||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* General Public License for more details. You should have received a
|
||
|
* copy of the GNU General Public License along with this program. If
|
||
|
* not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* ADDITIONAL TERMS per GNU GPL Section 7
|
||
|
*
|
||
|
* No trademark or publicity rights are granted. This license does NOT
|
||
|
* give you any right, title or interest in the trademark SimCity or any
|
||
|
* other Electronic Arts trademark. You may not distribute any
|
||
|
* modification of this program using the trademark SimCity or claim any
|
||
|
* affliation or association with Electronic Arts Inc. or its employees.
|
||
|
*
|
||
|
* Any propagation or conveyance of this program must include this
|
||
|
* copyright notice and these terms.
|
||
|
*
|
||
|
* If you convey this program (or any modifications of it) and assume
|
||
|
* contractual liability for the program to recipients of it, you agree
|
||
|
* to indemnify Electronic Arts for any liability that those contractual
|
||
|
* assumptions impose on Electronic Arts.
|
||
|
*
|
||
|
* You may not misrepresent the origins of this program; modified
|
||
|
* versions of the program must be marked as such and not identified as
|
||
|
* the original program.
|
||
|
*
|
||
|
* This disclaimer supplements the one included in the General Public
|
||
|
* License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
|
||
|
* PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
|
||
|
* OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF
|
||
|
* SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS
|
||
|
* DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
|
||
|
* INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
|
||
|
* RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
|
||
|
* USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST
|
||
|
* INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
|
||
|
* MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
|
||
|
* UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
|
||
|
* WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
|
||
|
* CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
|
||
|
* ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME
|
||
|
* JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
|
||
|
* WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
|
||
|
* CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
|
||
|
* NOT APPLY TO YOU.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @file zone.cpp
|
||
|
* @brief Manages zone processing in Micropolis.
|
||
|
*
|
||
|
* This file contains functions related to zone processing in the
|
||
|
* Micropolis game. It includes routines for handling different types
|
||
|
* of zones such as residential, commercial, and industrial. Functions
|
||
|
* in this file are responsible for simulating zone growth, decline,
|
||
|
* and effects like pollution, power supply, and traffic generation.
|
||
|
* It also includes utility functions for evaluating zone conditions
|
||
|
* and performing zone transformations such as building or demolishing
|
||
|
* structures in response to game conditions.
|
||
|
*/
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
#include "micropolis.h"
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Handle zone.
|
||
|
* @param pos Position of the zone.
|
||
|
*/
|
||
|
void Micropolis::doZone(const Position &pos)
|
||
|
{
|
||
|
// Set Power Bit in Map from powerGridMap
|
||
|
bool zonePowerFlag = setZonePower(pos);
|
||
|
|
||
|
|
||
|
if (zonePowerFlag) {
|
||
|
poweredZoneCount++;
|
||
|
} else {
|
||
|
unpoweredZoneCount++;
|
||
|
}
|
||
|
|
||
|
MapTile tile = map[pos.posX][pos.posY] & LOMASK;
|
||
|
|
||
|
// Do special zones.
|
||
|
if ((tile > PORTBASE) &&
|
||
|
(tile < CHURCH1BASE)) {
|
||
|
doSpecialZone(pos, zonePowerFlag);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Do residential zones.
|
||
|
if (tile < HOSPITAL) {
|
||
|
doResidential(pos, zonePowerFlag);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Do hospitals and churches.
|
||
|
if ((tile < COMBASE) ||
|
||
|
((tile >= CHURCH1BASE) &&
|
||
|
(tile <= CHURCH7LAST))) {
|
||
|
doHospitalChurch(pos);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Do commercial zones.
|
||
|
if (tile < INDBASE) {
|
||
|
doCommercial(pos, zonePowerFlag);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Do industrial zones.
|
||
|
if (tile < CHURCH1BASE) {
|
||
|
doIndustrial(pos, zonePowerFlag);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
printf("UNEXPOECTED ZONE: %d !!!\n", tile);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle repairing or removing of hospitals and churches.
|
||
|
* @param pos Position of the hospital or church.
|
||
|
*/
|
||
|
void Micropolis::doHospitalChurch(const Position &pos)
|
||
|
{
|
||
|
MapTile tile = map[pos.posX][pos.posY] & LOMASK;
|
||
|
|
||
|
if (tile == HOSPITAL) {
|
||
|
|
||
|
hospitalPop++;
|
||
|
|
||
|
if (!(cityTime & 15)) {
|
||
|
repairZone(pos, HOSPITAL, 3);
|
||
|
}
|
||
|
|
||
|
if (needHospital == -1) { // Too many hospitals!
|
||
|
if (!getRandom(20)) {
|
||
|
zonePlop(pos, RESBASE); // Remove hospital.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else if ((tile == CHURCH0) ||
|
||
|
(tile == CHURCH1) ||
|
||
|
(tile == CHURCH2) ||
|
||
|
(tile == CHURCH3) ||
|
||
|
(tile == CHURCH4) ||
|
||
|
(tile == CHURCH5) ||
|
||
|
(tile == CHURCH6) ||
|
||
|
(tile == CHURCH7)) {
|
||
|
|
||
|
churchPop++;
|
||
|
|
||
|
//printf("CHURCH %d %d %d %d\n", churchPop, pos.posX, pos.posY, tile);
|
||
|
|
||
|
bool simulate = true;
|
||
|
|
||
|
if (!(cityTime & 15)) {
|
||
|
repairZone(pos, tile, 3);
|
||
|
}
|
||
|
|
||
|
if (needChurch == -1) { // Too many churches!
|
||
|
if (!getRandom(20)) {
|
||
|
zonePlop(pos, RESBASE); // Remove church.
|
||
|
simulate = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (simulate) {
|
||
|
//printf("SIM %d %d %d\n", pos.posX, pos.posY, tile);
|
||
|
|
||
|
int churchNumber = 0;
|
||
|
|
||
|
switch (tile) {
|
||
|
case CHURCH0:
|
||
|
churchNumber = 0;
|
||
|
break;
|
||
|
case CHURCH1:
|
||
|
churchNumber = 1;
|
||
|
break;
|
||
|
case CHURCH2:
|
||
|
churchNumber = 2;
|
||
|
break;
|
||
|
case CHURCH3:
|
||
|
churchNumber = 3;
|
||
|
break;
|
||
|
case CHURCH4:
|
||
|
churchNumber = 4;
|
||
|
break;
|
||
|
case CHURCH5:
|
||
|
churchNumber = 5;
|
||
|
break;
|
||
|
case CHURCH6:
|
||
|
churchNumber = 6;
|
||
|
break;
|
||
|
case CHURCH7:
|
||
|
churchNumber = 7;
|
||
|
break;
|
||
|
default:
|
||
|
assert(0); // Unexpected church tile
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
callback->simulateChurch(this, callbackVal, pos.posX, pos.posY, churchNumber);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
#define ASCBIT (ANIMBIT | CONDBIT | BURNBIT)
|
||
|
#define REGBIT (CONDBIT | BURNBIT)
|
||
|
|
||
|
void Micropolis::setSmoke(const Position &pos, bool zonePower)
|
||
|
{
|
||
|
static bool aniThis[8] = { true, false, true, true, false, false, true, true };
|
||
|
static short dx1[8] = { -1, 0, 1, 0, 0, 0, 0, 1 };
|
||
|
static short dy1[8] = { -1, 0, -1, -1, 0, 0, -1, -1 };
|
||
|
static short aniTabA[8] = { 0, 0, 32, 40, 0, 0, 48, 56 };
|
||
|
static short aniTabB[8] = { 0, 0, 36, 44, 0, 0, 52, 60 };
|
||
|
static short aniTabC[8] = { IND1, 0, IND2, IND4, 0, 0, IND6, IND8 };
|
||
|
static short aniTabD[8] = { IND1, 0, IND3, IND5, 0, 0, IND7, IND9 };
|
||
|
|
||
|
MapTile tile = map[pos.posX][pos.posY] & LOMASK;
|
||
|
|
||
|
if (tile < IZB) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int z = (tile - IZB) >>3; /// @todo Why div 8? Industry is 9 tiles long!!
|
||
|
z = z & 7;
|
||
|
|
||
|
if (aniThis[z]) {
|
||
|
int xx = pos.posX + dx1[z];
|
||
|
int yy = pos.posY + dy1[z];
|
||
|
|
||
|
if (testBounds(xx, yy)) {
|
||
|
|
||
|
if (zonePower) {
|
||
|
|
||
|
/// @todo Why do we assign the same map position twice?
|
||
|
/// @todo Add #SMOKEBASE into aniTabA and aniTabB tables?
|
||
|
if ((map[xx][yy] & LOMASK) == aniTabC[z]) {
|
||
|
map[xx][yy] = ASCBIT | (SMOKEBASE + aniTabA[z]);
|
||
|
map[xx][yy] = ASCBIT | (SMOKEBASE + aniTabB[z]);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/// @todo Why do we assign the same map position twice?
|
||
|
if ((map[xx][yy] & LOMASK) > aniTabC[z]) {
|
||
|
map[xx][yy] = REGBIT | aniTabC[z];
|
||
|
map[xx][yy] = REGBIT | aniTabD[z];
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* If needed, add a new hospital or a new church.
|
||
|
* @param pos Center position of the new hospital or church.
|
||
|
*/
|
||
|
void Micropolis::makeHospital(const Position &pos)
|
||
|
{
|
||
|
if (needHospital > 0) {
|
||
|
zonePlop(pos, HOSPITAL - 4);
|
||
|
needHospital = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (needChurch > 0) {
|
||
|
int churchType = getRandom(7); // 0 to 7 inclusive
|
||
|
int tile;
|
||
|
if (churchType == 0) {
|
||
|
tile = CHURCH0;
|
||
|
} else {
|
||
|
tile = CHURCH1 + ((churchType - 1) * 9);
|
||
|
}
|
||
|
|
||
|
//printf("NEW CHURCH tile %d x %d y %d type %d\n", tile, pos.posX, pos.posY, churchType);
|
||
|
|
||
|
zonePlop(pos, tile - 4);
|
||
|
needChurch = 0;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Compute land value at \a pos, taking pollution into account.
|
||
|
* @param pos Position of interest.
|
||
|
* @return Indication of land-value adjusted for pollution
|
||
|
* (\c 0 => low value, \c 3 => high value)
|
||
|
*/
|
||
|
short Micropolis::getLandPollutionValue(const Position &pos)
|
||
|
{
|
||
|
short landVal;
|
||
|
|
||
|
landVal = landValueMap.worldGet(pos.posX, pos.posY);
|
||
|
landVal -= pollutionDensityMap.worldGet(pos.posX, pos.posY);
|
||
|
|
||
|
if (landVal < 30) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (landVal < 80) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (landVal < 150) {
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Update the rate of growth at position \a pos by \a amount.
|
||
|
* @param pos Position to modify.
|
||
|
* @param amount Amount of change (can both be positive and negative).
|
||
|
*/
|
||
|
void Micropolis::incRateOfGrowth(const Position &pos, int amount)
|
||
|
{
|
||
|
int value = rateOfGrowthMap.worldGet(pos.posX, pos.posY);
|
||
|
|
||
|
value = clamp(value + amount * 4, -200, 200);
|
||
|
rateOfGrowthMap.worldSet(pos.posX, pos.posY, value);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Put down a 3x3 zone around the center tile at \a pos..
|
||
|
* @param base Tile number of the top-left tile. @see Tiles
|
||
|
* @return Build was a success.
|
||
|
* @bug This function allows partial on-map construction. Is that intentional? No!
|
||
|
*/
|
||
|
bool Micropolis::zonePlop(const Position &pos, int base)
|
||
|
{
|
||
|
short z, x;
|
||
|
static const short Zx[9] = {-1, 0, 1,-1, 0, 1,-1, 0, 1};
|
||
|
static const short Zy[9] = {-1,-1,-1, 0, 0, 0, 1, 1, 1};
|
||
|
|
||
|
for (z = 0; z < 9; z++) { /* check for fire */
|
||
|
int xx = pos.posX + Zx[z];
|
||
|
int yy = pos.posY + Zy[z];
|
||
|
|
||
|
if (testBounds(xx, yy)) {
|
||
|
x = map[xx][yy] & LOMASK;
|
||
|
|
||
|
if ((x >= FLOOD) && (x < ROADBASE)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
for (z = 0; z < 9; z++) {
|
||
|
int xx = pos.posX + Zx[z];
|
||
|
int yy = pos.posY + Zy[z];
|
||
|
|
||
|
if (testBounds(xx, yy)) {
|
||
|
map[xx][yy] = base + BNCNBIT;
|
||
|
}
|
||
|
|
||
|
base++;
|
||
|
}
|
||
|
|
||
|
setZonePower(pos);
|
||
|
map[pos.posX][pos.posY] |= ZONEBIT + BULLBIT;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Count the number of single tile houses in a residential zone.
|
||
|
* @param pos Position of the residential zone.
|
||
|
* @return Number of single tile houses.
|
||
|
*/
|
||
|
short Micropolis::doFreePop(const Position &pos)
|
||
|
{
|
||
|
short count = 0;
|
||
|
|
||
|
for (short x = pos.posX - 1; x <= pos.posX + 1; x++) {
|
||
|
for (short y = pos.posY - 1; y <= pos.posY + 1; y++) {
|
||
|
if (x >= 0 && x < WORLD_W && y >= 0 && y < WORLD_H) {
|
||
|
MapTile tile = map[x][y] & LOMASK;
|
||
|
if (tile >= LHTHR && tile <= HHTHR) {
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Copy the value of #powerGridMap at position \a pos to the map.
|
||
|
* @param pos Position to copy.
|
||
|
* @return Does the tile have power?
|
||
|
*/
|
||
|
bool Micropolis::setZonePower(const Position &pos)
|
||
|
{
|
||
|
MapValue mapValue = map[pos.posX][pos.posY];
|
||
|
MapTile tile = mapValue & LOMASK;
|
||
|
|
||
|
if (tile == NUCLEAR || tile == POWERPLANT) {
|
||
|
map[pos.posX][pos.posY] = mapValue | PWRBIT;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (powerGridMap.worldGet(pos.posX, pos.posY)) {
|
||
|
map[pos.posX][pos.posY] = mapValue | PWRBIT;
|
||
|
return true;
|
||
|
} else {
|
||
|
map[pos.posX][pos.posY] = mapValue & (~PWRBIT);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Try to build a house at the zone at \a pos.
|
||
|
* @param pos Center tile of the zone.
|
||
|
* @param value Value to build (land value?)
|
||
|
* @todo Have some form of looking around the center tile (like getFromMap())
|
||
|
*/
|
||
|
void Micropolis::buildHouse(const Position &pos, int value)
|
||
|
{
|
||
|
short z, score, hscore, BestLoc;
|
||
|
static short ZeX[9] = { 0,-1, 0, 1,-1, 1,-1, 0, 1};
|
||
|
static short ZeY[9] = { 0,-1,-1,-1, 0, 0, 1, 1, 1};
|
||
|
|
||
|
BestLoc = 0;
|
||
|
hscore = 0;
|
||
|
|
||
|
for (z = 1; z < 9; z++) {
|
||
|
int xx = pos.posX + ZeX[z];
|
||
|
int yy = pos.posY + ZeY[z];
|
||
|
|
||
|
if (testBounds(xx, yy)) {
|
||
|
|
||
|
score = evalLot(xx, yy);
|
||
|
|
||
|
/// @bug score is never 0 !!
|
||
|
if (score != 0) {
|
||
|
|
||
|
if (score > hscore) {
|
||
|
hscore = score;
|
||
|
BestLoc = z;
|
||
|
}
|
||
|
|
||
|
/// @todo Move the code below to a better place.
|
||
|
/// If we just updated hscore above, we could
|
||
|
// trigger this code too.
|
||
|
if (score == hscore && !(getRandom16() & 7)) {
|
||
|
BestLoc = z;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (BestLoc) {
|
||
|
int xx = pos.posX + ZeX[BestLoc];
|
||
|
int yy = pos.posY + ZeY[BestLoc];
|
||
|
|
||
|
if (testBounds(xx, yy)) {
|
||
|
/// @todo Is HOUSE the proper constant here?
|
||
|
map[xx][yy] = HOUSE + BLBNCNBIT + getRandom(2) + value * 3;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Evaluate suitability of the position for placing a new house.
|
||
|
* @return Suitability.
|
||
|
*/
|
||
|
short Micropolis::evalLot(int x, int y)
|
||
|
{
|
||
|
short z, score;
|
||
|
static short DX[4] = { 0, 1, 0,-1};
|
||
|
static short DY[4] = {-1, 0, 1, 0};
|
||
|
|
||
|
/* test for clear lot */
|
||
|
z = map[x][y] & LOMASK;
|
||
|
|
||
|
if (z && (z < RESBASE || z > RESBASE + 8)) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
score = 1;
|
||
|
|
||
|
for (z = 0; z < 4; z++) {
|
||
|
int xx = x + DX[z];
|
||
|
int yy = y + DY[z];
|
||
|
|
||
|
if (testBounds(xx, yy) &&
|
||
|
map[xx][yy] != DIRT && (map[xx][yy] & LOMASK) <= LASTROAD) {
|
||
|
score++; /* look for road */
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return score;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle residential zone.
|
||
|
* @param pos Center tile of the residential zone.
|
||
|
* @param zonePwrFlg Does the zone have power?
|
||
|
*/
|
||
|
void Micropolis::doResidential(const Position &pos, bool zonePower)
|
||
|
{
|
||
|
short tpop, zscore, locvalve, value, TrfGood;
|
||
|
|
||
|
resZonePop++;
|
||
|
|
||
|
MapTile tile = map[pos.posX][pos.posY] & LOMASK;
|
||
|
|
||
|
if (tile == FREEZ) {
|
||
|
tpop = doFreePop(pos);
|
||
|
} else {
|
||
|
tpop = getResZonePop(tile);
|
||
|
}
|
||
|
|
||
|
resPop += tpop;
|
||
|
|
||
|
if (tpop > getRandom(35)) {
|
||
|
/* Try driving from residential to commercial */
|
||
|
TrfGood = makeTraffic(pos, ZT_COMMERCIAL);
|
||
|
} else {
|
||
|
TrfGood = 1;
|
||
|
}
|
||
|
|
||
|
if (TrfGood == -1) {
|
||
|
value = getLandPollutionValue(pos);
|
||
|
doResOut(pos, tpop, value);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (tile == FREEZ || !(getRandom16() & 7)) {
|
||
|
|
||
|
locvalve = evalRes(pos, TrfGood);
|
||
|
zscore = resValve + locvalve;
|
||
|
|
||
|
if (!zonePower) {
|
||
|
zscore = -500;
|
||
|
}
|
||
|
|
||
|
if (zscore > -350 &&
|
||
|
((short)(zscore - 26380) > ((short)getRandom16Signed()))) {
|
||
|
|
||
|
if (!tpop && !(getRandom16() & 3)) {
|
||
|
makeHospital(pos);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
value = getLandPollutionValue(pos);
|
||
|
doResIn(pos, tpop, value);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (zscore < 350 &&
|
||
|
(((short)(zscore + 26380)) < ((short)getRandom16Signed()))) {
|
||
|
value = getLandPollutionValue(pos);
|
||
|
doResOut(pos, tpop, value);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Perform residential immigration into the current residential tile.
|
||
|
* @param pos Position of the tile.
|
||
|
* @param pop Population ?
|
||
|
* @param value Land value corrected for pollution.
|
||
|
*/
|
||
|
void Micropolis::doResIn(const Position &pos, int pop, int value)
|
||
|
{
|
||
|
short pollution = pollutionDensityMap.worldGet(pos.posX, pos.posY);
|
||
|
|
||
|
if (pollution > 128) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
MapTile tile = map[pos.posX][pos.posY] & LOMASK;
|
||
|
|
||
|
if (tile == FREEZ) {
|
||
|
|
||
|
if (pop < 8) {
|
||
|
buildHouse(pos, value);
|
||
|
incRateOfGrowth(pos, 1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (populationDensityMap.worldGet(pos.posX, pos.posY) > 64) {
|
||
|
resPlop(pos, 0, value);
|
||
|
incRateOfGrowth(pos, 8);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pop < 40) {
|
||
|
resPlop(pos, (pop / 8) - 1, value);
|
||
|
incRateOfGrowth(pos, 8);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Perform residential emigration from the current residential tile.
|
||
|
* @param pos Position of the tile.
|
||
|
* @param pop Population ?
|
||
|
* @param value Land value corrected for pollution.
|
||
|
*/
|
||
|
void Micropolis::doResOut(const Position &pos, int pop, int value)
|
||
|
{
|
||
|
static short Brdr[9] = {0,3,6,1,4,7,2,5,8};
|
||
|
short x, y, loc, z;
|
||
|
|
||
|
if (!pop) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pop > 16) {
|
||
|
resPlop(pos, (pop - 24) / 8, value);
|
||
|
incRateOfGrowth(pos, -8);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pop == 16) {
|
||
|
incRateOfGrowth(pos, -8);
|
||
|
map[pos.posX][pos.posY] = (FREEZ | BLBNCNBIT | ZONEBIT);
|
||
|
for (x = pos.posX - 1; x <= pos.posX + 1; x++) {
|
||
|
for (y = pos.posY - 1; y <= pos.posY + 1; y++) {
|
||
|
if (testBounds(x, y)) {
|
||
|
if ((map[x][y] & LOMASK) != FREEZ) {
|
||
|
map[x][y] = LHTHR + value + getRandom(2) + BLBNCNBIT;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pop < 16) {
|
||
|
incRateOfGrowth(pos, -1);
|
||
|
z = 0;
|
||
|
for (x = pos.posX - 1; x <= pos.posX + 1; x++) {
|
||
|
for (y = pos.posY - 1; y <= pos.posY + 1; y++) {
|
||
|
if (testBounds(x, y)) {
|
||
|
loc = map[x][y] & LOMASK;
|
||
|
if ((loc >= LHTHR) && (loc <= HHTHR)) {
|
||
|
map[x][y] = Brdr[z] + BLBNCNBIT + FREEZ - 4;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
z++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return population of a residential zone center tile
|
||
|
* (RZB, RZB+9, ..., HOSPITAL - 9).
|
||
|
*
|
||
|
* @param mapTile Center tile of a residential zone.
|
||
|
* @return Population of the residential zone.
|
||
|
* (16, 24, 32, 40, 16, ..., 40 )
|
||
|
*/
|
||
|
short Micropolis::getResZonePop(MapTile mapTile)
|
||
|
{
|
||
|
short CzDen = ((mapTile - RZB) / 9) % 4;
|
||
|
|
||
|
return CzDen * 8 + 16;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Put down a residential zone.
|
||
|
* @param pos Center tile of the residential zone.
|
||
|
* @param den Population density (0..3)
|
||
|
* @param value Land value - pollution (0..3), higher is better.
|
||
|
*/
|
||
|
void Micropolis::resPlop(const Position &pos, int den, int value)
|
||
|
{
|
||
|
short base;
|
||
|
|
||
|
base = ((value * 4 + den) * 9) + RZB - 4;
|
||
|
zonePlop(pos, base);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Evaluate residential zone.
|
||
|
*/
|
||
|
short Micropolis::evalRes(const Position &pos, int traf)
|
||
|
{
|
||
|
short value;
|
||
|
|
||
|
if (traf < 0) {
|
||
|
return -3000;
|
||
|
}
|
||
|
|
||
|
value = landValueMap.worldGet(pos.posX, pos.posY);
|
||
|
value -= pollutionDensityMap.worldGet(pos.posX, pos.posY);
|
||
|
|
||
|
if (value < 0) {
|
||
|
value = 0; /* Cap at 0 */
|
||
|
} else {
|
||
|
value = min(value * 32, 6000); /* Cap at 6000 */
|
||
|
}
|
||
|
|
||
|
value = value - 3000;
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Handle commercial zone.
|
||
|
* @param pos Position of the commercial zone.
|
||
|
* @param zonePwrFlg Does the zone have power?
|
||
|
* @todo Make zonePwrFlg a boolean.
|
||
|
*/
|
||
|
void Micropolis::doCommercial(const Position &pos, bool zonePower)
|
||
|
{
|
||
|
short tpop, TrfGood;
|
||
|
short zscore, locvalve, value;
|
||
|
|
||
|
MapTile tile = map[pos.posX][pos.posY] & LOMASK;
|
||
|
|
||
|
comZonePop++;
|
||
|
tpop = getComZonePop(tile);
|
||
|
comPop += tpop;
|
||
|
|
||
|
if (tpop > getRandom(5)) {
|
||
|
/* Try driving from commercial to industrial */
|
||
|
TrfGood = makeTraffic(pos, ZT_INDUSTRIAL);
|
||
|
} else {
|
||
|
TrfGood = 1;
|
||
|
}
|
||
|
|
||
|
if (TrfGood == -1) {
|
||
|
value = getLandPollutionValue(pos);
|
||
|
doComOut(pos, tpop, value);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!(getRandom16() & 7)) {
|
||
|
|
||
|
locvalve = evalCom(pos, TrfGood);
|
||
|
zscore = comValve + locvalve;
|
||
|
|
||
|
if (!zonePower) {
|
||
|
zscore = -500;
|
||
|
}
|
||
|
|
||
|
if (TrfGood &&
|
||
|
(zscore > -350) &&
|
||
|
(((short)(zscore - 26380)) > ((short)getRandom16Signed()))) {
|
||
|
value = getLandPollutionValue(pos);
|
||
|
doComIn(pos, tpop, value);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((zscore < 350) &&
|
||
|
(((short)(zscore + 26380)) < ((short)getRandom16Signed()))) {
|
||
|
value = getLandPollutionValue(pos);
|
||
|
doComOut(pos, tpop, value);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Handle immigration of commercial zone.
|
||
|
* @param pos Position of the commercial zone.
|
||
|
* @param pop Population ?
|
||
|
* @param value Land value corrected for pollution.
|
||
|
*/
|
||
|
void Micropolis::doComIn(const Position &pos, int pop, int value)
|
||
|
{
|
||
|
short z;
|
||
|
|
||
|
z = landValueMap.worldGet(pos.posX, pos.posY);
|
||
|
z = z >>5;
|
||
|
|
||
|
if (pop > z) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pop < 5) {
|
||
|
comPlop(pos, pop, value);
|
||
|
incRateOfGrowth(pos, 8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle emigration of commercial zone.
|
||
|
* @param pos Position of the commercial zone.
|
||
|
* @param pop Population ?
|
||
|
* @param value Land value corrected for pollution.
|
||
|
*/
|
||
|
void Micropolis::doComOut(const Position &pos, int pop, int value)
|
||
|
{
|
||
|
if (pop > 1) {
|
||
|
comPlop(pos, pop - 2, value);
|
||
|
incRateOfGrowth(pos, -8);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pop == 1) {
|
||
|
zonePlop(pos, COMBASE);
|
||
|
incRateOfGrowth(pos, -8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Get commercial zone population number.
|
||
|
* @param tile Tile of the commercial zone.
|
||
|
* @return Population number of the zone.
|
||
|
*/
|
||
|
short Micropolis::getComZonePop(MapTile tile)
|
||
|
{
|
||
|
if (tile == COMCLR) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
short CzDen = ((tile - CZB) / 9) % 5 + 1;
|
||
|
return CzDen;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Build a commercial zone.
|
||
|
* @param pos Position of the commercial zone.
|
||
|
* @param Den Density
|
||
|
* @param Value Land value corrected for pollution.
|
||
|
*/
|
||
|
void Micropolis::comPlop(const Position &pos, int Den, int Value)
|
||
|
{
|
||
|
short base;
|
||
|
|
||
|
base = ((Value * 5) + Den) * 9 + CZB - 4;
|
||
|
zonePlop(pos, base);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Compute evaluation of a commercial zone.
|
||
|
* @param traf Result if traffic attempt.
|
||
|
* @return Evaluation value of the commercial zone.
|
||
|
*/
|
||
|
short Micropolis::evalCom(const Position &pos, int traf)
|
||
|
{
|
||
|
short Value;
|
||
|
|
||
|
if (traf < 0) {
|
||
|
return -3000;
|
||
|
}
|
||
|
|
||
|
Value = comRateMap.worldGet(pos.posX, pos.posY);
|
||
|
|
||
|
return Value;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Handle industrial zone.
|
||
|
* @param pos Position of the industrial zone.
|
||
|
* @param zonePwrFlg Does the zone have power?
|
||
|
* @todo Make zonePwrFlg a boolean.
|
||
|
*/
|
||
|
void Micropolis::doIndustrial(const Position &pos, bool zonePower)
|
||
|
{
|
||
|
short tpop, zscore, TrfGood;
|
||
|
|
||
|
MapTile tile = map[pos.posX][pos.posY] & LOMASK;
|
||
|
|
||
|
indZonePop++;
|
||
|
setSmoke(pos, zonePower);
|
||
|
tpop = getIndZonePop(tile);
|
||
|
indPop += tpop;
|
||
|
|
||
|
if (tpop > getRandom(5)) {
|
||
|
/* Try driving from industrial to residential */
|
||
|
TrfGood = makeTraffic(pos, ZT_RESIDENTIAL);
|
||
|
} else {
|
||
|
TrfGood = 1;
|
||
|
}
|
||
|
|
||
|
if (TrfGood == -1) {
|
||
|
doIndOut(pos, tpop, getRandom16() & 1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!(getRandom16() & 7)) {
|
||
|
zscore = indValve + evalInd(TrfGood);
|
||
|
|
||
|
if (!zonePower) {
|
||
|
zscore = -500;
|
||
|
}
|
||
|
|
||
|
if (zscore > -350 &&
|
||
|
(((short)(zscore - 26380)) > ((short)getRandom16Signed()))) {
|
||
|
doIndIn(pos, tpop, getRandom16() & 1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (zscore < 350 &&
|
||
|
(((short)(zscore + 26380)) < ((short)getRandom16Signed()))) {
|
||
|
doIndOut(pos, tpop, getRandom16() & 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Handle immigration of industrial zone.
|
||
|
* @param pos Position of the center tile of the industrial tile.
|
||
|
* @param pos Population value of the industrial zone.
|
||
|
* @param value Random land value (it seems).
|
||
|
*/
|
||
|
void Micropolis::doIndIn(const Position &pos, int pop, int value)
|
||
|
{
|
||
|
if (pop < 4) {
|
||
|
indPlop(pos, pop, value);
|
||
|
incRateOfGrowth(pos, 8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle industrial zone emigration.
|
||
|
* @param pos Position of the center tile of the industrial tile.
|
||
|
* @param pos Population value of the industrial zone.
|
||
|
* @param value Random land value (it seems).
|
||
|
*/
|
||
|
void Micropolis::doIndOut(const Position &pos, int pop, int value)
|
||
|
{
|
||
|
if (pop > 1) {
|
||
|
indPlop(pos, pop - 2, value);
|
||
|
incRateOfGrowth(pos, -8);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pop == 1) {
|
||
|
zonePlop(pos, INDBASE); // empty industrial zone
|
||
|
incRateOfGrowth(pos, -8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Get the population value for the given industrial tile.
|
||
|
* @param tile Center tile value of the industrial zone.
|
||
|
* @return Population value.
|
||
|
*/
|
||
|
short Micropolis::getIndZonePop(MapTile tile)
|
||
|
{
|
||
|
if (tile == INDCLR) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
short CzDen = (((tile - IZB) / 9) % 4) + 1;
|
||
|
return CzDen;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Place an industrial zone around center tile \a pos.
|
||
|
* @param pos Center of the industrial zone.
|
||
|
* @param den Population density of the industrial zone (0, 1, 2, or 3).
|
||
|
* @param value Landvalue of the industrial zone (0 or 1).
|
||
|
*/
|
||
|
void Micropolis::indPlop(const Position &pos, int den, int value)
|
||
|
{
|
||
|
short base = ((value * 4) + den) * 9 + IND1;
|
||
|
zonePlop(pos, base);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Compute evaluation of an industrial zone.
|
||
|
* @param traf Result if traffic attempt.
|
||
|
* @return Evaluation value of the industrial zone.
|
||
|
*/
|
||
|
short Micropolis::evalInd(int traf)
|
||
|
{
|
||
|
if (traf < 0) {
|
||
|
return -1000;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|