/* emscripten.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 emscripten.cpp
* @brief Emscripten bindings for Micropolis game engine.
*
* This file contains Emscripten bindings that allow the Micropolis
* (open-source version of SimCity) game engine to be used in a web
* environment. It utilizes Emscripten's Embind feature to expose C++
* classes, functions, enums, and data structures to JavaScript,
* enabling the Micropolis game engine to be controlled and interacted
* with through a web interface. This includes key functionalities
* such as simulation control, game state management, map
* manipulation, and event handling. The binding includes only
* essential elements for gameplay, omitting low-level rendering and
* platform-specific code.
*/
////////////////////////////////////////////////////////////////////////
#include
#include "micropolis.h"
////////////////////////////////////////////////////////////////////////
using namespace emscripten;
////////////////////////////////////////////////////////////////////////
// This file uses emscripten's embind to bind C++ classes,
// C structures, functions, enums, and contents into JavaScript,
// so you can even subclass C++ classes in JavaScript,
// for implementing plugins and user interfaces.
//
// Wrapping the entire Micropolis class from the Micropolis (open-source
// version of SimCity) code into Emscripten for JavaScript access is a
// large and complex task, mainly due to the size and complexity of the
// class. The class encompasses almost every aspect of the simulation,
// including map generation, simulation logic, user interface
// interactions, and more.
//
// Strategy for Wrapping
//
// 1. Core Simulation Logic: Focus on the core simulation aspects, such
// as the methods to run the simulation, update game states, and handle
// user inputs (like building tools and disaster simulations). This is
// crucial for any gameplay functionality.
//
// 2. Memory and Performance Considerations: JavaScript and WebAssembly
// run in a browser context, which can have memory limitations and
// performance constraints. Carefully manage memory allocation,
// especially when dealing with the game's map and various buffers.
//
// 3. Direct Memory Access: Provide JavaScript access to critical game
// data structures like the map buffer for efficient reading and
// writing. This can be done using Emscripten's heap access functions
// (HEAP8, HEAP16, HEAP32, etc.).
//
// 4. User Interface and Rendering: This part might not be necessary to
// wrap, as modern web technologies (HTML, CSS, WebGL) can be used for
// UI. However, providing some hooks for game state (like score, budget,
// etc.) to JavaScript might be helpful.
//
// 5. Callbacks and Interactivity: Ensure that key game events and
// callbacks are exposed to JavaScript, allowing for interactive and
// responsive gameplay.
//
// 6. Optimizations: Where possible, optimize C++ code for WebAssembly,
// focusing on critical paths in the simulation loop.
//
// Decisions and Explanations
//
// - Excluded Elements:
//
// - Low-level rendering or platform-specific code, as this can be
// handled more efficiently with web technologies.
//
// - Parts of the code that handle file I/O directly, as file access
// in a web context is typically handled differently (e.g., using
// browser APIs or server-side support).
//
// - Any networking or multiplayer code, as web-based
// implementations would differ significantly from desktop-based
// network code.
//
// - Included Elements:
//
// - Core game mechanics, such as map generation, zone simulation
// (residential, commercial, industrial), disaster simulation, and
// basic utilities.
//
// - Game state management, including budgeting, scoring, and city
// evaluation.
//
// - Direct memory access to critical structures like the map
// buffer, allowing efficient manipulation from JavaScript.
//
// - Essential callbacks and event handling mechanisms to ensure
// interactivity.
//
// Conclusion
//
// Given the complexity and size of the Micropolis class, wrapping the
// entire class directly is impractical. However, focusing on key areas
// essential for gameplay and providing efficient interfaces for
// critical data structures can create a functional and interactive city
// simulation in a web context. Further optimizations and adjustments
// would likely be needed based on testing and specific requirements of
// the web implementation.
//
// Implementation Notes
//
// The enum_, class_, constructor, function, and property functions
// from the emscripten namespace are used to specify how C++
// constructs should be exposed to JavaScript. You can use these to
// control which parts of your code are accessible and how they should
// be used from JavaScript.
//
// I've made some assumptions here:
//
// The types MapValue and MapTile are simple types (like integers or
// floats). If they are complex types, they would need their own
// bindings.
//
// I'm assuming that the copy constructor and copy assignment
// operator for the Position class are correctly implemented. If
// they aren't, then the Position object may not behave as expected
// in JavaScript.
//
// Micropolis Binding Design
//
// The Micropolis interface organizes the Micropolis class header into
// categories of functions that are relevant for interaction with the
// JavaScript user interface, scripts, or plugins. The aim is to expose
// functions that could help in monitoring and controlling game state
// effectively.
//
// - Exposed to JavaScript (Public Interface)
// - Simulation Control and Settings
// - void simTick()
// - void setSpeed(short speed)
// - void setGameLevel(GameLevel level)
// - void setCityName(const std::string &name)
// - void setYear(int year)
// - void pause()
// - void resume()
// - void setEnableDisasters(bool value)
// - void setAutoBudget(bool value)
// - void setAutoBulldoze(bool value)
// - void setAutoGoto(bool value)
// - void setEnableSound(bool value)
// - void setDoAnimation(bool value)
// - void doNewGame()
// - void doBudget()
// - void doScoreCard()
// - void updateFunds()
// - Gameplay Mechanics
// - void doTool(EditingTool tool, short tileX, short tileY)
// - void generateMap(int seed)
// - void clearMap()
// - void makeDisaster(DisasterType type)
// - void getDemands(float *resDemandResult, float *comDemandResult, float *indDemandResult)
// - Random Number Generation
// - int simRandom()
// - int getRandom(short range)
// - int getRandom16()
// - int getRandom16Signed()
// - short getERandom(short limit)
// - void randomlySeedRandom()
// - void seedRandom(int seed)
// - Game State and Data Access
// - int getTile(int x, int y)
// - void setTile(int x, int y, int tile)
// - void setFunds(Quad dollars)
// - Quad getCityPopulation()
// - void updateMaps()
// - void updateGraphs()
// - void updateEvaluation()
// - void updateBudget()
// - Events and Callbacks
// - void sendMessage(short messageNumber, short x = NOWHERE, short y = NOWHERE)
// - void makeSound(std::string channel, std::string sound, int x = -1, int y = -1)
// - Hidden from JavaScript (Private Interface)
// - Internal Simulation Mechanics
// - void simFrame()
// - void simulate()
// - void doSimInit()
// - void setValves()
// - void clearCensus()
// - void collectTax()
// - void mapScan(int x1, int x2)
// - Utility Functions
// - void initMapArrays()
// - void destroyMapArrays()
// - void initSimMemory()
// - void initGraphs()
// - Zone Handling
// - void doZone(const Position &pos)
// - void doResidential(const Position &pos, bool zonePower)
// - void doCommercial(const Position &pos, bool zonePower)
// - void doIndustrial(const Position &pos, bool zonePower)
// - Disaster Simulation
// - void doDisasters()
// - void scenarioDisaster()
// - void fireAnalysis()
// - void makeFire()
// - void makeFlood()
//
// Conclusion
//
// These exposed functions provide a comprehensive interface for
// scripting, plugins, and user interactions through JavaScript. The
// exposed set of functions includes random number generation,
// simulation control mechanisms, UI-triggered actions like budget
// updates, along with essential gameplay mechanics. The private section
// continues to encapsulate internal simulation details and complex data
// management routines integral to the game's core mechanics.
EMSCRIPTEN_BINDINGS(MicropolisEngine) {
// position.h
enum_("Direction2")
.value("INVALID", Direction2::DIR2_INVALID)
.value("NORTH", Direction2::DIR2_NORTH)
.value("NORTH_EAST", Direction2::DIR2_NORTH_EAST)
.value("EAST", Direction2::DIR2_EAST)
.value("SOUTH_EAST", Direction2::DIR2_SOUTH_EAST)
.value("SOUTH", Direction2::DIR2_SOUTH)
.value("SOUTH_WEST", Direction2::DIR2_SOUTH_WEST)
.value("WEST", Direction2::DIR2_WEST)
.value("NORTH_WEST", Direction2::DIR2_NORTH_WEST)
;
class_("Position")
.constructor<>()
.constructor()
.function("move", &Position::move)
.function("testBounds", &Position::testBounds)
.property("posX", &Position::posX)
.property("posY", &Position::posY)
;
function("increment45", &increment45);
function("increment90", &increment90);
function("rotate45", &rotate45);
function("rotate90", &rotate90);
function("rotate180", &rotate180);
// tool.h
class_("ToolEffects")
//.constructor() // TODO: wrap
.function("clear", &ToolEffects::clear)
.function("modifyWorld", &ToolEffects::modifyWorld)
.function("modifyIfEnoughFunding", &ToolEffects::modifyIfEnoughFunding)
.function("getMapValue", select_overload(&ToolEffects::getMapValue))
.function("getMapTile", select_overload(&ToolEffects::getMapTile))
.function("getCost", &ToolEffects::getCost)
.function("addCost", &ToolEffects::addCost)
.function("setMapValue", select_overload(&ToolEffects::setMapValue))
//.function("addFrontendMessage", &ToolEffects::addFrontendMessage) // TODO: wrap
;
enum_("MapTileBits")
.value("PWRBIT", PWRBIT)
.value("CONDBIT", CONDBIT)
.value("BURNBIT", BURNBIT)
.value("BULLBIT", BULLBIT)
.value("ANIMBIT", ANIMBIT)
.value("ZONEBIT", ZONEBIT)
.value("ALLBITS", ALLBITS)
.value("LOMASK", LOMASK)
.value("BLBNBIT", BLBNBIT)
.value("BLBNCNBIT", BLBNCNBIT)
.value("BNCNBIT", BNCNBIT)
;
enum_("EditingTool")
.value("TOOL_RESIDENTIAL", TOOL_RESIDENTIAL)
.value("TOOL_COMMERCIAL", TOOL_COMMERCIAL)
.value("TOOL_INDUSTRIAL", TOOL_INDUSTRIAL)
.value("TOOL_FIRESTATION", TOOL_FIRESTATION)
.value("TOOL_POLICESTATION", TOOL_POLICESTATION)
.value("TOOL_QUERY", TOOL_QUERY)
.value("TOOL_WIRE", TOOL_WIRE)
.value("TOOL_BULLDOZER", TOOL_BULLDOZER)
.value("TOOL_RAILROAD", TOOL_RAILROAD)
.value("TOOL_ROAD", TOOL_ROAD)
.value("TOOL_STADIUM", TOOL_STADIUM)
.value("TOOL_PARK", TOOL_PARK)
.value("TOOL_SEAPORT", TOOL_SEAPORT)
.value("TOOL_COALPOWER", TOOL_COALPOWER)
.value("TOOL_NUCLEARPOWER", TOOL_NUCLEARPOWER)
.value("TOOL_AIRPORT", TOOL_AIRPORT)
.value("TOOL_NETWORK", TOOL_NETWORK)
.value("TOOL_WATER", TOOL_WATER)
.value("TOOL_LAND", TOOL_LAND)
.value("TOOL_FOREST", TOOL_FOREST)
.value("TOOL_COUNT", TOOL_COUNT)
.value("TOOL_FIRST", TOOL_FIRST)
.value("TOOL_LAST", TOOL_LAST);
;
// map_type.h
class_