TileIndex TileAdd(TileIndex tile, TileIndexDiff add, const char *exp, const char *file, int line) { int dx; int dy; uint x; uint y; dx = add & MapMaxX(); if (dx >= (int)MapSizeX() / 2) dx -= MapSizeX(); dy = (add - dx) / (int)MapSizeX(); x = TileX(tile) + dx; y = TileY(tile) + dy; if (x >= MapSizeX() || y >= MapSizeY()) { char buf[512]; snprintf(buf, lengthof(buf), "TILE_ADD(%s) when adding 0x%.4X and 0x%.4X failed", exp, tile, add); #if !defined(_MSC_VER) || defined(WINCE) fprintf(stderr, "%s:%d %s\n", file, line, buf); #else _assert(buf, (char*)file, line); #endif } assert(TileXY(x, y) == TILE_MASK(tile + add)); return TileXY(x, y); }
/** * The main new land generator using Perlin noise. Desert landscape is handled * different to all others to give a desert valley between two high mountains. * Clearly if a low height terrain (flat/very flat) is chosen, then the tropic * areas won't be high enough, and there will be very little tropic on the map. * Thus Tropic works best on Hilly or Mountainous. */ void GenerateTerrainPerlin() { if (!AllocHeightMap()) return; GenerateWorldSetAbortCallback(FreeHeightMap); HeightMapGenerate(); IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); HeightMapNormalize(); IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); /* First make sure the tiles at the north border are void tiles if needed. */ if (_settings_game.construction.freeform_edges) { for (uint x = 0; x < MapSizeX(); x++) MakeVoid(TileXY(x, 0)); for (uint y = 0; y < MapSizeY(); y++) MakeVoid(TileXY(0, y)); } int max_height = H2I(TGPGetMaxHeight()); /* Transfer height map into OTTD map */ for (int y = 0; y < _height_map.size_y; y++) { for (int x = 0; x < _height_map.size_x; x++) { TgenSetTileHeight(TileXY(x, y), Clamp(H2I(_height_map.height(x, y)), 0, max_height)); } } IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); FreeHeightMap(); GenerateWorldSetAbortCallback(NULL); }
/** * Copy and paste heights from one map to another. * * @param src_area Area to read heights from. It consists of tiles, not of tile corners * e.g. if you pass a single tile area then 4 corners will be terraformed. * @param dst_area_north Norhern tile of the area to write heigths at. * @param transformation Transformation to perform on tile indices. * @param height_delta Offset, number of units to add to each height. */ void CopyPasteHeights(const GenericTileArea &src_area, GenericTileIndex dst_area_north, DirTransformation transformation, int height_delta) { /* include also corners at SW and SE edges */ GenericTileArea src_corners(src_area.tile, src_area.w + 1, src_area.h + 1); /* transform the most northern corner */ GenericTileIndex transformed_north_corner = src_corners.TransformedNorth(dst_area_north, transformation); #ifdef WITH_ASSERT { assert(IsValidTileIndex(dst_area_north)); uint x = TileX(dst_area_north); uint y = TileY(dst_area_north); assert(!IsMainMapTile(dst_area_north) || !_settings_game.construction.freeform_edges || (x > 0 && y > 0)); Dimension dst_dim = { src_corners.w, src_corners.h }; dst_dim = TransformDimension(dst_dim, transformation); assert(x + dst_dim.width <= MapSizeX(MapOf(dst_area_north)) && y + dst_dim.height <= MapSizeY(MapOf(dst_area_north))); } #endif /* WITH_ASSERT */ if (IsMainMapTile(dst_area_north)) { HeightsCopyPastingIterator iter(src_corners, AsMainMapTile(transformed_north_corner), transformation, height_delta); TerraformPasteTiles(&iter); } else { for (TransformationTileIteratorT<true, true> iter(src_corners, transformed_north_corner, transformation); IsValidTileIndex(iter); ++iter) { SetTileHeight(iter.DstTile(), TileHeight(iter.SrcTile())); } } }
/** * Create the DemandCalculator and immediately do the calculation. * @param job Job to calculate the demands for. */ DemandCalculator::DemandCalculator(LinkGraphJob &job) : max_distance(MapSizeX() + MapSizeY() - 2) { const LinkGraphSettings &settings = job.Settings(); CargoID cargo = job.Cargo(); this->accuracy = settings.accuracy; this->mod_dist = settings.demand_distance; if (this->mod_dist > 100) { /* Increase effect of mod_dist > 100 */ int over100 = this->mod_dist - 100; this->mod_dist = 100 + over100 * over100; } switch (settings.GetDistributionType(cargo)) { case DT_SYMMETRIC: this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size)); break; case DT_ASYMMETRIC: this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler()); break; default: /* Nothing to do. */ break; } }
/** * Generate a world. * @param mode The mode of world generation (see GenWorldMode). * @param size_x The X-size of the map. * @param size_y The Y-size of the map. * @param reset_settings Whether to reset the game configuration (used for restart) */ void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_settings) { if (HasModalProgress()) return; _gw.mode = mode; _gw.size_x = size_x; _gw.size_y = size_y; SetModalProgress(true); _gw.abort = false; _gw.abortp = NULL; _gw.lc = _local_company; _gw.quit_thread = false; _gw.threaded = true; /* This disables some commands and stuff */ SetLocalCompany(COMPANY_SPECTATOR); InitializeGame(_gw.size_x, _gw.size_y, true, reset_settings); PrepareGenerateWorldProgress(); /* Load the right landscape stuff, and the NewGRFs! */ GfxLoadSprites(); LoadStringWidthTable(); /* Re-init the windowing system */ ResetWindowSystem(); /* Create toolbars */ SetupColoursAndInitialWindow(); SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); if (_gw.thread != NULL) { _gw.thread->Join(); delete _gw.thread; _gw.thread = NULL; } if (!VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&_GenerateWorld, NULL, &_gw.thread, "ottd:genworld")) { DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode"); _gw.threaded = false; _modal_progress_work_mutex->EndCritical(); _GenerateWorld(NULL); _modal_progress_work_mutex->BeginCritical(); return; } UnshowCriticalError(); /* Remove any open window */ DeleteAllNonVitalWindows(); /* Hide vital windows, because we don't allow to use them */ HideVitalWindows(); /* Don't show the dialog if we don't have a thread */ ShowGenerateWorldProgress(); /* Centre the view on the map */ if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) { ScrollMainWindowToTile(TileXY(MapSizeX() / 2, MapSizeY() / 2), true); } }
/** * Param the minimum distance to an edge * @param tile the tile to get the distance from * @return the distance from the edge in tiles */ uint DistanceFromEdge(TileIndex tile) { const uint xl = TileX(tile); const uint yl = TileY(tile); const uint xh = MapSizeX() - 1 - xl; const uint yh = MapSizeY() - 1 - yl; const uint minl = min(xl, yl); const uint minh = min(xh, yh); return min(minl, minh); }
/** * This function takes care of the fact that land in OpenTTD can never differ * more than 1 in height */ void FixSlopes() { uint width, height; int row, col; byte current_tile; /* Adjust height difference to maximum one horizontal/vertical change. */ width = MapSizeX(); height = MapSizeY(); /* Top and left edge */ for (row = 0; (uint)row < height; row++) { for (col = 0; (uint)col < width; col++) { current_tile = MAX_TILE_HEIGHT; if (col != 0) { /* Find lowest tile; either the top or left one */ current_tile = TileHeight(TileXY(col - 1, row)); // top edge } if (row != 0) { if (TileHeight(TileXY(col, row - 1)) < current_tile) { current_tile = TileHeight(TileXY(col, row - 1)); // left edge } } /* Does the height differ more than one? */ if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) { /* Then change the height to be no more than one */ SetTileHeight(TileXY(col, row), current_tile + 1); } } } /* Bottom and right edge */ for (row = height - 1; row >= 0; row--) { for (col = width - 1; col >= 0; col--) { current_tile = MAX_TILE_HEIGHT; if ((uint)col != width - 1) { /* Find lowest tile; either the bottom and right one */ current_tile = TileHeight(TileXY(col + 1, row)); // bottom edge } if ((uint)row != height - 1) { if (TileHeight(TileXY(col, row + 1)) < current_tile) { current_tile = TileHeight(TileXY(col, row + 1)); // right edge } } /* Does the height differ more than one? */ if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) { /* Then change the height to be no more than one */ SetTileHeight(TileXY(col, row), current_tile + 1); } } } }
void FlatEmptyWorld(byte tile_height) { int edge_distance = _settings_game.construction.freeform_edges ? 0 : 2; for (uint row = edge_distance; row < MapSizeY() - edge_distance; row++) { for (uint col = edge_distance; col < MapSizeX() - edge_distance; col++) { SetTileHeight(TileXY(col, row), tile_height); } } FixSlopes(); MarkWholeScreenDirty(); }
/** * Make a heightmap of the current map. * @param filename Filename to use for saving. */ bool MakeHeightmapScreenshot(const char *filename) { Colour palette[256]; for (uint i = 0; i < lengthof(palette); i++) { palette[i].a = 0xff; palette[i].r = i; palette[i].g = i; palette[i].b = i; } const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format; return sf->proc(filename, HeightmapCallback, NULL, MapSizeX(), MapSizeY(), 8, palette); }
/** * Move ourselves to the next tile in the rectange on the map. */ TileIterator &DiagonalTileIterator::operator++() { assert(this->tile != INVALID_TILE); /* Determine the next tile, while clipping at map borders */ bool new_line = false; do { /* Iterate using the rotated coordinates. */ if (this->a_max == 1 || this->a_max == -1) { /* Special case: Every second column has zero length, skip them completely */ this->a_cur = 0; if (this->b_max > 0) { this->b_cur = min(this->b_cur + 2, this->b_max); } else { this->b_cur = max(this->b_cur - 2, this->b_max); } } else { /* Every column has at least one tile to process */ if (this->a_max > 0) { this->a_cur += 2; new_line = this->a_cur >= this->a_max; } else { this->a_cur -= 2; new_line = this->a_cur <= this->a_max; } if (new_line) { /* offset of initial a_cur: one tile in the same direction as a_max * every second line. */ this->a_cur = abs(this->a_cur) % 2 ? 0 : (this->a_max > 0 ? 1 : -1); if (this->b_max > 0) { ++this->b_cur; } else { --this->b_cur; } } } /* And convert the coordinates back once we've gone to the next tile. */ uint x = this->base_x + (this->a_cur - this->b_cur) / 2; uint y = this->base_y + (this->b_cur + this->a_cur) / 2; /* Prevent wrapping around the map's borders. */ this->tile = x >= MapSizeX() || y >= MapSizeY() ? INVALID_TILE : TileXY(x, y); } while (this->tile > MapSize() && this->b_max != this->b_cur); if (this->b_max == this->b_cur) this->tile = INVALID_TILE; return *this; }
/** * Allocate array of (MapSizeX()+1)*(MapSizeY()+1) heights and init the _height_map structure members * @return true on success */ static inline bool AllocHeightMap() { height_t *h; _height_map.size_x = MapSizeX(); _height_map.size_y = MapSizeY(); /* Allocate memory block for height map row pointers */ _height_map.total_size = (_height_map.size_x + 1) * (_height_map.size_y + 1); _height_map.dim_x = _height_map.size_x + 1; _height_map.h = CallocT<height_t>(_height_map.total_size); /* Iterate through height map initialize values */ FOR_ALL_TILES_IN_HEIGHT(h) *h = _invalid_height; return true; }
GenericTileIndex TileAddXY(GenericTileIndex tile, int dx, int dy, const char *exp, const char *file, int line) { uint x = TileX(tile) + dx; uint y = TileY(tile) + dy; if (x >= MapSizeX(MapOf(tile)) || y >= MapSizeY(MapOf(tile))) { char buf[512]; snprintf(buf, lengthof(buf), "TILE_ADDXY(%s) when adding 0x%.4X and <0x%.4X, 0x%.4X> failed", exp, IndexOf(tile), dx, dy); #if !defined(_MSC_VER) || defined(WINCE) fprintf(stderr, "%s:%d %s\n", file, line, buf); #else _assert(buf, (char*)file, line); #endif } return TileXY<true>(x, y, MapOf(tile)); }
/** * Get the area of the matrix square that contains a specific tile. * @param The tile to get the map area for. * @param extend Extend the area by this many squares on all sides. * @return Tile area containing the tile. */ static TileArea GetAreaForTile(TileIndex tile, uint extend = 0) { uint tile_x = (TileX(tile) / N) * N; uint tile_y = (TileY(tile) / N) * N; uint w = N, h = N; w += min(extend * N, tile_x); h += min(extend * N, tile_y); tile_x -= min(extend * N, tile_x); tile_y -= min(extend * N, tile_y); w += min(extend * N, MapSizeX() - tile_x - w); h += min(extend * N, MapSizeY() - tile_y - h); return TileArea(TileXY(tile_x, tile_y), w, h); }
/** Send a welcome message to the admin. */ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome() { Packet *p = new Packet(ADMIN_PACKET_SERVER_WELCOME); p->Send_string(_settings_client.network.server_name); p->Send_string(_openttd_revision); p->Send_bool (_network_dedicated); p->Send_string(_network_game_info.map_name); p->Send_uint32(_settings_game.game_creation.generation_seed); p->Send_uint8 (_settings_game.game_creation.landscape); p->Send_uint32(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1)); p->Send_uint16(MapSizeX()); p->Send_uint16(MapSizeY()); this->SendPacket(p); return NETWORK_RECV_STATUS_OKAY; }
void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr) { /* Just a fail-safe.. should never happen */ if (!_network_udp_server) { return; } NetworkGameInfo ngi; /* Update some game_info */ ngi.clients_on = _network_game_info.clients_on; ngi.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1); ngi.server_lang = _settings_client.network.server_lang; ngi.use_password = !StrEmpty(_settings_client.network.server_password); ngi.clients_max = _settings_client.network.max_clients; ngi.companies_on = (byte)Company::GetNumItems(); ngi.companies_max = _settings_client.network.max_companies; ngi.spectators_on = NetworkSpectatorCount(); ngi.spectators_max = _settings_client.network.max_spectators; ngi.game_date = _date; ngi.map_width = MapSizeX(); ngi.map_height = MapSizeY(); ngi.map_set = _settings_game.game_creation.landscape; ngi.dedicated = _network_dedicated; ngi.grfconfig = _grfconfig; strecpy(ngi.map_name, _network_game_info.map_name, lastof(ngi.map_name)); strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name)); strecpy(ngi.server_revision, _openttd_revision, lastof(ngi.server_revision)); Packet packet(PACKET_UDP_SERVER_RESPONSE); this->SendNetworkGameInfo(&packet, &ngi); /* Let the client know that we are here */ this->SendPacket(&packet, client_addr); DEBUG(net, 2, "[udp] queried from %s", client_addr->GetHostname()); }
/** * Generalized circular search allowing for rectangles and a hole. * Function performing a search around a center rectangle and going outward. * The center rectangle is left out from the search. To do a rectangular search * without a hole, set either h or w to zero. * Every tile will be tested by means of the callback function proc, * which will determine if yes or no the given tile meets criteria of search. * @param tile to start the search from. Upon completion, it will return the tile matching the search. * This tile should be directly north of the hole (if any). * @param radius How many tiles to search outwards. Note: This is a radius and thus different * from the size parameter of the other CircularTileSearch function, which is a diameter. * @param w the width of the inner rectangle * @param h the height of the inner rectangle * @param proc callback testing function pointer. * @param user_data to be passed to the callback function. Depends on the implementation * @return result of the search * @pre proc != NULL * @pre radius > 0 */ bool CircularTileSearch(TileIndex *tile, uint radius, uint w, uint h, TestTileOnSearchProc proc, void *user_data) { assert(proc != NULL); assert(radius > 0); uint x = TileX(*tile) + w + 1; uint y = TileY(*tile); const uint extent[DIAGDIR_END] = { w, h, w, h }; for (uint n = 0; n < radius; n++) { for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { /* Is the tile within the map? */ for (uint j = extent[dir] + n * 2 + 1; j != 0; j--) { if (x < MapSizeX() && y < MapSizeY()) { TileIndex t = TileXY(x, y); /* Is the callback successful? */ if (proc(t, user_data)) { /* Stop the search */ *tile = t; return true; } } /* Step to the next 'neighbour' in the circular line */ x += _tileoffs_by_diagdir[dir].x; y += _tileoffs_by_diagdir[dir].y; } } /* Jump to next circle to test */ x += _tileoffs_by_dir[DIR_W].x; y += _tileoffs_by_dir[DIR_W].y; } *tile = INVALID_TILE; return false; }
/** * Converts a given grayscale map to something that fits in OTTD map system * and create a map of that data. * @param img_width the with of the image in pixels/tiles * @param img_height the height of the image in pixels/tiles * @param map the input map */ static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map) { /* Defines the detail of the aspect ratio (to avoid doubles) */ const uint num_div = 16384; uint width, height; uint row, col; uint row_pad = 0, col_pad = 0; uint img_scale; uint img_row, img_col; TileIndex tile; /* Get map size and calculate scale and padding values */ switch (_settings_game.game_creation.heightmap_rotation) { default: NOT_REACHED(); case HM_COUNTER_CLOCKWISE: width = MapSizeX(); height = MapSizeY(); break; case HM_CLOCKWISE: width = MapSizeY(); height = MapSizeX(); break; } if ((img_width * num_div) / img_height > ((width * num_div) / height)) { /* Image is wider than map - center vertically */ img_scale = (width * num_div) / img_width; row_pad = (1 + height - ((img_height * img_scale) / num_div)) / 2; } else { /* Image is taller than map - center horizontally */ img_scale = (height * num_div) / img_height; col_pad = (1 + width - ((img_width * img_scale) / num_div)) / 2; } if (_settings_game.construction.freeform_edges) { for (uint x = 0; x < MapSizeX(); x++) MakeVoid(TileXY(x, 0)); for (uint y = 0; y < MapSizeY(); y++) MakeVoid(TileXY(0, y)); } /* Form the landscape */ for (row = 0; row < height; row++) { for (col = 0; col < width; col++) { switch (_settings_game.game_creation.heightmap_rotation) { default: NOT_REACHED(); case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break; case HM_CLOCKWISE: tile = TileXY(row, col); break; } /* Check if current tile is within the 1-pixel map edge or padding regions */ if ((!_settings_game.construction.freeform_edges && DistanceFromEdge(tile) <= 1) || (row < row_pad) || (row >= (height - row_pad - (_settings_game.construction.freeform_edges ? 0 : 1))) || (col < col_pad) || (col >= (width - col_pad - (_settings_game.construction.freeform_edges ? 0 : 1)))) { SetTileHeight(tile, 0); } else { /* Use nearest neighbor resizing to scale map data. * We rotate the map 45 degrees (counter)clockwise */ img_row = (((row - row_pad) * num_div) / img_scale); switch (_settings_game.game_creation.heightmap_rotation) { default: NOT_REACHED(); case HM_COUNTER_CLOCKWISE: img_col = (((width - 1 - col - col_pad) * num_div) / img_scale); break; case HM_CLOCKWISE: img_col = (((col - col_pad) * num_div) / img_scale); break; } assert(img_row < img_height); assert(img_col < img_width); /* Colour scales from 0 to 255, OpenTTD height scales from 0 to 15 */ SetTileHeight(tile, map[img_row * img_width + img_col] / 16); } /* Only clear the tiles within the map area. */ if (TileX(tile) != MapMaxX() && TileY(tile) != MapMaxY() && (!_settings_game.construction.freeform_edges || (TileX(tile) != 0 && TileY(tile) != 0))) { MakeClear(tile, CLEAR_GRASS, 3); } } } }
CRect CGameMap::MapRect(){ CRect r(0,0,MapSizeX(),MapSizeY()); return r; }
static void Save_MAPS() { _map_dim_x = MapSizeX(); _map_dim_y = MapSizeY(); SlGlobList(_map_dimensions); }
/** * Clamp the tile area to map borders. */ void TileArea::ClampToMap() { assert(this->tile < MapSize()); this->w = min(this->w, MapSizeX() - TileX(this->tile)); this->h = min(this->h, MapSizeY() - TileY(this->tile)); }
/** * The internal, real, generate function. */ static void _GenerateWorld(void *) { /* Make sure everything is done via OWNER_NONE. */ Backup<CompanyByte> _cur_company(_current_company, OWNER_NONE, FILE_LINE); try { _generating_world = true; _modal_progress_work_mutex->BeginCritical(); if (_network_dedicated) DEBUG(net, 1, "Generating map, please wait..."); /* Set the Random() seed to generation_seed so we produce the same map with the same seed */ if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = _settings_newgame.game_creation.generation_seed = InteractiveRandom(); _random.SetSeed(_settings_game.game_creation.generation_seed); SetGeneratingWorldProgress(GWP_MAP_INIT, 2); SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP); IncreaseGeneratingWorldProgress(GWP_MAP_INIT); /* Must start economy early because of the costs. */ StartupEconomy(); /* Don't generate landscape items when in the scenario editor. */ if (_gw.mode == GWM_EMPTY) { SetGeneratingWorldProgress(GWP_OBJECT, 1); /* Make sure the tiles at the north border are void tiles if needed. */ if (_settings_game.construction.freeform_edges) { for (uint row = 0; row < MapSizeY(); row++) MakeVoid(TileXY(0, row)); for (uint col = 0; col < MapSizeX(); col++) MakeVoid(TileXY(col, 0)); } /* Make the map the height of the setting */ if (_game_mode != GM_MENU) FlatEmptyWorld(_settings_game.game_creation.se_flat_world_height); ConvertGroundTilesIntoWaterTiles(); IncreaseGeneratingWorldProgress(GWP_OBJECT); } else { GenerateLandscape(_gw.mode); GenerateClearTile(); /* only generate towns, tree and industries in newgame mode. */ if (_game_mode != GM_EDITOR) { if (!GenerateTowns(_settings_game.economy.town_layout)) { _cur_company.Restore(); HandleGeneratingWorldAbortion(); return; } GenerateIndustries(); GenerateObjects(); GenerateTrees(); } } /* These are probably pointless when inside the scenario editor. */ SetGeneratingWorldProgress(GWP_GAME_INIT, 3); StartupCompanies(); IncreaseGeneratingWorldProgress(GWP_GAME_INIT); StartupEngines(); IncreaseGeneratingWorldProgress(GWP_GAME_INIT); StartupDisasters(); _generating_world = false; /* No need to run the tile loop in the scenario editor. */ if (_gw.mode != GWM_EMPTY) { uint i; SetGeneratingWorldProgress(GWP_RUNTILELOOP, 0x500); for (i = 0; i < 0x500; i++) { RunTileLoop(); _tick_counter++; IncreaseGeneratingWorldProgress(GWP_RUNTILELOOP); } if (_game_mode != GM_EDITOR) { Game::StartNew(); if (Game::GetInstance() != NULL) { SetGeneratingWorldProgress(GWP_RUNSCRIPT, 2500); _generating_world = true; for (i = 0; i < 2500; i++) { Game::GameLoop(); IncreaseGeneratingWorldProgress(GWP_RUNSCRIPT); if (Game::GetInstance()->IsSleeping()) break; } _generating_world = false; } } } BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP); ResetObjectToPlace(); _cur_company.Trash(); _current_company = _local_company = _gw.lc; SetGeneratingWorldProgress(GWP_GAME_START, 1); /* Call any callback */ if (_gw.proc != NULL) _gw.proc(); IncreaseGeneratingWorldProgress(GWP_GAME_START); CleanupGeneration(); _modal_progress_work_mutex->EndCritical(); ShowNewGRFError(); if (_network_dedicated) DEBUG(net, 1, "Map generated, starting game"); DEBUG(desync, 1, "new_map: %08x", _settings_game.game_creation.generation_seed); if (_debug_desync_level > 0) { char name[MAX_PATH]; seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); } } catch (...) { BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP, true); if (_cur_company.IsValid()) _cur_company.Restore(); _generating_world = false; _modal_progress_work_mutex->EndCritical(); throw; } }
/** * Terraform the north corner of a tile to a specific height. * * @param ts TerraformerState. * @param tile Tile. * @param height Aimed height. * @return Error code or cost. */ static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height) { assert(tile < MapSize()); /* Check range of destination height */ if (height < 0) return_cmd_error(STR_ERROR_ALREADY_AT_SEA_LEVEL); if (height > _settings_game.construction.max_heightlevel) return_cmd_error(STR_ERROR_TOO_HIGH); /* * Check if the terraforming has any effect. * This can only be true, if multiple corners of the start-tile are terraformed (i.e. the terraforming is done by towns/industries etc.). * In this case the terraforming should fail. (Don't know why.) */ if (height == TerraformGetHeightOfTile(ts, tile)) return CMD_ERROR; /* Check "too close to edge of map". Only possible when freeform-edges is off. */ uint x = TileX(tile); uint y = TileY(tile); if (!_settings_game.construction.freeform_edges && ((x <= 1) || (y <= 1) || (x >= MapMaxX() - 1) || (y >= MapMaxY() - 1))) { /* * Determine a sensible error tile */ if (x == 1) x = 0; if (y == 1) y = 0; _terraform_err_tile = TileXY(x, y); return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP); } /* Mark incident tiles that are involved in the terraforming. */ TerraformAddDirtyTileAround(ts, tile); /* Store the height modification */ TerraformSetHeightOfTile(ts, tile, height); CommandCost total_cost(EXPENSES_CONSTRUCTION); /* Increment cost */ total_cost.AddCost(_price[PR_TERRAFORM]); /* Recurse to neighboured corners if height difference is larger than 1 */ { const TileIndexDiffC *ttm; TileIndex orig_tile = tile; static const TileIndexDiffC _terraform_tilepos[] = { { 1, 0}, // move to tile in SE {-2, 0}, // undo last move, and move to tile in NW { 1, 1}, // undo last move, and move to tile in SW { 0, -2} // undo last move, and move to tile in NE }; for (ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) { tile += ToTileIndexDiff(*ttm); if (tile >= MapSize()) continue; /* Make sure we don't wrap around the map */ if (Delta(TileX(orig_tile), TileX(tile)) == MapSizeX() - 1) continue; if (Delta(TileY(orig_tile), TileY(tile)) == MapSizeY() - 1) continue; /* Get TileHeight of neighboured tile as of current terraform progress */ int r = TerraformGetHeightOfTile(ts, tile); int height_diff = height - r; /* Is the height difference to the neighboured corner greater than 1? */ if (abs(height_diff) > 1) { /* Terraform the neighboured corner. The resulting height difference should be 1. */ height_diff += (height_diff < 0 ? 1 : -1); CommandCost cost = TerraformTileHeight(ts, tile, r + height_diff); if (cost.Failed()) return cost; total_cost.AddCost(cost); } } } return total_cost; }