FrontendMessages;
+
+/**
+ * Class for storing effects of applying a tool to the world.
+ *
+ * When applying a tool, two things change:
+ * - The world map.
+ * - The funds of the player.
+ * - Messages sent to the player and the front-end.
+ * - Sounds played for the player.
+ *
+ * The funds gives a decision problem. To decide whether the tool can be
+ * applied, you need to know the cost. To know the cost you need to know the
+ * exact changes being made.
+ * The simplest way to compute the exact changes is to simply apply the tool to
+ * the world. This holds especially when tools get stacked on top of each
+ * other.
+ *
+ * This class provides an easy way out, greatly simplifying the problem.
+ * All tools do not modify the world directly, but instead put their results
+ * in an instance of this class, thus collecting all the modifications.
+ * After the whole operation is 'done', the #ToolEffects instance can tell the
+ * precise cost and what has been changed in the world. At that moment, the
+ * yes/no decision can be made, and the effects can be copied to the real map
+ * and funds.
+ *
+ * @todo Extend the class for storing messages and sounds.
+ */
+class ToolEffects
+{
+public:
+ ToolEffects(Micropolis *sim);
+ ~ToolEffects();
+
+ void clear();
+ void modifyWorld();
+ bool modifyIfEnoughFunding();
+
+ MapValue getMapValue(const Position& pos) const;
+ inline MapValue getMapValue(int x, int y) const;
+ inline MapTile getMapTile(const Position& pos) const;
+ inline MapTile getMapTile(int x, int y) const;
+ inline int getCost() const;
+
+ inline void addCost(int amount);
+ void setMapValue(const Position& pos, MapValue mapVal);
+ inline void setMapValue(int x, int y, MapValue mapVal);
+ inline void addFrontendMessage(FrontendMessage *msg);
+
+private:
+ Micropolis *sim; ///< Simulator to get map values from, and to apply changes.
+ int cost; ///< Accumulated costs.
+ WorldModificationsMap modifications; ///< Collected world modifications.
+ FrontendMessages frontendMessages; ///< Collected messages to send.
+};
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Get the tile of a map position.
+ * @param pos Position being queried.
+ * @return Tile at the specified position.
+ *
+ * @pre Position must be within map limits
+ */
+inline MapTile ToolEffects::getMapTile(const Position& pos) const
+{
+ return this->getMapValue(pos) & LOMASK;
+}
+
+
+/**
+ * Get the tile of a map position.
+ * @param x Horizontal coordinate of position being queried.
+ * @param y Vertical coordinate of position being queried.
+ * @return Tile at the specified position.
+ *
+ * @pre Position must be within map limits
+ */
+inline MapValue ToolEffects::getMapTile(int x, int y) const
+{
+ return this->getMapValue(Position(x, y)) & LOMASK;
+}
+
+
+/**
+ * Get the total cost collected so far.
+ * @return Total cost.
+ */
+inline int ToolEffects::getCost() const
+{
+ return this->cost;
+}
+
+
+/**
+ * Add some amount to the total.
+ */
+inline void ToolEffects::addCost(int amount)
+{
+ assert(amount >= 0); // To be on the safe side.
+ this->cost += amount;
+}
+
+
+/**
+ * Get the value of a map position.
+ * @param x Horizontal coordinate of position being queried.
+ * @param y Vertical coordinate of position being queried.
+ * @return Map value at the specified position.
+ *
+ * @pre Position must be within map limits
+ */
+inline MapValue ToolEffects::getMapValue(int x, int y) const
+{
+ return this->getMapValue(Position(x, y));
+}
+
+
+/**
+ * Set a new map value.
+ * @param pos Position to set.
+ * @param x Horizontal coordinate of position to set.
+ * @param y Vertical coordinate of position to set.
+ * @param mapVal Value to set.
+ */
+inline void ToolEffects::setMapValue(int x, int y, MapValue mapVal)
+{
+ this->setMapValue(Position(x, y), mapVal);
+}
+
+
+/**
+ * Add a #FrontendMessage to the queue to send.
+ * @param msg Frontend message to send.
+ */
+inline void ToolEffects::addFrontendMessage(FrontendMessage *msg)
+{
+ this->frontendMessages.push_back(msg);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+/** Properties of a building with respect to its construction. */
+class BuildingProperties
+{
+public:
+ BuildingProperties(int xs, int ys, MapTile base, EditingTool tool,
+ std::string tName, bool anim);
+ ~BuildingProperties();
+
+ const int sizeX; ///< Number of tiles in horizontal direction.
+ const int sizeY; ///< Number of tiles in vertical direction.
+
+ const MapTile baseTile; ///< Tile value at top-left in the map.
+
+ const EditingTool tool; ///< Tool needed for making the building.
+
+ /** Name of the tool needed for making the building. */
+ std::string toolName;
+
+ const bool buildingIsAnimated; ///< Building has animated tiles.
+};
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+#endif
diff --git a/core/traffic.cpp b/core/traffic.cpp
new file mode 100644
index 0000000..dfa260b
--- /dev/null
+++ b/core/traffic.cpp
@@ -0,0 +1,519 @@
+/* traffic.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 traffic.cpp
+ * @brief Implements traffic simulation for the Micropolis game
+ * engine.
+ *
+ * This file handles the generation, management, and effects of
+ * traffic within the Micropolis game. It includes functions for
+ * simulating traffic flow, connecting destinations, and updating
+ * traffic density maps. The code manages various traffic-related
+ * tasks such as finding road connections, driving to destinations,
+ * and handling dead-end situations. Additionally, it updates the
+ * simulation's internal state based on traffic conditions.
+ */
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+#include "micropolis.h"
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Makes traffic starting from the road tile at \x, \y.
+ * @param x Start x position of the attempt
+ * @param y Start y position of the attempt
+ * @param dest Zone type to go to.
+ * @return \c 1 if connection found, \c 0 if not found,
+ * \c -1 if no connection to road found.
+ */
+short Micropolis::makeTrafficAt(int x, int y, ZoneType dest)
+{
+ Position pos;
+ pos.posX = x;
+ pos.posY = y;
+
+ if (tryDrive(pos, dest)) { /* attempt to drive somewhere */
+ addToTrafficDensityMap(); /* if sucessful, inc trafdensity */
+ return 1; /* traffic passed */
+ }
+
+ return 0; /* traffic failed */
+}
+
+
+/**
+ * Find a connection over a road from position \a x \a y to a specified zone type.
+ * @param x Start x position of the attempt
+ * @param y Start y position of the attempt
+ * @param dest Zone type to go to.
+ * @return \c 1 if connection found, \c 0 if not found,
+ * \c -1 if no connection to road found.
+ */
+short Micropolis::makeTraffic(int x, int y, ZoneType dest)
+{
+ Position startPos;
+ startPos.posX = x;
+ startPos.posY = y;
+ return makeTraffic(startPos, dest);
+}
+
+
+/**
+ * Find a connection over a road from \a startPos to a specified zone type.
+ * @param startPos Start position of the attempt.
+ * @param dest Zone type to go to.
+ * @return \c 1 if connection found, \c 0 if not found,
+ * \c -1 if no connection to road found.
+ */
+short Micropolis::makeTraffic(const Position &startPos, ZoneType dest)
+{
+ curMapStackPointer = 0; // Clear position stack
+
+ Position pos(startPos);
+
+#if 0
+ if ((!getRandom(2)) && findPerimeterTelecom(pos)) {
+ /* printf("Telecom!\n"); */
+ return 1;
+ }
+#endif
+
+ if (findPerimeterRoad(&pos)) { /* look for road on zone perimeter */
+
+ if (tryDrive(pos, dest)) { /* attempt to drive somewhere */
+ addToTrafficDensityMap(); /* if sucessful, inc trafdensity */
+ return 1; /* traffic passed */
+ }
+
+ return 0; /* traffic failed */
+ } else {
+ return -1; /* no road found */
+ }
+}
+
+
+/**
+ * Update the #trafficDensityMap from the positions at the
+ * #curMapStackXY stack.
+ */
+void Micropolis::addToTrafficDensityMap()
+{
+ /* For each saved position of the drive */
+ while (curMapStackPointer > 0) {
+
+ Position pos = pullPos();
+ if (pos.testBounds()) {
+
+ MapTile tile = map[pos.posX][pos.posY] & LOMASK;
+
+ if (tile >= ROADBASE && tile < POWERBASE) {
+ SimSprite *sprite;
+
+ // Update traffic density.
+ int traffic = trafficDensityMap.worldGet(pos.posX, pos.posY);
+ traffic += 50;
+ traffic = min(traffic, 240);
+ trafficDensityMap.worldSet(pos.posX, pos.posY, (Byte)traffic);
+
+ // Check for heavy traffic.
+ if (traffic >= 240 && getRandom(5) == 0) {
+
+ trafMaxX = pos.posX;
+ trafMaxY = pos.posY;
+
+ /* Direct helicopter towards heavy traffic */
+ sprite = getSprite(SPRITE_HELICOPTER);
+ if (sprite != NULL && sprite->control == -1) {
+
+ sprite->destX = trafMaxX * 16;
+ sprite->destY = trafMaxY * 16;
+
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Push a position onto the position stack.
+ * @param pos Position to push.
+ * @pre Stack may not be full.
+ */
+void Micropolis::pushPos(const Position &pos)
+{
+ curMapStackPointer++;
+ assert(curMapStackPointer < MAX_TRAFFIC_DISTANCE + 1);
+ curMapStackXY[curMapStackPointer] = pos;
+}
+
+
+/**
+ * Pull top-most position from the position stack.
+ * @return Pulled position.
+ * @pre Stack may not be empty (curMapStackPointer > 0)
+ */
+Position Micropolis::pullPos()
+{
+ assert(curMapStackPointer > 0);
+ curMapStackPointer--;
+ return curMapStackXY[curMapStackPointer + 1];
+}
+
+
+/**
+ * Find a connection to a road at the perimeter.
+ * @param pos Starting position.
+ * Gets updated when a perimeter has been found.
+ * @return Indication that a connection has been found.
+ *
+ * @todo We could randomize the search.
+ */
+bool Micropolis::findPerimeterRoad(Position *pos)
+{
+ /* look for road on edges of zone */
+ static const short PerimX[12] = {-1, 0, 1, 2, 2, 2, 1, 0,-1,-2,-2,-2};
+ static const short PerimY[12] = {-2,-2,-2,-1, 0, 1, 2, 2, 2, 1, 0,-1};
+ short tx, ty;
+
+ for (short z = 0; z < 12; z++) {
+
+ tx = pos->posX + PerimX[z];
+ ty = pos->posY + PerimY[z];
+
+ if (testBounds(tx, ty)) {
+
+ if (roadTest(map[tx][ty])) {
+
+ pos->posX = tx;
+ pos->posY = ty;
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Find a telecom connection at the perimeter.
+ * @param pos Position to start searching.
+ * @return A telecom connection has been found.
+ *
+ * @todo Decide whether we want telecomm code.
+ */
+bool Micropolis::findPerimeterTelecom(const Position &pos)
+{
+ /* look for telecom on edges of zone */
+ static const short PerimX[12] = {-1, 0, 1, 2, 2, 2, 1, 0,-1,-2,-2,-2};
+ static const short PerimY[12] = {-2,-2,-2,-1, 0, 1, 2, 2, 2, 1, 0,-1};
+ short tx, ty, tile;
+
+ for (short z = 0; z < 12; z++) {
+
+ tx = pos.posX + PerimX[z];
+ ty = pos.posX + PerimY[z];
+
+ if (testBounds(tx, ty)) {
+
+ tile = map[tx][ty] & LOMASK;
+ if (tile >= TELEBASE && tile <= TELELAST) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Try to drive to a destination.
+ * @param startPos Starting position.
+ * @param destZone Zonetype to drive to.
+ * @return Was drive succesful?
+ * @post Position stack (curMapStackXY) is filled with some intermediate
+ * positions of the drive.
+ *
+ * @bug The stack is popped, but position (and dirLast) is not updated.
+ */
+bool Micropolis::tryDrive(const Position &startPos, ZoneType destZone)
+{
+ Direction2 dirLast = DIR2_INVALID;
+ Position drivePos(startPos);
+
+ /* Maximum distance to try */
+ for (short dist = 0; dist < MAX_TRAFFIC_DISTANCE; dist++) {
+
+ Direction2 dir = tryGo(drivePos, dirLast);
+ if (dir != DIR2_INVALID) { // we found a road
+ drivePos.move(dir);
+ dirLast = rotate180(dir);
+
+ /* Save pos every other move.
+ * This also relates to
+ * Micropolis::trafficDensityMap::MAP_BLOCKSIZE
+ */
+ if (dist & 1) {
+ pushPos(drivePos);
+ }
+
+ if (driveDone(drivePos, destZone)) { // if destination is reached
+ return true; /* pass */
+ }
+
+ } else {
+
+ if (curMapStackPointer > 0) { /* dead end, backup */
+ curMapStackPointer--;
+ dist += 3;
+ } else {
+ return false; /* give up at start */
+ }
+
+ }
+ }
+
+ return false; /* gone MAX_TRAFFIC_DISTANCE */
+}
+
+
+/**
+ * Try to drive one tile in a random direction.
+ * @param pos Current position.
+ * @param dirLast Forbidden direction for movement (to prevent reversing).
+ * @return Direction of movement, \c #DIR2_INVALID is returned if not moved.
+ */
+Direction2 Micropolis::tryGo(const Position &pos, Direction2 dirLast)
+{
+ Direction2 directions[4];
+
+ // Find connections from current position.
+ Direction2 dir = DIR2_NORTH;
+ int count = 0;
+ for (int i = 0; i < 4; i++) {
+ if (dir != dirLast && roadTest(getTileFromMap(pos, dir, DIRT))) {
+ // found a road in an allowed direction
+ directions[i] = dir;
+ count++;
+ } else {
+ directions[i] = DIR2_INVALID;
+ }
+
+ dir = rotate90(dir);
+ }
+
+ if (count == 0) { // dead end
+ return DIR2_INVALID;
+ }
+
+ // We have at least one way to go.
+
+ if (count == 1) { // only one solution
+ for (int i = 0; i < 4; i++) {
+ if (directions[i] != DIR2_INVALID) {
+ return directions[i];
+ }
+ }
+ }
+
+ // more than one choice, draw a random number.
+ int i = getRandom16() & 3;
+ while (directions[i] == DIR2_INVALID) {
+ i = (i + 1) & 3;
+ }
+ return directions[i];
+}
+
+
+/**
+ * Get neighbouring tile from the map.
+ * @param pos Current position.
+ * @param dir Direction of neighbouring tile, only horizontal and
+ * vertical directions are supported.
+ * @param defaultTile Tile to return if off-map.
+ * @return The tile in the indicated direction. If tile is off-world or an
+ * incorrect direction is given, \c DIRT is returned.
+ */
+MapTile Micropolis::getTileFromMap(const Position &pos,
+ Direction2 dir, MapTile defaultTile)
+{
+ switch (dir) {
+
+ case DIR2_NORTH:
+ if (pos.posY > 0) {
+ return map[pos.posX][pos.posY - 1] & LOMASK;
+ }
+
+ return defaultTile;
+
+ case DIR2_EAST:
+ if (pos.posX < WORLD_W - 1) {
+ return map[pos.posX + 1][pos.posY] & LOMASK;
+ }
+
+ return defaultTile;
+
+ case DIR2_SOUTH:
+ if (pos.posY < WORLD_H - 1) {
+ return map[pos.posX][pos.posY + 1] & LOMASK;
+ }
+
+ return defaultTile;
+
+ case DIR2_WEST:
+ if (pos.posX > 0) {
+ return map[pos.posX - 1][pos.posY] & LOMASK;
+ }
+
+ return defaultTile;
+
+ default:
+ return defaultTile;
+
+ }
+}
+
+
+/**
+ * Has the journey arrived at its destination?
+ * @param pos Current position.
+ * @param destZone Zonetype to drive to.
+ * @return Destination has been reached.
+ */
+bool Micropolis::driveDone(const Position &pos, ZoneType destZone)
+{
+ // FIXME: Use macros to determine the zone type: residential, commercial or industrial.
+ /* commercial, industrial, residential destinations */
+ static const MapTile targetLow[3] = {COMBASE, LHTHR, LHTHR};
+ static const MapTile targetHigh[3] = {NUCLEAR, PORT, COMBASE};
+
+ assert(ZT_NUM_DESTINATIONS == LENGTH_OF(targetLow));
+ assert(ZT_NUM_DESTINATIONS == LENGTH_OF(targetHigh));
+
+ MapTile l = targetLow[destZone]; // Lowest acceptable tile value
+ MapTile h = targetHigh[destZone]; // Highest acceptable tile value
+
+ if (pos.posY > 0) {
+ MapTile z = map[pos.posX][pos.posY - 1] & LOMASK;
+ if (z >= l && z <= h) {
+ return true;
+ }
+ }
+
+ if (pos.posX < (WORLD_W - 1)) {
+ MapTile z = map[pos.posX + 1][pos.posY] & LOMASK;
+ if (z >= l && z <= h) {
+ return true;
+ }
+ }
+
+ if (pos.posY < (WORLD_H - 1)) {
+ MapTile z = map[pos.posX][pos.posY + 1] & LOMASK;
+ if (z >= l && z <= h) {
+ return true;
+ }
+ }
+
+ if (pos.posX > 0) {
+ MapTile z = map[pos.posX - 1][pos.posY] & LOMASK;
+ if (z >= l && z <= h) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Can the given tile be used as road?
+ * @param mv Value from the map.
+ * @return Indication that you can drive on the given tile
+ */
+bool Micropolis::roadTest(MapValue mv)
+{
+ MapTile tile = mv & LOMASK;
+
+ if (tile < ROADBASE || tile > LASTRAIL) {
+ return false;
+ }
+
+ if (tile >= POWERBASE && tile < LASTPOWER) {
+ return false;
+ }
+
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////
diff --git a/core/update.cpp b/core/update.cpp
new file mode 100644
index 0000000..1c737cc
--- /dev/null
+++ b/core/update.cpp
@@ -0,0 +1,281 @@
+/* update.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 update.cpp
+ * @brief Handles updates and refreshes in various aspects of
+ * Micropolis.
+ *
+ * This file contains functions to update and refresh game elements
+ * such as maps, graphs, evaluation metrics, financial status, and
+ * other user interface components. It ensures that the game state is
+ * accurately reflected in the user interface, including changes in
+ * city time, budget, and city demand. The file plays a crucial role
+ * in syncing the game's backend calculations with frontend displays
+ * and interactions.
+ */
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+#include "micropolis.h"
+#include "text.h"
+
+
+/////////////////////////////////////////////////////////////////////////
+
+
+void Micropolis::doUpdateHeads()
+{
+ showValves();
+ doTimeStuff();
+ reallyUpdateFunds();
+ updateOptions();
+}
+
+
+void Micropolis::updateMaps()
+{
+ invalidateMaps();
+ doUpdateHeads();
+}
+
+
+void Micropolis::updateGraphs()
+{
+ changeCensus();
+}
+
+
+void Micropolis::updateEvaluation()
+{
+ changeEval();
+}
+
+
+void Micropolis::updateHeads()
+{
+ mustUpdateFunds = true;
+ valveFlag = true;
+ cityTimeLast = cityYearLast = cityMonthLast = totalFundsLast =
+ resLast = comLast = indLast = -999999;
+ doUpdateHeads();
+}
+
+
+/** Set a flag that the funds display is out of date. */
+void Micropolis::updateFunds()
+{
+ mustUpdateFunds = true;
+}
+
+
+void Micropolis::reallyUpdateFunds()
+{
+ if (!mustUpdateFunds) {
+ return;
+ }
+
+ mustUpdateFunds = false;
+
+ if (totalFunds != totalFundsLast) {
+ totalFundsLast = totalFunds;
+
+ callback->updateFunds(this, callbackVal, totalFunds);
+ }
+
+}
+
+
+void Micropolis::doTimeStuff()
+{
+ updateDate();
+}
+
+
+/**
+ * @bug Message is wrong.
+ */
+void Micropolis::updateDate()
+{
+ int megalinium = 1000000;
+
+ cityTimeLast = cityTime >> 2;
+
+ cityYear = ((int)cityTime / 48) + (int)startingYear;
+ cityMonth = ((int)cityTime % 48) >> 2;
+
+ if (cityYear >= megalinium) {
+ setYear(startingYear);
+ cityYear = startingYear;
+ sendMessage(MESSAGE_NOT_ENOUGH_POWER, NOWHERE, NOWHERE, true);
+
+ }
+
+ if ((cityYearLast != cityYear) ||
+ (cityMonthLast != cityMonth)) {
+
+ cityYearLast = cityYear;
+ cityMonthLast = cityMonth;
+
+ callback->updateDate(this, callbackVal, cityYear, cityMonth);
+ }
+}
+
+
+void Micropolis::showValves()
+{
+ if (valveFlag) {
+ drawValve();
+ valveFlag = false;
+ }
+}
+
+
+void Micropolis::drawValve()
+{
+ float r, c, i;
+
+ r = resValve;
+
+ if (r < -1500) {
+ r = -1500;
+ }
+
+ if (r > 1500) {
+ r = 1500;
+ }
+
+ c = comValve;
+
+ if (c < -1500) {
+ c = -1500;
+ }
+
+ if (c > 1500) {
+ c = 1500;
+ }
+
+ i = indValve;
+
+ if (i < -1500) {
+ i = -1500;
+ }
+
+ if (i > 1500) {
+ i = 1500;
+ }
+
+ if ((r != resLast) ||
+ (c != comLast) ||
+ (i != indLast)) {
+
+ resLast = (int)r;
+ comLast = (int)c;
+ indLast = (int)i;
+
+ setDemand(r, c, i);
+ }
+}
+
+
+void Micropolis::setDemand(float r, float c, float i)
+{
+ callback->updateDemand(this, callbackVal, r, c, i);
+}
+
+
+void Micropolis::updateOptions()
+{
+ if (mustUpdateOptions) {
+ mustUpdateOptions = false;
+ callback->updateOptions(this, callbackVal);
+ }
+}
+
+
+/** @todo Keeping track of pending updates should be moved to the interface
+ * (the simulator generates events, the interface forwards them to
+ * the GUI when possible/allowed.
+ */
+void Micropolis::updateUserInterface()
+{
+ /// @todo Send all pending update messages to the user interface.
+
+ // city: after load file, load scenario, or generate city
+ // map: when small overall map changes
+ // editor: when large close-up map changes
+ // graph: when graph changes
+ // evaluation: when evaluation changes
+ // budget: when budget changes
+ // date: when date changes
+ // funds: when funds change
+ // demand: when demand changes
+ // level: when level changes
+ // speed: when speed changes
+ // delay: when delay changes
+ // option: when options change
+}
+
+
+////////////////////////////////////////////////////////////////////////
diff --git a/core/utilities.cpp b/core/utilities.cpp
new file mode 100644
index 0000000..7715c20
--- /dev/null
+++ b/core/utilities.cpp
@@ -0,0 +1,435 @@
+/* utilities.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 utilities.cpp
+ * @brief Utility functions for the Micropolis game engine.
+ *
+ * This file includes a collection of utility functions used
+ * throughout the Micropolis game engine. These functions perform a
+ * variety of tasks such as manipulating game speed, updating game
+ * levels, handling city names, and managing game settings like
+ * disasters, auto-bulldoze, and sound. It also includes functions for
+ * updating the financial status and formatting numbers into currency
+ * strings. The utilities support the main game engine by providing
+ * essential services for game management and player interaction.
+ */
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+#include "micropolis.h"
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+/* comefrom: drawTaxesCollected incBoxValue decBoxValue drawCurrentFunds
+ drawActualBox updateFunds updateCurrentCost */
+std::string Micropolis::makeDollarDecimalStr(const std::string &numStr)
+{
+ short leftMostSet;
+ short numOfDigits;
+ short numOfChars;
+ short numOfCommas;
+ short dollarIndex = 0;
+ short numIndex = 0;
+ short x;
+
+ numOfDigits = numStr.length();
+
+ std::string dollarStr;
+
+ dollarStr += '$';
+ if (numOfDigits == 1) {
+ dollarStr += numStr[0];
+ return dollarStr;
+ } else if (numOfDigits == 2) {
+ dollarStr += numStr[0];
+ dollarStr += numStr[1];
+ return dollarStr;
+ } else if (numOfDigits == 3) {
+ dollarStr += numStr[0];
+ dollarStr += numStr[1];
+ dollarStr += numStr[2];
+ return dollarStr;
+ } else {
+ leftMostSet = numOfDigits % 3;
+
+ if (leftMostSet == 0) {
+ leftMostSet = 3;
+ }
+
+ numOfCommas = (numOfDigits - 1) / 3;
+
+ /* add 1 for the dollar sign */
+ numOfChars = numOfDigits + numOfCommas + 1;
+
+ for (x = 1; x <= leftMostSet; x++) {
+ dollarStr += numStr[numIndex++];
+ }
+
+ for (x = 1; x <= numOfCommas; x++) {
+ dollarStr += ',';
+ dollarStr += numStr[numIndex++];
+ dollarStr += numStr[numIndex++];
+ dollarStr += numStr[numIndex++];
+ }
+
+ }
+
+ return dollarStr;
+}
+
+/**
+ * Pause a simulation
+ * @see resume
+ */
+void Micropolis::pause()
+{
+ if (!simPaused) {
+ simPausedSpeed = simSpeedMeta;
+ setSpeed(0);
+ simPaused = true;
+ }
+
+ // Call back even if the state did not change.
+ callback->updatePaused(this, callbackVal, simPaused);
+}
+
+/**
+ * Resume simulation after pausing it
+ * @see pause
+ */
+void Micropolis::resume()
+{
+ if (simPaused) {
+ simPaused = false;
+ setSpeed(simPausedSpeed);
+ }
+
+ // Call back even if the state did not change.
+ callback->updatePaused(this, callbackVal, simPaused);
+}
+
+
+void Micropolis::setSpeed(short speed)
+{
+ if (speed < 0) {
+ speed = 0;
+ } else if (speed > 3) {
+ speed = 3;
+ }
+
+ simSpeedMeta = speed;
+
+ if (simPaused) {
+ simPausedSpeed = simSpeedMeta;
+ speed = 0;
+ }
+
+ simSpeed = speed;
+
+ callback->updateSpeed(this, callbackVal, speed);
+}
+
+
+void Micropolis::setPasses(int passes)
+{
+ simPasses = passes;
+ simPass = 0;
+ callback->updatePasses(this, callbackVal, passes);
+}
+
+/**
+ * Set the game level and initial funds.
+ * @param level New game level.
+ */
+void Micropolis::setGameLevelFunds(GameLevel level)
+{
+ switch (level) {
+
+ default:
+ case LEVEL_EASY:
+ setFunds(20000);
+ setGameLevel(LEVEL_EASY);
+ break;
+
+ case LEVEL_MEDIUM:
+ setFunds(10000);
+ setGameLevel(LEVEL_MEDIUM);
+ break;
+
+ case LEVEL_HARD:
+ setFunds(5000);
+ setGameLevel(LEVEL_HARD);
+ break;
+
+ }
+}
+
+
+/** Set/change the game level.
+ * @param level New game level.
+ */
+void Micropolis::setGameLevel(GameLevel level)
+{
+ assert(level >= LEVEL_FIRST && level <= LEVEL_LAST);
+ gameLevel = level;
+ updateGameLevel();
+}
+
+
+/** Report to the front-end that a new game level has been set. */
+void Micropolis::updateGameLevel()
+{
+ callback->updateGameLevel(this, callbackVal, gameLevel);
+}
+
+
+void Micropolis::setCityName(const std::string &name)
+{
+ std::string cleanName;
+
+ int i;
+ int n = name.length();
+ for (i = 0; i < n; i++) {
+ char ch = name[i];
+ if (!isalnum(ch)) {
+ ch = '_';
+ }
+ cleanName.push_back(ch);
+ }
+
+ setCleanCityName(cleanName);
+}
+
+
+/**
+ * Set the name of the city.
+ * @param name New name of the city.
+ */
+void Micropolis::setCleanCityName(const std::string &name)
+{
+ cityName = name;
+
+ callback->updateCityName(this, callbackVal, cityName);
+}
+
+
+void Micropolis::setYear(int year)
+{
+ // Must prevent year from going negative, since it screws up the non-floored modulo arithmetic.
+ if (year < startingYear) {
+ year = startingYear;
+ }
+
+ year = (year - startingYear) - (cityTime / 48);
+ cityTime += year * 48;
+ doTimeStuff();
+}
+
+
+/**
+ * Get the current year.
+ * @return The current game year.
+ */
+int Micropolis::currentYear()
+{
+ return (cityTime / 48) + startingYear;
+}
+
+
+/**
+ * Notify the user interface to start a new game.
+ */
+void Micropolis::doNewGame()
+{
+ callback->newGame(this, callbackVal);
+}
+
+
+/**
+ * set the enableDisasters flag, and set the flag to
+ * update the user interface.
+ * @param value New setting for #enableDisasters
+ */
+void Micropolis::setEnableDisasters(bool value)
+{
+ enableDisasters = value;
+ mustUpdateOptions = true;
+}
+
+
+/**
+ * Set the auto-budget to the given value.
+ * @param value New value for the auto-budget setting.
+ */
+void Micropolis::setAutoBudget(bool value)
+{
+ autoBudget = value;
+ mustUpdateOptions = true;
+}
+
+
+/**
+ * Set the autoBulldoze flag to the given value,
+ * and set the mustUpdateOptions flag to update
+ * the user interface.
+ *
+ * @param value The value to set autoBulldoze to.
+ */
+void Micropolis::setAutoBulldoze(bool value)
+{
+ autoBulldoze = value;
+ mustUpdateOptions = true;
+}
+
+
+/**
+ * Set the autoGoto flag to the given value,
+ * and set the mustUpdateOptions flag to update
+ * the user interface.
+ *
+ * @param value The value to set autoGoto to.
+ */
+void Micropolis::setAutoGoto(bool value)
+{
+ autoGoto = value;
+ mustUpdateOptions = true;
+}
+
+
+/**
+ * Set the enableSound flag to the given value,
+ * and set the mustUpdateOptions flag to update
+ * the user interface.
+ *
+ * @param value The value to set enableSound to.
+ */
+void Micropolis::setEnableSound(bool value)
+{
+ enableSound = value;
+ mustUpdateOptions = true;
+}
+
+
+/**
+ * Set the doAnimation flag to the given value,
+ * and set the mustUpdateOptions flag to update
+ * the user interface.
+ *
+ * @param value The value to set doAnimation to.
+ */
+void Micropolis::setDoAnimation(bool value)
+{
+ doAnimation = value;
+ mustUpdateOptions = true;
+}
+
+
+/**
+ * Set the doMessages flag to the given value,
+ * and set the mustUpdateOptions flag to update
+ * the user interface.
+ *
+ * @param value The value to set doMessages to.
+ */
+void Micropolis::setDoMessages(bool value)
+{
+ doMessages = value;
+ mustUpdateOptions = true;
+}
+
+
+/**
+ * Set the doNotices flag to the given value,
+ * and set the mustUpdateOptions flag to update
+ * the user interface.
+ *
+ * @param value The value to set doNotices to.
+ */
+void Micropolis::setDoNotices(bool value)
+{
+ doNotices = value;
+ mustUpdateOptions = true;
+}
+
+
+/**
+ * Return the residential, commercial and industrial
+ * development demands, as floating point numbers
+ * from -1 (lowest demand) to 1 (highest demand).
+ */
+void Micropolis::getDemands(
+ float *resDemandResult,
+ float *comDemandResult,
+ float *indDemandResult)
+{
+ *resDemandResult = (float)resValve / (float)RES_VALVE_RANGE;
+ *comDemandResult = (float)comValve / (float)COM_VALVE_RANGE;
+ *indDemandResult = (float)indValve / (float)IND_VALVE_RANGE;
+}
+
+
+////////////////////////////////////////////////////////////////////////
diff --git a/core/zone.cpp b/core/zone.cpp
new file mode 100644
index 0000000..291e154
--- /dev/null
+++ b/core/zone.cpp
@@ -0,0 +1,1037 @@
+/* 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 .
+ *
+ * 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;
+}
+
+
+////////////////////////////////////////////////////////////////////////
diff --git a/docs/HAR2009Transcript.html b/docs/HAR2009Transcript.html
new file mode 100644
index 0000000..637bd0b
--- /dev/null
+++ b/docs/HAR2009Transcript.html
@@ -0,0 +1,1374 @@
+
+
+
+ Constructionist Educational Open Source SimCity, by Don Hopkins, at HAR 2009.
+
+
+
+
+
+
+
+
+
+
+
+ HAR 2009 Lightning Talk Transcript: Constructionist Educational Open Source SimCity, by Don Hopkins.
+
+
+
+ Illustrated and edited transcript of the
+ YouTube video playlist: HAR 2009: Lightning talks Friday.
+
+
+
+
+
+
+
+
+
+ Constructionist Educational Open Source SimCity
+ By Don Hopkins,
+ Stupid Fun Club, Amsterdam.
+ Email: don@DonHopkins.com
+ Blog: http://www.DonHopkins.com
+ Demo: http://www.MicropolisOnline.com
+ Project: http://code.google.com/p/micropolis
+ Facebook: http://apps.facebook.com/micropolisonline
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ I'm Don Hopkins, and this is the
+ Micropolis
+ Online project.
+
+
+
+ I'll give you a quick history, and then try to get through the slides
+ to the demo.
+
+
+
+ I started working on
+ SimCity
+ about 1992.
+
+
+
+ When I saw it at
+ college
+ before that, I just fell in love with it, and really wanted to
+ figure out how it worked, and do stuff with the user
+ interface and everything.
+
+
+
+ So when I had the chance to port
+ SimCity to the Sun Workstation,
+ I jumped at that, and made a version of it using the
+ NeWS
+ window system,
+ James Gosling's
+ PostScript based window system.
+
+
+
+ And then I developed an
+ X11 version of
+ SimCity,
+ which was multi player. We sold that as a
+ Unix product. Then the contract expired on that.
+
+
+
+ Later on I went on to
+ Maxis
+ to work on
+ The Sims
+ with
+ Will Wright.
+
+
+
+
+
+ I dredged up this SimCity code, and we talked Electronic Arts into
+ making it open source under the
+ GPLv3
+ a few years ago.
+
+
+
+ The argument -- not the argument: actually they were very pleasant
+ about it -- the thing that convinced them that it was a good idea was
+ the
+ One Laptop Per Child
+ project.
+
+
+
+ The OLPC project is focused on
+ "Constructionist Education",
+ which is about children learning -- anyone learning, by building
+ things.
+
+
+
+ And SimCity is pretty much the
+ quintessential constructionist education game.
+
+
+
+ But it needed to be
+ opened up,
+ so that it can be even better.
+
+
+
+
+
+ This OLPC project is based on
+ Seymour Papert's
+ ideas about
+ teaching children to program,
+ even when they're very young, and
+ Alan Kay's
+ ideas about the
+ Dynabook
+ and
+ object oriented programming,
+ and
+ how kids learn.
+
+
+
+ One of the important things about constructionism is kids helping
+ each other, communicating, and learning from each other.
+
+
+
+ I adapted the old X11/Unix version of SimCity to the
+ OLPC XO-I Children's Laptop.
+ The original X11 version supported multi player mode,
+ but I disabled that feature to simplify the game, because it
+ didn't scale well, was complex to configure, and not well
+ designed. OLPC games for kids need to be very easy to use.
+
+
+
+ What we really needed to do was to make a new version that had a
+ much better user interface designed to take advantage of the
+ OLPC collaboration features and social networking platforms like
+ Facebook. It needs to have meaningful, scalable, easy to use
+ multi player interaction.
+
+
+
+ Fortunately, the OLPC project chose to use
+ Python
+ as the official scripting language. Python is a
+ powerful, easy to learn language,
+ that is
+ widely used in education.
+ It's also very useful as a web programming language, for implementing a multi player game server.
+
+
+
+ But let me go on:
+
+
+
+ Micropolis Project Goals
+
+
+
+
+ -
+ Develop a fun web based educational game, based on the original
+ SimCity source code.
+
+ -
+ Focused on constructionist education.
+
+ -
+ Built out of free open source software.
+
+ -
+ Runs on the server, plays in the browser.
+
+ -
+ Supports collaborative, democratic, multi-user play.
+
+
+
+
+
+
+
+
+
+ The goals: We want to make a fun game that's based on the original
+ source code.
+
+
+
+ "Constructionist Education" is a the overall theme, but it's not
+ obvious how that actually manifests itself in a game, and it
+ takes a lot of experimentation, trial and error to figure out.
+
+
+
+ We're using free open source software, distributing it over the
+ web, and running it on centralized servers. So it's easy to make
+ updates to the game as the design evolves.
+
+
+
+ We want to put in features to support collaboration, voting,
+ teach democracy, and also teach language skills and
+ interpersonal communication skills.
+
+
+
+ Accomplishments So Far
+
+
+
+
+ -
+ Ported and shipped the TC L/Tk/X11 version of Micropolis
+ on the One Laptop Per Child XO-I.
+
+ -
+ Developed a cross platform, desktop version of Micropolis, based
+ on GTK/Python.
+
+ -
+ Developed an open source, internationalized, web based version
+ of Micropolis.
+
+ -
+ Client based on OpenLaszlo/Flash.
+
+ -
+ Server based on TurboGears/Python/C++.
+
+
+
+ -
+ Renovated, re-factored and documented the code.
+
+ -
+ Established a solid groundwork and open framework for developing
+ an educational, collaborative, multi-player game.
+
+
+
+
+
+
+
+ So far what's been done: First, we took the old
+ TCL/Tk/X11
+ version, cleaned it up, got it through EA's Quality Assurance,
+ and put it on the One Laptop Per Child.
+
+
+
+ Then we stripped down the code, translated it into C++, then
+ cleaned it up, refactored it, and took out all the user
+ interface. Then we started building a new user interface on top
+ of that.
+
+
+
+ I had some really great help from some talented people.
+ John Gilmore
+ supported me for many years during the development of SimCity,
+ and shepherded it through the complex legal process of
+ relicensing it under GPLv3.
+
+
+
+ Albert Hofkamp
+ is a software developer at the Eindhoven University of
+ Technology. He helped me clean up this code, and put Doxygen
+ documentation into it. Now the code is much sturdier and easier
+ to do something with.
+
+
+
+ Tom Tjon A Loi
+ is a an artist who's also helping on the project. I'll
+ show you some ideas that we've put together about what direction it's
+ going to go in the future.
+
+
+
+ We're also benefiting from
+ Kennisnet
+ and
+ IBM Netherlands,
+ who are helping us make an educational version of this game.
+
+
+
+ The OLPC has been something for people to focus on, and get a
+ bunch of people on board to make this happen. But we want
+ Micropolis to run on as many different platforms as possible.
+
+
+
+ What's really wonderful is that I have had some really great
+ conversations about this and encouragement from Alan Kay, who has been
+ trying to do this stuff for years with kids.
+
+
+
+ Alan Kay's criticism of SimCity
+ is that you can't see inside of it and understand what it's
+ doing, and you have to accept it as a black box. So now I'm
+ trying to solve that problem.
+
+
+
+ Open Source Software
+
+
+
+
+ -
+ Micropolis is built with a carefully chosen, well proven, widely
+ supported set of open source, cross platform tools and libraries.
+
+ -
+ Micropolis itself and its TileEngine module are open source
+ (GPL3). The client runs on the proprietary Adobe Flash
+ player. With a little more work, The client will also run in
+ standard browsers, thanks to OpenLaszlo.
+
+ -
+ Linux. Apache. MySQL. Python. Cairo. Pango. GTK. GCC. SWIG.
+ PyCairo. PyGTK. TurboGears. Genshi. SQLAlchemy. CherryPy.
+ PyAMF. AMFast. Pie menus. OpenLaszlo. Red5. Java. Tomcat.
+ MediaWiki. PHP. Subversion. Doxygen.
+
+
+
+
+
+
+
+ We're using a whole bunch of
+ Free and open source software,
+ building on top of it, and then making new software that's open
+ source.
+
+
+
+ One of the things that the OLPC project wants
+ game developers
+ to do is to make games out of modular components that can be
+ plugged together to make other games.
+
+
+
+ So I've been separating out the
+ tile engine
+ of SimCity, and the sprites, and the sound, and trying to make
+ those work for other things.
+
+
+
+ Web Server and Client
+
+
+
+
+ -
+ Server: Linux, Apache, MySQL, Python, TurboGears, SWIG, C++
+
+ -
+ Portable, cross platform C++ and Python code.
+
+ -
+ Linux and Unix
+
+ -
+ Mac OS/X
+
+ -
+ Windows (not currently maintained but viable)
+
+ -
+ Deploys on standard Fedora Linux server, i.e. Amazon Elastic
+ Computing Server, etc.
+
+
+
+ -
+ Client: OpenLaszlo, XML, JavaScript, AMF
+
+ -
+ Web based client runs in Adobe Flash player, widely supported on
+ most web browsers and operating systems.
+
+
+
+
+
+
+
+
+
+ The architecture of the web version is that there's a C++ core
+ MicropolisEngine, which is plugged into the Python programming
+ language as an extension module. Then Python is used to script
+ the higher level parts of the game, graphics and web server.
+ The user interface is implemented in the client, which is
+ written in OpenLaszlo and runs on Flash in the web browser.
+
+
+
+ I'm using
+ SWIG,
+ a tool for making wrappers for libraries, and plugging
+ them into extension language. And it lets you get your library to a
+ whole bunch of different extension languages. So right now it's
+ Python, but it could be Lua or Java or whatever.
+
+
+
+ The SWIG wrapper layer around the C++ game engine is glue that
+ binds the game's C++ classes into Python. So C++ can call
+ Python, and Python can call C++, and they can access and modify
+ each other's data structures. One nice thing about SWIG is that
+ you can subclass your C++ classes in Python, and then just
+ script everything in a nice language.
+
+
+
+ It's all open source stuff.
+
+
+
+ The web server does HTTP and it does
+ Adobe AMF,
+ which is kind of like binary JSON for Flash.
+
+
+
+ Now I'm doing the client in another open source scripting language called
+ OpenLaszlo.
+ It's like JavaScript and XML that gets compiled into Flash.
+ But it will also compile into generic DHTML.
+
+
+
+
+
+ Right now I'm concentrating on just getting the Flash interface
+ working, and once the paint's dry on that, I can then do the
+ parts that were Flash specific using the Canvas and DHTML and
+ nice open standards like that.
+
+
+
+ The intention is to get it independent of Flash, but Flash is the
+ fastest way to get it working first.
+
+
+
+ The other thing I have is a Python scripted GTK based user
+ interface, for the desktop.
+
+
+
+ Internationalization
+
+
+
+
+ -
+ All text comes from Unicode XML localization files, so the
+ text can be translated to support different languages and
+ countries.
+
+ -
+ Currently supports English and Dutch, but other languages are
+ easy to add, given translation resources.
+
+ -
+ Web based tools for conveniently reviewing and editing
+ translations.
+
+ -
+ This is a very important feature, since we're initially
+ delivering to the Dutch educational system, and we intend
+ to support many other languages in the future.
+
+
+
+
+
+ Another important thing is internationalizing it and taking all the
+ strings out. I would love to have volunteers to help translate it to
+ other languages. I made a little web based tool to do that. We're
+ doing this for the Dutch educational system, with Kennisnet's support.
+
+
+
+ Multi Player Collaboration
+
+
+
+
+ -
+ Designed, developed and shipped a multi-player X11 version of
+ SimCity in 1993 for DUX Software, demonstrated in Amsterdam
+ at INTERCHI '93.
+
+ -
+ Micropolis has been systematically redesigned with multi-player
+ collaboration and constructionist education in mind.
+
+ -
+ Currently laying the groundwork for multi player and online
+ community support, while getting the single player game up and
+ running first.
+
+ -
+ User accounts. Saved cities. Shared cities. Chat. Robots and
+ Avatars. Basic MediaWiki integration. Client/server session,
+ messaging and simulation engine plumbing.
+
+
+
+
+
+
+
+
+
+
+
+ Previously I made a
+ multi player version of this for
+ X11.
+ But X11 doesn't scale very well, and it was kind of an experiment.
+
+
+
+ But it did test out some ideas of making it a collaborative,
+ cooperative game, instead of a competitive game.
+
+
+
+ I didn't want to mess up the game, but I wanted to layer another
+ social game on top of it.
+
+
+
+ We're all sharing the same money, we're trying to achieve the same
+ goals, so if somebody does something that pisses you off, you're going
+ to be mad at them, and won't play with them. That will play out at a
+ higher level than the game.
+
+
+
+ It's got voting for expensive things, like making a nuclear power
+ plant, or changing the taxes. And the non-expensive things are just a
+ free-for-all.
+
+
+
+ I want to re-visit the multi player game, but first get a really
+ solid base to build it on top of. Doing a web based game is the
+ best way to do that.
+
+
+
+ Constructionist Education
+
+
+
+
+ -
+ The original SimCity game was an exemplary constructionist
+ educational game, which has been successfully used in many
+ classrooms, but it was a closed commercial product, not
+ specifically designed to support education.
+
+ -
+ Since Micropolis is open source SimCity, we can open up,
+ renovate, visualize, document and extend it in wonderful ways,
+ to specifically support constructionist education, social
+ networking and collaborative democratic play.
+
+ -
+ "One of the most wonderful possibilities about this venture is
+ that it will bring together very fluent designers from many
+ worlds of computing (more worlds than usually combine to make a
+ game) in the service of the children. We should really try to
+ pull this off!" -Alan Kay
+
+
+
+
+
+
+
+ I'm adding stuff, like integrating it with
+ MediaWiki.
+
+
+
+ One of the important things about Constructionist Education is getting
+ people to help each other out, and chat.
+
+
+
+ I want to teach language skills with this. Not just how to be a good
+ mayor. That's not the goal. It's how to write about what you want to
+ do, and convince other people to vote for it.
+
+
+
+ I think it's going to be nice to embed this in things like
+ Facebook
+ and
+ MediaWiki,
+ because what you're doing is writing stories and
+ newspapers, and blogging.
+
+
+
+
+
+ The Sims 1 (and later versions) had something called
+ The Sims Exchange,
+ where when you save your game file, it also writes out a bunch
+ of web files, that describe your house, your family, the story
+ you've told about them with pictures. And then it uploads that
+ to the web.
+
+
+
+ So really, the game is a web publishing tool. I want to apply
+ those ideas to SimCity, and that would be in the form of a
+ newspaper. So you could act as a journalist when something
+ happened.
+
+
+
+ These events in the game are generating opportunities for you to click
+ on it, open it up, and write about what your interpretation of it
+ is. And then automatically push it into a wiki page or a blog.
+
+
+
+ So the result of playing a game will be this rich save file that has
+ points of interest with stories attached to them. You could look at it
+ geographically as a map, to see all the points of interest. You could
+ look at it as a calendar, like a blog. And then all these things that
+ happened with the story can link to a save file, so you can then jump
+ back into the game at that point.
+
+
+
+
+
+ The eventual goal is to make it sort of like TiVo for
+ SimCity. You have all these save file snapshots, and then you
+ can fast forward and reverse by replaying the edits that you
+ did.
+
+
+
+ And you will have branching. You can imagine that when you play
+ game, at any point, if you decide "let's raise taxes", you could
+ make a checkpoint there. And then you could revisit that
+ decision, and then branch reality out to another thing, if you
+ didn't raise taxes.
+
+
+
+ I want players to be able to make a tree of possibilities, and then
+ share all these save files, and then comment on them, and write about
+ them.
+
+
+
+ That's where the educational stuff comes in. The teacher should be
+ able to write scenarios that plug into the game, that generate events,
+ and give people things to discuss.
+
+
+
+
+
+ Alan Kay said some really wonderful things about the project to
+ bring open source SimCity to the OLPC:
+
+
+
+
+ "One of the most wonderful possibilities about this venture is that
+ it will bring together very fluent designers from many worlds of
+ computing (more worlds than usually combine to make a game) in the
+ service of the children. We should really try to pull this off!"
+ -Alan Kay
+
+
+
+
+ I'm looking for volunteers. I never thought I'd be working directly or
+ indirectly with Alan Kay, so it just blew my mind that he was
+ interested in it. And he's very supportive.
+
+
+
+ Here's the long term goal: There's a game, and there's a robot or a
+ monster. You double click on a monster, and it should open up a window
+ that has a visual program that controls how it behaves.
+
+
+
+ This has already been done in the 80's on the Apple ][, and it was a
+ game called
+ Robot Odyssey,
+ by
+ Warren Robinett,
+ who made
+ Atari Adventure.
+ And it was so before its time. It turns out that it is one of
+ Alan Kay's favorite games,
+ and that he's
+ waiting for the other shoe to drop.
+
+
+
+ Robot Odyssey taught
+ visual programming
+ and logic to kids. You go inside a robot, and it's a room, with
+ plugs and sensors, and you have logic parts that you can bring
+ out, and make a circuit inside that robot.
+
+
+
+ What I'm trying to do -- oh I'd better get to the demo -- is do this
+ within SimCity. And with a visual programming language that's just a
+ layer on top of
+ Python.
+ We already have Python. I like Python. It's well designed. I'm
+ not going to try to invent something different, I just want to
+ give you a visual way to get to everything Python can do, which
+ is anything.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Educational Applications
+
+
+
+
+ -
+ Encourage creative writing by blogging, twittering, chatting,
+ debating, writing and publishing newspaper articles about
+ events and issues in the city.
+
+ -
+ Teach democratic processes. Writing proposals. Campaigning for
+ support. Discussing and debating issues. Voting on bills.
+
+ -
+ Publish wiki pages with interactive annotated maps, timelines of
+ events and creative writing about the cities.
+
+ -
+ Open up the simulator to enable plug-in programmable zones,
+ tools, robots, avatars, events, policies, courseware, etc.
+
+ -
+ Teach older kids to program Python and C++. Implement a visual
+ behavior programming language (like Robot Odyssey and
+ SimAntics). Teach younger kids to think logically and program
+ robots (like Logo turtles and Lego Robotics).
+
+
+
+
+
+
+
+ How does Constructionist Education manifest itself in something
+ like SimCity?
+
+
+
+ Creative writing, blogging, twittering and chatting: and all this
+ social stuff that people do.
+
+
+
+ Democracy. Writing proposals. Trying to get somebody to support your
+ proposal. Publishing things into wikis.
+
+
+
+ Opening it up to enable plugin programming. The first step is to look
+ at the code to see how it works. And then tweak it and change it, and
+ then make your whole new thing.
+
+
+
+ The teachers should be able to write courseware, scenarios,
+ teaching events and educational content that plugs into the game,
+ without any programming.
+
+
+
+ The older kids and adults can use existing examples to learn
+ Python and C++ programming.
+
+
+
+ And younger kids plus everyone else will have an easier, higher
+ level visual programming language in which to discover and
+ customize the system.
+
+
+
+ That is version three. I'm only at version one right now.
+
+
+
+ Next Steps
+
+
+
+
+ -
+ Shared city library. Ratings. Tagging. Journals. Discussions.
+ Linking. History browsing. Sharing. Voting. Profiles. Friends.
+
+ -
+ Text, voice and video chat. In-game chat with avatars on the
+ map. Integrated video player for help, tutorials and
+ lessons. Integrated html frame for synchronized web browsing.
+
+ -
+ MediaWiki integration, to support creative writing by students
+ and courseware development by teachers.
+
+ -
+ PayPal integrations. Donations, subscriptions and
+ micro-payments, for virtual money, cheats, high speed
+ simulation, extra storage, fund raising, etc.
+
+
+
+
+
+
+
+ I have the web server and browser client running. I want to put
+ in sharing cities, tagging, journals, discussions -- you know,
+ the whole social net thing. It occurs to me that maybe I should
+ just let Facebook handle a lot of that stuff. I'll just make a
+ Facebook
+ application.
+
+
+
+ Slow the game down a lot, so your Facebook wall will let your
+ friends vote on things. I'm getting ahead of myself.
+
+
+
+ MediaWiki
+ integration. Take well known things that solve a problem
+ well, and piggy back on top of them, instead of trying to invent your
+ own things.
+
+
+
+ PayPay, so we can pay for all the CPU time it takes.
+
+
+
+ Social Networking
+
+
+
+ Tom Tjon A Loi made these mock-ups of what SimCity might look like inside a
+ social network like Facebook.
+
+
+
+ Identity
+
+
+
+
+
+
+ The important thing is that you have to have an identity. It knows
+ about you. You're part of a community of real people.
+
+
+
+ Voting
+
+
+
+
+
+
+ You've got friends, and this could be a Facebook app. If you add
+ the app, you can do all this stuff and play it yourself. But
+ even if you don't add the app, you can still see and interact
+ with what other people are doing.
+
+
+
+ Citizen
+
+
+
+
+
+
+ It's all about showing other people what you're doing,
+ explaining it to them, discussing the issues, and getting their
+ feedback.
+
+
+
+ Journal
+
+
+
+
+
+
+ You can have these links into the save files, and people saying
+ things. I want to make it so one game might last a week, and take
+ many little steps.
+
+
+
+ Community
+
+
+
+
+
+
+ What I have already made to prove the concept of plugins, is plugin
+ zones. The first plugin zone I made was a plugin church, because
+ that's controversial and will upset people.
+
+
+
+ The idea is that I want to be able to script churches and other
+ zones in Python. And create new zones and content for them
+ without even programming. The other plugin is a plugin
+ character, or agent. Like the monster or helicopter, with
+ programmable behavior.
+
+
+
+ Chat
+
+
+
+
+
+
+ In a multiplayer game, there could be several people in the same
+ game, and you're all on the same map in one place, and you can
+ chat embedded in the game, with collaborative editing.
+
+
+
+ If you put something down, it would open up an interface that
+ says: "Hey, Don wants to make this. Do you vote for it? Yes or
+ no." And then have a chat associated with that, where you can
+ discuss the issue.
+
+
+
+ Squatters
+
+
+
+
+
+
+ And then imagine
+ other people could come into you game,
+ and they find: "Hey this zone is not developed. We're going to
+ put a coffeeshop here!"
+
+
+
+ I want it eventually to be open enough that other people can come in
+ and effect your game. And of course you always have a save file.
+
+
+
+ Micropolis Demo
+
+
+
+ There's a tool for doing translations.
+
+
+
+
+
+
+ The source code, designs and documentation are all in
+ Google Code.
+
+
+
+
+
+
+ Ah, that's the robot! I wanted to
+ show some code.
+ This is how PacMan works. He follows the road.
+
+
+
+ So I wrote a plugin robot that you can put into the city.
+
+
+
+
+
+
+ I'd better get to the actual code that's running.
+
+
+
+
+
+
+ These are the different scenarios, we could load, or generate
+ something randomly. Here, we'll do one of my favorite cities here. I
+ made this city.
+
+
+
+
+
+
+ This is a neat kind of a
+ lazy tile view.
+ There are 120 by 100 tiles on the server, and we're telling the
+ server where we're looking at. And it's sending just the tiles
+ that we need to see.
+
+
+
+
+
+
+ Things that happen in the city get logged, and there's a chat.
+
+
+
+ This will help you figure out what you need to do to improve you
+ city.
+
+
+
+ This is a history.
+
+
+
+
+
+
+ Here's the nice one here: we can raise taxes a lot. It was so funny
+ because the taxes in the Netherlands are so much more than 20%.
+
+
+
+
+
+
+ We can look at the population density map overlay.
+
+
+
+
+
+
+ Ok, now here's a meltdown. And when you get a meltdown, the notice
+ window says "Hey there's a meltdown!", and has a live view showing it,
+ that you can click on to go there.
+
+
+
+
+
+
+ Now this little window up here, when something interesting happens in
+ the game, will show you a live view, and tell you something. But we
+ can also have a link to Wikipedia or whatever.
+
+
+
+ I want to put in more educational stuff. Like the church says "Hey,
+ did you know that Scientologists believe this?" and "Click here for
+ more!". We can think of the churches as in-game advertising.
+
+
+
+ And then if you have too many churches, you use the bulldozer to get
+ rid of them.
+
+
+
+ There's a speed control. I can make it go super-duper fast. And
+ everybody just left. This is more like a twitch game.
+
+
+
+ Let me do the cheat. Ok, what is it? I just added this, so I'm
+ not quite sure. You go "!faith 1000". That makes them want a lot
+ more churches. Woah! Ok, so I'm getting many churches. There
+ goes the tax base.
+
+
+
+ I've programmed a plug-in church with a plug-in agent: the
+ Church of Pacmania, and the PacBot. The church spawn PacBots on
+ the road next to it, which eat the traffic on the roads
+ (lowering the pollution and raising their score, of course). So
+ naturally the Church of Pacmania generate a lot of traffic, to
+ attract and feed the PacBots. I'm going to have a "Tax the
+ Churches" option, too. If I lower the taxes, then more people
+ will come in, and the churches will recruit them, and they'll
+ all drive to church. So then there will be a lot more traffic
+ for the PacBots to eat.
+
+
+
+ Oh, I ran out of money. Let me do the robots real quick. So this is
+ PacBot. You can put him down. Oh, I'm out of money! The fastest way is
+ to just refresh the screen, and start fresh.
+
+
+
+ We'll go straight in, we'll get rid of this. Oh, pie menus, right! If
+ you click... (Dutch "Taartmenu" cursor pops up!) I've got to have a
+ talk with my translator.
+
+
+
+
+
+
+ You click, and you get a pie menu, which has items around the cursor
+ in different directions. So if you click and go right, you get a road.
+ And then you can do a little road. And if you click and go up and
+ right, you get a bulldozer.
+
+
+
+
+
+
+ And then there are submenus for zoning parks, and stuff like
+ that. This gives you a nice quick gesture interface.
+
+
+
+
+
+
+ The one I wanted was PacBot. So if you click him, that's running
+ Python code that is looking at the map, and is looks for the traffic,
+ and it goes up, up, up, up, up. See?
+
+
+
+
+
+
+ Wait, you haven't -- hang on, wait: you can have lots of them. And
+ then they compete. You get this wonderful emergent behavior of PacBot
+ looking for traffic, and then the other PacBot eats it. You really
+ need to zoom this up a little bit.
+
+
+
+
+
+
+ The great thing about this: this is another game layered on top. You
+ can make your PacBot maze, and then you can torture them. They'll go
+ crazy, it's like a lion in his cage here. Ah, caught one! Ah! He's
+ blue. Let's get him here. He's not going to be happy about
+ this. Where'd he go? Oh oh, there's a menu to get rid of him, so I
+ think I deleted him. Here, we'll put one there. He's just going nuts
+ there.
+
+
+
+
+
+
+ Wait, where were we? Real quick. Where's my iPhone? Did I leave the
+ phone down here? You can't see it so you'll have to believe me.
+
+
+
+ The first multi-player interface I'm making is an iPhone app, so it
+ loads the city, and it lets you zoom and run over it. And I'm just
+ about to hook it up to the server, so you can come into the game as a
+ helicopter, and just tilt to run around, and then just chat.
+
+
+
+ Eventually I want to do the whole game on the iPhone, Andriod and
+ whatever else. The first step is to just get presence and chatting in
+ there.
+
+
+
+ One interesting thing: Micropolis is
+ GPLv3.
+ So you can't put it on the iPhone, because of the
+ anti-TiVoization
+ clause, which prohibits using hardware restrictions to prevent users from running modified versions of the software
+ on that hardware.
+
+
+
+ But it's possible to have the simulation running on the server
+ and a client displaying and interacting on the iPhone. I think
+ it's more interesting to have the simulation running on the
+ server, in order to enable social interactions; and anyway
+ there's already an authentic single player SimCity that runs on
+ the iPhone.
+
+
+
+
+
+ Any questions?
+
+
+
+ Q: What's the cheat code to get money?
+
+
+
+ Mike: War!
+
+
+
+ Don: That's what churches are for.
+
+
+
+ I encourage you to script your own church and plug it in. I'm totally
+ going to have the
+ Flying Spaghetti Monster,
+ and he's going to actually convert all the other churches to
+ Flying Spaghetti Monster churches, so it will be a moot point,
+ and there will be no war.
+
+
+
+ Announcer: Please thank Mr. Hopkins for his nice solution to the
+ traffic problem.
+
+
+
+ Micropolis on Facebook
+
+
+
+ You can now play
+ Micropolis on Facebook!
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/HackingAtRandomTalk.txt b/docs/HackingAtRandomTalk.txt
new file mode 100644
index 0000000..6103b34
--- /dev/null
+++ b/docs/HackingAtRandomTalk.txt
@@ -0,0 +1,93 @@
+Micropolis, the open source version of SimCity, is a fun, engaging
+game, that helps teach kids about science, language, mathematics, art
+and politics. Its goal is to fulfill SimCity's potential as a
+microworld for children's learning and exploration.
+
+A web based version of Micropolis is the best way to quickly reach the
+largest audience, and an important step towards the long term goal of
+developing an open source, collaborative multi player, educationally
+oriented simulation gaming platform.
+
+This project is about creating educational open source software. It's
+the culmination of years of research and development, that is now
+possible thanks to Electronic Arts making SimCity open source.
+
+Will Wright wrote the original SimCity city simulation game, first
+released in 1989. Since 1992, Don Hopkins ported SimCity to various
+platforms, redesigned the user interface, added multi player support,
+cleaned and refactored the code, and integrated it with scripting
+languages and web servers.
+
+On January 10, 2008 the SimCity source code was released under the
+free software GPL 3 license, under the name "Micropolis". Now that
+it's GPL, it can be adapted to many platforms, including Linux, Mac,
+and Windows guis, as well as web servers, cell phones and embedded
+devices! And it can be improved and extended to make it a better
+educational tool and open source programming example.
+
+The Micropolis project is building an exemplary open source game out
+of modular reusable components, that other people can learn from,
+build on top of, integrate with other languages and user interfaces,
+and use as a starting point for their own projects.
+
+The plan to develop Micropolis into an educational gaming platform
+draws heavily on the vision and experience of educators, researchers
+and designers including Seymour Papert, Hal Abelson, Alan Kay, Will
+Wright, Ben Shneiderman, and Mark Weiser.
+
+Accomplishments of the Micropolis project so far:
+
+Translated the original C SimCity code to C++.
+
+Cleaned up all the code, organized into types and classes, refactored
+and rewrote old crappy code, renamed variables and functions, measured
+performance and optimized bottlenecks, applied a consistent
+programming style, and heavily commented the code, wrote lots of
+documentation and designs.
+
+Used doxygen to generate extensive online documentation from formatted
+commments in the code, with an html reference manual, member and
+parameter descriptions, usage cross references, hyperlinked listings,
+bug and todo lists, etc.
+
+Removed all of the user interface code from the core simulation engine
+(called MicropolisCore), and added programming interfaces to
+efficiently access the internal data and control the simulation.
+
+Implemented a general purpose TileEngine module, used by but
+independent of Micropolis. Supports various memory formats, efficient
+rendering techniques, graphics sets, tile mapping, lazy procedural
+tile rendering, scaling and panning, caching, and tile animation
+compressed network protocols. The CellEngine cellular automata machine
+module also uses the TileEngine.
+
+Integrated MicropolisCore, TileEngine and CellEngine with Python by
+using SWIG, a scripting language interface wrapper generator. SWIG
+makes it easy to develop and change the programming interface (C++
+classes, etc), and automatically generate all the glue code that makes
+it possible to access and control the C++ objects from Python (or
+other languages). SWIG's advantage is that it makes it easy to plug
+the same C++ code into many other languages like Lua, Ruby, Java, etc.
+
+Implemented a desktop based GTK user interface to Micropolis, which
+runs on Mac, Windows and Linux. The user interface is written in
+Python, and based on PyGTK for user interface widgets, Cairo for
+graphics, and Pango for text.
+
+Implemented a desktop based GTK user interface to the CellEngine
+module, a cellular automata machine simulation, which uses the same
+TileEngine as Micropolis.
+
+Implemented a web server based OpenLaszlo (Flash) user interface to
+Micropolis, which runs the simulation on the web server, and displays
+the user interface in a web browser with Flash (or eventually any
+mainstream DHTML browser).
+
+Internationalized the web based version of Micropolis, and implemented
+a web based tool for localization, managing and editing translations
+into different languages.
+
+Implemented a MediaWiki extension for embedding OpenLaszlo
+applications in wiki pages, so you can write Wiki pages including live
+playable views of cities.
+
diff --git a/docs/MicropolisHARTalk.key/Contents/PkgInfo b/docs/MicropolisHARTalk.key/Contents/PkgInfo
new file mode 100644
index 0000000..b0bc8e0
--- /dev/null
+++ b/docs/MicropolisHARTalk.key/Contents/PkgInfo
@@ -0,0 +1 @@
+????????
\ No newline at end of file
diff --git a/docs/MicropolisHARTalk.key/QuickLook/Thumbnail.jpg b/docs/MicropolisHARTalk.key/QuickLook/Thumbnail.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..50dcde3cfd9a8ce31231c905b40e1364902a53e4
GIT binary patch
literal 107178
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