/* 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 . * * 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(); } ////////////////////////////////////////////////////////////////////////