citylimits/core/micropolis.cpp
2024-05-03 22:50:34 -04:00

1599 lines
39 KiB
C++

/* micropolis.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 micropolis.cpp
* @brief Primary implementation file for the Micropolis game engine.
*
* This file contains the core implementation of the Micropolis game
* engine. It includes the main class constructor, destructor,
* initialization and destruction functions, and other essential
* methods that underpin the game's functionality. The file covers
* various aspects of the game such as simulation control, disaster
* handling, sound effects, and map manipulation.
*/
////////////////////////////////////////////////////////////////////////
#include "micropolis.h"
////////////////////////////////////////////////////////////////////////
/**
* Simulator constructor.
*/
Micropolis::Micropolis() :
populationDensityMap(0),
trafficDensityMap(0),
pollutionDensityMap(0),
landValueMap(0),
crimeRateMap(0),
terrainDensityMap(0),
tempMap1(0),
tempMap2(0),
tempMap3(0),
powerGridMap(0),
rateOfGrowthMap(0),
fireStationMap(0),
fireStationEffectMap(0),
policeStationMap(0),
policeStationEffectMap(0),
comRateMap(0)
{
setCallback(new ConsoleCallback(), emscripten::val::null());
init();
}
/** Simulator destructor. */
Micropolis::~Micropolis()
{
setCallback(NULL, emscripten::val::null());
destroy();
}
void Micropolis::setCallback(Callback *callback0, emscripten::val callbackVal0)
{
if (callback != NULL) {
delete callback;
}
callback = callback0;
callbackVal = callbackVal0;
}
/** Initialize simulator variables to a sane default. */
void Micropolis::init()
{
////////////////////////////////////////////////////////////////////////
// allocate.cpp
// short roadTotal;
roadTotal = 0;
// short railTotal;
railTotal = 0;
// short firePop;
firePop = 0;
// short resPop;
resPop = 0;
// short comPop;
comPop = 0;
// short indPop;
indPop = 0;
// short totalPop;
totalPop = 0;
// short totalPopLast;
totalPopLast = 0;
// short resZonePop;
resZonePop = 0;
// short comZonePop;
comZonePop = 0;
// short indZonePop;
indZonePop = 0;
// short totalZonePop;
totalZonePop = 0;
// short hospitalPop;
hospitalPop = 0;
// short churchPop;
churchPop = 0;
// short faith;
faith = 0;
// short stadiumPop;
stadiumPop = 0;
// short policeStationPop;
policeStationPop = 0;
// short fireStationPop;
fireStationPop = 0;
// short coalPowerPop;
coalPowerPop = 0;
// short nuclearPowerPop;
nuclearPowerPop = 0;
// short seaportPop;
seaportPop = 0;
// short airportPop;
airportPop = 0;
// short needHospital;
needHospital = 0;
// short needChurch;
needChurch = 0;
// short crimeAverage;
crimeAverage = 0;
// short pollutionAverage;
pollutionAverage = 0;
// short landValueAverage;
landValueAverage = 0;
// Quad cityTime;
cityTime = 0;
// Quad cityMonth;
cityMonth = 0;
// Quad cityYear;
cityYear = 0;
// short startingYear;
startingYear = 0;
// short *map[WORLD_W];
memset(map, 0, sizeof(short *) * WORLD_W);
// short resHist10Max;
resHist10Max = 0;
// short resHist120Max;
resHist120Max = 0;
// short comHist10Max;
comHist10Max = 0;
// short comHist120Max;
comHist120Max = 0;
// short indHist10Max;
indHist10Max = 0;
// short indHist120Max;
indHist120Max = 0;
censusChanged = false;
// Quad roadSpend;
roadSpend = 0;
// Quad policeSpend;
policeSpend = 0;
// Quad fireSpend;
fireSpend = 0;
// Quad roadFund;
roadFund = 0;
// Quad policeFund;
policeFund = 0;
// Quad fireFund;
fireFund = 0;
roadEffect = 0;
policeEffect = 0;
fireEffect = 0;
// Quad taxFund;
taxFund = 0;
// short cityTax;
cityTax = 0;
// bool taxFlag;
taxFlag = false;
populationDensityMap.clear();
trafficDensityMap.clear();
pollutionDensityMap.clear();
landValueMap.clear();
crimeRateMap.clear();
powerGridMap.clear();
terrainDensityMap.clear();
rateOfGrowthMap.clear();
fireStationMap.clear();
fireStationEffectMap.clear();
policeStationMap.clear();
policeStationEffectMap.clear();
comRateMap.clear();
// unsigned short *mapBase;
mapBase = NULL;
// short *resHist;
resHist = NULL;
// short *comHist;
comHist = NULL;
// short *indHist;
indHist = NULL;
// short *moneyHist;
moneyHist = NULL;
// short *pollutionHist;
pollutionHist = NULL;
// short *crimeHist;
crimeHist = NULL;
// short *miscHist;
miscHist = NULL;
////////////////////////////////////////////////////////////////////////
// budget.cpp
// float roadPercent;
roadPercent = (float)0.0;
// float policePercent;
policePercent = (float)0.0;
// float firePercent;
firePercent = (float)0.0;
// Quad roadValue;
roadValue = 0;
// Quad policeValue;
policeValue = 0;
// Quad fireValue;
fireValue = 0;
// int mustDrawBudget;
mustDrawBudget = 0;
////////////////////////////////////////////////////////////////////////
// disasters.cpp
// short floodCount;
floodCount = 0;
////////////////////////////////////////////////////////////////////////
// evaluate.cpp
// short cityYes;
cityYes = 0;
// short problemVotes[PROBNUM]; /* these are the votes for each */
memset(problemVotes, 0, sizeof(short) * PROBNUM);
// short problemOrder[CVP_PROBLEM_COMPLAINTS]; /* sorted index to above */
memset(problemOrder, 0, sizeof(short) * CVP_PROBLEM_COMPLAINTS);
// Quad cityPop;
cityPop = 0;
// Quad cityPopDelta;
cityPopDelta = 0;
// Quad cityAssessedValue;
cityAssessedValue = 0;
cityClass = CC_VILLAGE;
// short cityScore;
cityScore = 0;
// short cityScoreDelta;
cityScoreDelta = 0;
// short trafficAverage;
trafficAverage = 0;
////////////////////////////////////////////////////////////////////////
// generate.cpp
// int TreeLevel; /* level for tree creation */
terrainTreeLevel = -1;
// int LakeLevel; /* level for lake creation */
terrainLakeLevel = -1;
// int CurveLevel; /* level for river curviness */
terrainCurveLevel = -1;
// int CreateIsland; /* -1 => 10%, 0 => never, 1 => always */
terrainCreateIsland = -1;
////////////////////////////////////////////////////////////////////////
// graph.cpp
graph10Max = 0;
graph120Max = 0;
////////////////////////////////////////////////////////////////////////
// main.cpp
// int simLoops;
simLoops = 0;
// int simPasses;
simPasses = 0;
// int simPass;
simPass = 0;
simPaused = false; // Simulation is running
// int simPausedSpeed;
simPausedSpeed = 3;
// int heatSteps;
heatSteps = 0;
// int heatFlow;
heatFlow = -7;
// int heatRule;
heatRule = 0;
// int heatWrap;
heatWrap = 3;
// std::string cityFileName;
cityFileName = "";
// std::string cityName;
cityName = "";
// bool tilesAnimated;
tilesAnimated = false;
// bool doAnimaton;
doAnimation = true;
// bool doMessages;
doMessages = true;
// bool doNotices;
doNotices = true;
// short *cellSrc;
cellSrc = NULL;
// short *cellDst;
cellDst = NULL;
////////////////////////////////////////////////////////////////////////
// message.cpp
// Quad cityPopLast;
cityPopLast = 0;
// short categoryLast;
categoryLast = 0;
autoGoto = false;
////////////////////////////////////////////////////////////////////////
// power.cpp
powerStackPointer = 0;
// Position powerStackXY[POWER_STACK_SIZE];
for (int i = 0; i < POWER_STACK_SIZE; i++) {
powerStackXY[i] = Position();
}
////////////////////////////////////////////////////////////////////////
// random.cpp
// UQuad nextRandom;
nextRandom = 1;
////////////////////////////////////////////////////////////////////////
// scan.cpp
// short newMap;
newMap = 0;
// short newMapFlags[MAP_TYPE_COUNT];
memset(newMapFlags, 0, sizeof(short) * MAP_TYPE_COUNT);
// short cityCenterX;
cityCenterX = 0;
// short cityCenterY;
cityCenterY = 0;
// short pollutionMaxX;
pollutionMaxX = 0;
// short pollutionMaxY;
pollutionMaxY = 0;
// short crimeMaxX;
crimeMaxX = 0;
// short crimeMaxY;
crimeMaxY = 0;
// Quad donDither;
donDither = 0;
////////////////////////////////////////////////////////////////////////
// simulate.cpp
valveFlag = false;
// short crimeRamp;
crimeRamp = 0;
// short pollutionRamp;
pollutionRamp = 0;
resCap = false; // Do not block residential growth
comCap = false; // Do not block commercial growth
indCap = false; // Do not block industrial growth
// short cashFlow;
cashFlow = 0;
// float externalMarket;
externalMarket = (float)4.0;
disasterEvent = SC_NONE;
// short disasterWait;
disasterWait = 0;
scoreType = SC_NONE;
// short scoreWait;
scoreWait = 0;
// short poweredZoneCount;
poweredZoneCount = 0;
// short unpoweredZoneCount;
unpoweredZoneCount = 0;
newPower = false;
// short cityTaxAverage;
cityTaxAverage = 0;
// short simCycle;
simCycle = 0;
// short phaseCycle;
phaseCycle = 0;
// short speedCycle;
speedCycle = 0;
// bool doInitialEval
doInitialEval = false;
// int mapSerial;
mapSerial = 1;
// short resValve;
resValve = 0;
// short comValve;
comValve = 0;
// short indValve;
indValve = 0;
////////////////////////////////////////////////////////////////////////
// sprite.cpp
//SimSprite *spriteList;
spriteList = NULL;
// SimSprite *freeSprites;
freeSprites = NULL;
// SimSprite *globalSprites[SPRITE_COUNT];
memset(globalSprites, 0, sizeof(SimSprite *) * SPRITE_COUNT);
// int absDist;
absDist = 0;
// short spriteCycle;
spriteCycle = 0;
////////////////////////////////////////////////////////////////////////
// stubs.cpp
// Quad totalFunds;
totalFunds = 0;
autoBulldoze = true;
autoBudget = true;
gameLevel = LEVEL_EASY;
// short initSimLoad;
initSimLoad = 0;
scenario = SC_NONE;
// short simSpeed;
simSpeed = 0;
// short simSpeedMeta;
simSpeedMeta = 0;
enableSound = false;
enableDisasters = true;
evalChanged = false;
// short blinkFlag;
blinkFlag = 0;
////////////////////////////////////////////////////////////////////////
// traffic.cpp
// short curMapStackPointer;
curMapStackPointer = 0;
// Position curMapStackXY[MAX_TRAFFIC_DISTANCE+1];
for (int i = 0; i < MAX_TRAFFIC_DISTANCE + 1; i++) {
curMapStackXY[i] = Position();
}
// short trafMaxX, trafMaxY;
trafMaxX = 0;
trafMaxY = 0;
////////////////////////////////////////////////////////////////////////
// update.cpp
mustUpdateFunds = false;
mustUpdateOptions = false;
// Quad cityTimeLast;
cityTimeLast = 0;
// Quad cityYearLast;
cityYearLast = 0;
// Quad cityMonthLast;
cityMonthLast = 0;
// Quad totalFundsLast;
totalFundsLast = 0;
// Quad resLast;
resLast = 0;
// Quad comLast;
comLast = 0;
// Quad indLast;
indLast = 0;
simInit();
}
void Micropolis::destroy()
{
destroyMapArrays();
// TODO: Clean up all other stuff:
}
/**
* Check whether \a dir points to a directory.
* If not, report an error.
* @param dir Directory to search.
* @param envVar Environment variable controlling searchpath of the directory.
* @return Directory has been found.
*/
static bool testDirectory(const std::string& dir, const std::string &envVar)
{
struct stat statbuf;
if (stat(dir.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
return true;
}
fprintf(stderr, "Can't find the directory \"%s\"!\n", dir.c_str());
fprintf(stderr,
"The environment variable \"%s\" should name a directory.\n",
envVar.c_str());
return false;
}
/** Initialize for a simulation */
void Micropolis::simInit()
{
setEnableSound(true); // Enable sound
mustUpdateOptions = true; // Update options displayed at user
scenario = SC_NONE;
startingYear = 1900;
simPasses = 1;
simPass = 0;
setAutoGoto(true); // Enable auto-goto
setCityTax(7);
cityTime = 50;
setEnableDisasters(true); // Enable disasters
setAutoBulldoze(true); // Enable auto bulldoze
setAutoBudget(true); // Enable auto-budget
blinkFlag = 1;
simSpeed = 3;
changeEval();
simPaused = false; // Simulation is running
simLoops = 0;
initSimLoad = 2;
initMapArrays();
initGraphs();
initFundingLevel();
resetMapState();
resetEditorState();
clearMap();
initWillStuff();
setFunds(5000);
setGameLevelFunds(LEVEL_EASY);
setSpeed(0);
setPasses(1);
}
/**
* Update ????
* @todo What is the purpose of this function? (also in relation with
* Micropolis::simTick()).
*/
void Micropolis::simUpdate()
{
//printf("simUpdate\n");
blinkFlag = ((tickCount() % 60) < 30) ? 1 : -1;
if (simSpeed && !heatSteps) {
tilesAnimated = false;
}
doUpdateHeads();
graphDoer();
updateBudget();
scoreDoer();
}
/**
* ????
* @todo Why is Micropolis::cellSrc not allocated together with all the other
* variables?
* @todo What is the purpose of this function?
* @todo KILL the define.
*/
void Micropolis::simHeat()
{
int x, y;
static int a = 0;
short *src, *dst;
int fl = heatFlow;
const int SRCCOL = WORLD_H + 2;
const int DSTCOL = WORLD_H;
if (cellSrc == NULL) {
cellSrc = (short *)newPtr((WORLD_W + 2) * (WORLD_H + 2) * sizeof (short));
cellDst = (short *)&map[0][0];
}
src = cellSrc + SRCCOL + 1;
dst = cellDst;
/*
* Copy wrapping edges:
*
* 0 ff f0 f1 ... fe ff f0
*
* 1 0f 00 01 ... 0e 0f 00
* 2 1f 10 11 ... 1e 1f 10
* .. .. .. .. .. ..
* ef e0 e1 ... ee ef e0
* h ff f0 f1 ... fe ff f0
*
* h+1 0f 00 01 ... 0e 0f 00
*
* wrap value: effect:
* 0 no effect
* 1 copy future=>past, no wrap
* 2 no copy, wrap edges
* 3 copy future=>past, wrap edges
* 4 copy future=>past, same edges
*/
switch (heatWrap) {
case 0:
break;
case 1:
for (x = 0; x < WORLD_W; x++) {
memcpy(src, dst, WORLD_H * sizeof (short));
src += SRCCOL;
dst += DSTCOL;
}
break;
case 2:
for (x = 0; x < WORLD_W; x++) {
src[-1] = src[WORLD_H - 1];
src[WORLD_H] = src[0];
src += SRCCOL;
dst += DSTCOL;
}
memcpy(
cellSrc,
cellSrc + (SRCCOL * WORLD_W),
SRCCOL * sizeof (short));
memcpy(
cellSrc + SRCCOL * (WORLD_W + 1),
cellSrc + SRCCOL,
SRCCOL * sizeof (short));
break;
case 3:
for (x = 0; x < WORLD_W; x++) {
memcpy(src, dst, WORLD_H * sizeof (short));
src[-1] = src[WORLD_H - 1];
src[WORLD_H] = src[0];
src += SRCCOL;
dst += DSTCOL;
}
memcpy(
cellSrc,
cellSrc + (SRCCOL * WORLD_W),
SRCCOL * sizeof (short));
memcpy(
cellSrc + SRCCOL * (WORLD_W + 1),
cellSrc + SRCCOL,
SRCCOL * sizeof (short));
break;
case 4:
src[0] =
dst[0];
src[1 + WORLD_H] =
dst[WORLD_H - 1];
src[(1 + WORLD_W) * SRCCOL] =
dst[(WORLD_W - 1) * DSTCOL];
src[((2 + WORLD_W) * SRCCOL) - 1] =
dst[(WORLD_W * WORLD_H) - 1];
for (x = 0; x < WORLD_W; x++) {
memcpy(src, dst, WORLD_H * sizeof (short));
src[-1] = src[0];
src[WORLD_H] = src[WORLD_H - 1];
src += SRCCOL;
dst += DSTCOL;
}
memcpy(
cellSrc + (SRCCOL * (WORLD_W + 1)),
cellSrc + (SRCCOL * WORLD_W),
SRCCOL * sizeof (short));
memcpy(
cellSrc,
cellSrc + SRCCOL,
SRCCOL * sizeof (short));
break;
default:
NOT_REACHED();
break;
}
#define CLIPPER_LOOP_BODY(CODE) \
src = cellSrc; dst = cellDst; \
for (x = 0; x < WORLD_W;) { \
short nw, n, ne, w, c, e, sw, s, se; \
src = cellSrc + (x * SRCCOL); \
dst = cellDst + (x * DSTCOL); \
w = src[0]; c = src[SRCCOL]; e = src[2 * SRCCOL]; \
sw = src[1]; s = src[SRCCOL + 1]; se = src[(2 * SRCCOL) + 1]; \
for (y = 0; y < WORLD_H; y++) { \
nw = w; w = sw; sw = src[2]; \
n = c; c = s; s = src[SRCCOL + 2]; \
ne = e; e = se; se = src[(2 * SRCCOL) + 2]; \
{ CODE } \
src++; dst++; \
} \
x++; \
src = cellSrc + ((x + 1) * SRCCOL) - 3; \
dst = cellDst + ((x + 1) * DSTCOL) - 1; \
nw = src[1]; n = src[SRCCOL + 1]; ne = src[(2 * SRCCOL) + 1]; \
w = src[2]; c = src[SRCCOL + 2]; e = src[(2 * SRCCOL) + 2]; \
for (y = WORLD_H - 1; y >= 0; y--) { \
sw = w; w = nw; nw = src[0]; \
s = c; c = n; n = src[SRCCOL]; \
se = e; e = ne; ne = src[2 * SRCCOL]; \
{ CODE } \
src--; dst--; \
} \
x++; \
}
switch (heatRule) {
case 0:
#define HEAT \
a += nw + n + ne + w + e + sw + s + se + fl; \
dst[0] = ((a >> 3) & LOMASK) | ANIMBIT | BURNBIT | BULLBIT; \
a &= 7;
CLIPPER_LOOP_BODY(HEAT);
break;
case 1:
#define ECOMASK 0x3fc
#define ECO \
{ \
c -= fl; n -= fl; s -= fl; e -= fl; w -= fl; \
ne -= fl; nw -= fl; se -= fl; sw -= fl; \
/* anneal */ \
int sum = \
(c&1) + (n&1) + (s&1) + (e&1) + (w&1) + \
(ne&1) + (nw&1) + (se&1) + (sw&1), cell; \
if (((sum > 5) || (sum == 4))) { \
/* brian's brain */ \
cell = \
((c <<1) & (0x3fc)) | \
(((((c >>1)&3) == 0) && \
(((n&2) + (s&2) + (e&2) + (w&2) + \
(ne&2) + (nw&2) + (se&2) + (sw&2)) == (2 <<1)) \
) ? 2 : 0) | \
1; \
} else { \
/* anti-life */ \
sum = \
((n&2) + (s&2) + (e&2) + (w&2) + \
(ne&2) + (nw&2) + (se&2) + (sw&2)) >>1; \
cell = \
(((c ^ 2) <<1) & ECOMASK) | \
((c&2) \
? ((sum != 5) ? 2 : 0) \
: (((sum != 5) && (sum != 6)) ? 2 : 0)); \
} \
dst[0] = \
((fl + cell) & LOMASK) | ANIMBIT | BURNBIT | BULLBIT; \
c += fl; n += fl; s += fl; e += fl; w += fl; \
ne += fl; nw += fl; se += fl; sw += fl; \
}
CLIPPER_LOOP_BODY(ECO);
break;
default:
NOT_REACHED();
break;
}
}
void Micropolis::simLoop(bool doSim)
{
if (heatSteps) {
int j;
for (j = 0; j < heatSteps; j++) {
simHeat();
}
moveObjects();
simulateRobots();
newMap = 1;
} else {
if (doSim) {
simFrame();
}
moveObjects();
simulateRobots();
}
simLoops++;
}
/**
* Move simulaton forward.
* @todo What is the purpose of this function? (also in relation with
* Micropolis::simUpdate()).
*/
void Micropolis::simTick()
{
if (simSpeed) {
for (simPass = 0; simPass < simPasses; simPass++) {
simLoop(true);
}
}
simUpdate();
}
void Micropolis::simulateRobots()
{
callback->simulateRobots(this, callbackVal);
}
/**
* Deduct \a dollars from the player funds.
* @param dollars Amount of money spent.
*/
void Micropolis::spend(int dollars)
{
setFunds(totalFunds - dollars);
}
/**
* Set player funds to \a dollars.
*
* Modify the player funds, and warn the front-end about the new amount of
* money.
* @param dollars New value for the player funds.
*/
void Micropolis::setFunds(int dollars)
{
totalFunds = dollars;
updateFunds();
}
/**
* Get number of ticks.
* @todo Figure out what a 'tick' is.
* @bug Unix version looks wrong, \c time.tv_usec should be divided to get
* seconds or \c time.tc_sec should be multiplied.
*/
Quad Micropolis::tickCount()
{
struct timeval time;
gettimeofday(&time, 0);
return (Quad)((time.tv_sec * 60) + (time.tv_usec * 60) / 1000000);
}
/**
* Claim \a size bytes of memory.
* @param size Number of bytes to claim.
* @return Pointer to the claimed memory.
*/
Ptr Micropolis::newPtr(int size)
{
return (Ptr)malloc(size);
}
/**
* Release claimed memory.
* @param data Pointer to previously claimed memory.
*/
void Micropolis::freePtr(void *data)
{
free(data);
}
/**
* Tell the front-end a scenario is started.
* @param scenario The scenario being started.
* @see Scenario.
*/
void Micropolis::doStartScenario(int scenario)
{
callback->startScenario(this, callbackVal, scenario);
}
/**
* Tell the front-end a game is started.
*/
void Micropolis::doStartGame()
{
callback->startGame(this, callbackVal);
}
/**
* Initialize the game.
* This is called from the scripting language.
* @todo we seem to have several of these functions.
*/
void Micropolis::initGame()
{
simPaused = false; // Simulation is running.
simPausedSpeed = 0;
simPass = 0;
simPasses = 1;
heatSteps = 0; // Disable cellular automata machine.
setSpeed(0);
}
/**
* Tell the front-end to show an earthquake to the user (shaking the map for
* some time).
*/
void Micropolis::doEarthquake(int strength)
{
makeSound("city", "ExplosionLow"); // Make the sound all over.
callback->startEarthquake(this, callbackVal, strength);
}
/** Tell the front-end that the maps are not valid any more */
void Micropolis::invalidateMaps()
{
mapSerial++;
callback->updateMap(this, callbackVal);
}
/**
* Instruct the front-end to make a sound.
* @param channel Name of the sound channel, which can effect the
* sound (location, volume, spatialization, etc).
* Use "city" for city sounds effects, and "interface"
* for user interface sounds.
* @param sound Name of the sound.
* @param x Tile X position of sound, 0 to WORLD_W, or -1 for everywhere.
* @param y Tile Y position of sound, 0 to WORLD_H, or -1 for everywhere.
*/
void Micropolis::makeSound(const std::string &channel,
const std::string &sound,
int x, int y)
{
if (enableSound) {
callback->makeSound(this, callbackVal, channel, sound, x, y);
}
}
/**
* Get a tile from the map.
* @param x X coordinate of the position to get, 0 to WORLD_W.
* @param y Y coordinate of the position to get, 0 to WORLD_H.
* @return Value of the map at the given position.
* @note Off-map positions are considered to contain #DIRT.
*/
int Micropolis::getTile(int x, int y)
{
if (!testBounds(x, y)) {
return DIRT;
}
return map[x][y];
}
/**
* Set a tile into the map.
* @param x X coordinate of the position to get, 0 to WORLD_W.
* @param y Y coordinate of the position to get, 0 to WORLD_H.
* @param tile the tile value to set.
* @note Off-map positions are ignored.
*/
void Micropolis::setTile(int x, int y, int tile)
{
if (!testBounds(x, y)) {
return;
}
map[x][y] = (unsigned short)tile;
}
/**
* Get the address of the internal buffer containing the map. This is
* used to enable the tile engine to access the tiles directly.
* @return Pointer to the start of the world map buffer.
*/
void *Micropolis::getMapBuffer()
{
return (void *)mapBase;
}
/**
* Get a value from the power grid map.
* @param x X coordinate of the position to get, 0 to WORLD_W.
* @param y Y coordinate of the position to get, 0 to WORLD_H.
* @return Value of the power grid map at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use powerGridMap.worldGet() instead).
*/
int Micropolis::getPowerGrid(int x, int y)
{
return powerGridMap.worldGet(x, y);
}
/**
* Set a value in the power grid map.
* @param x X coordinate of the position to get, 0 to WORLD_W.
* @param y Y coordinate of the position to get, 0 to WORLD_H.
* @param power the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use powerGridMap.worldSet() instead).
*/
void Micropolis::setPowerGrid(int x, int y, int power)
{
powerGridMap.worldSet(x, y, power);
}
/**
* Get the address of the internal buffer containing the power grid
* map. This is used to enable the tile engine to access the power
* grid map directly.
* @return Pointer to the start of the power grid map buffer.
*/
void *Micropolis::getPowerGridMapBuffer()
{
return (void *)powerGridMap.getBase();
}
/**
* Get a value from the population density map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @return Value of the population density map at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use populationDensityMap.worldGet() instead).
*/
int Micropolis::getPopulationDensity(int x, int y)
{
return populationDensityMap.get(x, y);
}
/**
* Set a value in the population density map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @param density the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use populationDensityMap.worldSet() instead).
*/
void Micropolis::setPopulationDensity(int x, int y, int density)
{
populationDensityMap.set(x, y, density);
}
/**
* Get the address of the internal buffer containing the population
* density map. This is used to enable the tile engine to access the
* population density map directly.
* @return Pointer to the start of the population density map buffer.
*/
void *Micropolis::getPopulationDensityMapBuffer()
{
return (void *)populationDensityMap.getBase();
}
/**
* Get a value from the rate of growth map.
* @param x X coordinate of the position to get, 0 to WORLD_W_8.
* @param y Y coordinate of the position to get, 0 to WORLD_H_8.
* @return Value of the rate of growth map at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use rateOfGrowthMap.worldGet() instead).
*/
int Micropolis::getRateOfGrowth(int x, int y)
{
return rateOfGrowthMap.get(x, y);
}
/**
* Set a value in the rate of growth map.
* @param x X coordinate of the position to get, 0 to WORLD_W_8.
* @param y Y coordinate of the position to get, 0 to WORLD_H_8.
* @param rate the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use rateOfGrowthMap.worldSet() instead).
*/
void Micropolis::setRateOfGrowth(int x, int y, int rate)
{
rateOfGrowthMap.set(x, y, rate);
}
/**
* Get the address of the internal buffer containing the rate of
* growth map. This is used to enable the tile engine to access the
* rate of growth map directly.
* @return Pointer to the start of the rate of growth map buffer.
*/
void *Micropolis::getRateOfGrowthMapBuffer()
{
return (void *)rateOfGrowthMap.getBase();
}
/**
* Get a value from the traffic density map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @return Value of the traffic density at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use trafficDensityMap.worldGet() instead).
*/
int Micropolis::getTrafficDensity(int x, int y)
{
return trafficDensityMap.get(x, y);
}
/**
* Set a value in the traffic density map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @param density the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use trafficDensityMap.worldSet() instead).
*/
void Micropolis::setTrafficDensity(int x, int y, int density)
{
trafficDensityMap.set(x, y, density);
}
/**
* Get the address of the internal buffer containing the traffic
* density map. This is used to enable the tile engine to access the
* traffic density map directly.
* @return Pointer to the start of the traffic density map buffer.
*/
void *Micropolis::getTrafficDensityMapBuffer()
{
return (void *)trafficDensityMap.getBase();
}
/**
* Get a value from the pollution density map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @return Value of the rate of pollution density map at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use pollutionDensityMap.worldGet() instead).
*/
int Micropolis::getPollutionDensity(int x, int y)
{
return pollutionDensityMap.get(x, y);
}
/**
* Set a value in the pollition density map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @param density the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use pollutionDensityMap.worldSet() instead).
*/
void Micropolis::setPollutionDensity(int x, int y, int density)
{
pollutionDensityMap.set(x, y, density);
}
/**
* Get the address of the internal buffer containing the pollution
* density map. This is used to enable the tile engine to access the
* pollution density map directly.
* @return Pointer to the start of the pollution density map buffer.
*/
void *Micropolis::getPollutionDensityMapBuffer()
{
return (void *)pollutionDensityMap.getBase();
}
/**
* Get a value from the crime rate map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @return Value of the population density map at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use crimeRateMap.worldGet() instead).
*/
int Micropolis::getCrimeRate(int x, int y)
{
return crimeRateMap.get(x, y);
}
/**
* Set a value in the crime rate map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @param rate the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use crimeRateMap.worldSet() instead).
*/
void Micropolis::setCrimeRate(int x, int y, int rate)
{
crimeRateMap.set(x, y, rate);
}
/**
* Get the address of the internal buffer containing the crime rate
* map. This is used to enable the tile engine to access the crime
* rate map directly.
* @return Pointer to the start of the crime rate map buffer.
*/
void *Micropolis::getCrimeRateMapBuffer()
{
return (void *)crimeRateMap.getBase();
}
/**
* Get a value from the land value map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @return Value of the land value map at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use landValueMap.worldGet() instead).
*/
int Micropolis::getLandValue(int x, int y)
{
return landValueMap.get(x, y);
}
/**
* Set a value in the land value map.
* @param x X coordinate of the position to get, 0 to WORLD_W_2.
* @param y Y coordinate of the position to get, 0 to WORLD_H_2.
* @param value the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use landValueMap.worldSet() instead).
*/
void Micropolis::setLandValue(int x, int y, int value)
{
landValueMap.set(x, y, value);
}
/**
* Get the address of the internal buffer containing the land value
* map. This is used to enable the tile engine to access the land
* value map directly.
* @return Pointer to the start of the land value map buffer.
*/
void *Micropolis::getLandValueMapBuffer()
{
return (void *)landValueMap.getBase();
}
/**
* Get a value from the fire coverage map.
* @param x X coordinate of the position to get, 0 to WORLD_W_8.
* @param y Y coordinate of the position to get, 0 to WORLD_H_8.
* @return Value of the fir coverage map at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use fireStationEffectMap.worldGet() instead).
*/
int Micropolis::getFireCoverage(int x, int y)
{
return fireStationEffectMap.get(x, y);
}
/**
* Set a value in the fire coverage map.
* @param x X coordinate of the position to get, 0 to WORLD_W_8.
* @param y Y coordinate of the position to get, 0 to WORLD_H_8.
* @param coverage the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use fireStationEffectMap.worldSet() instead).
*/
void Micropolis::setFireCoverage(int x, int y, int coverage)
{
fireStationEffectMap.set(x, y, coverage);
}
/**
* Get the address of the internal buffer containing the fire coverage
* map. This is used to enable the tile engine to access the fire
* coverage map directly.
* @return Pointer to the start of the fire coverage map buffer.
*/
void *Micropolis::getFireCoverageMapBuffer()
{
return (void *)fireStationEffectMap.getBase();
}
/**
* Get a value from the police coverage map.
* @param x X coordinate of the position to get, 0 to WORLD_W_8.
* @param y Y coordinate of the position to get, 0 to WORLD_H_8.
* @return Value of the fir coverage map at the given position.
* @note Off-map positions are considered to contain 0.
* @todo Use world coordinates instead (use policeStationEffectMap.worldGet() instead).
*/
int Micropolis::getPoliceCoverage(int x, int y)
{
return policeStationEffectMap.get(x, y);
}
/**
* Set a value in the police coverage map.
* @param x X coordinate of the position to get, 0 to WORLD_W_8.
* @param y Y coordinate of the position to get, 0 to WORLD_H_8.
* @param coverage the value to set.
* @note Off-map positions are ignored.
* @todo Use world coordinates instead (use policeStationEffectMap.worldSet() instead).
*/
void Micropolis::setPoliceCoverage(int x, int y, int coverage)
{
policeStationEffectMap.set(x, y, coverage);
}
/**
* Get the address of the internal buffer containing the police coverage
* map. This is used to enable the tile engine to access the police
* coverage map directly.
* @return Pointer to the start of the police coverage map buffer.
*/
void *Micropolis::getPoliceCoverageMapBuffer()
{
return (void *)policeStationEffectMap.getBase();
}
////////////////////////////////////////////////////////////////////////