truelight@4300: /* $Id$ */ truelight@4300: rubidium@9111: /** @file genworld.cpp Functions to generate a map. */ belugas@6179: truelight@4300: #include "stdafx.h" truelight@4300: #include "openttd.h" maedhros@6453: #include "landscape.h" rubidium@10208: #include "company_func.h" truelight@4300: #include "variables.h" truelight@4300: #include "thread.h" rubidium@8116: #include "command_func.h" truelight@4300: #include "genworld.h" truelight@4300: #include "gfxinit.h" rubidium@8107: #include "window_func.h" rubidium@5469: #include "network/network.h" truelight@4300: #include "debug.h" rubidium@8208: #include "settings_func.h" truelight@4300: #include "heightmap.h" rubidium@8225: #include "viewport_func.h" rubidium@8225: #include "gfx_func.h" rubidium@8139: #include "map_func.h" rubidium@8140: #include "date_func.h" rubidium@8142: #include "core/random_func.hpp" rubidium@8786: #include "engine_func.h" rubidium@8270: #include "settings_type.h" rubidium@8303: #include "newgrf_storage.h" frosch@8380: #include "water.h" rubidium@9127: #include "tilehighlight_func.h" truelight@4300: rubidium@8264: #include "table/sprites.h" rubidium@8264: rubidium@6247: void GenerateClearTile(); rubidium@6247: void GenerateIndustries(); rubidium@6247: void GenerateUnmovables(); rubidium@6247: bool GenerateTowns(); rubidium@6247: void GenerateTrees(); truelight@4300: rubidium@6247: void StartupEconomy(); rubidium@10207: void StartupCompanies(); rubidium@6247: void StartupDisasters(); truelight@4300: rubidium@9409: void InitializeGame(uint size_x, uint size_y, bool reset_date); truelight@4300: truelight@4300: /* Please only use this variable in genworld.h and genworld.c and truelight@4300: * nowhere else. For speed improvements we need it to be global, but truelight@4300: * in no way the meaning of it is to use it anywhere else besides truelight@4300: * in the genworld.h and genworld.c! -- TrueLight */ truelight@4300: gw_info _gw; truelight@4300: truelight@4300: /** truelight@4300: * Set the status of the Paint flag. truelight@4300: * If it is true, the thread will hold with any futher generating till truelight@4300: * the drawing of the screen is done. This is handled by truelight@4300: * SetGeneratingWorldProgress(), so calling that function will stall truelight@4300: * from time to time. truelight@4300: */ truelight@4300: void SetGeneratingWorldPaintStatus(bool status) truelight@4300: { truelight@4300: _gw.wait_for_draw = status; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Returns true if the thread wants the main program to do a (full) paint. truelight@4300: * If this returns false, please do not update the screen. Because we are truelight@4300: * writing in a thread, it can cause damaged data (reading and writing the truelight@4300: * same tile at the same time). truelight@4300: */ rubidium@6247: bool IsGeneratingWorldReadyForPaint() truelight@4300: { truelight@4300: /* If we are in quit_thread mode, ignore this and always return false. This truelight@4300: * forces the screen to not be drawn, and the GUI not to wait for a draw. */ truelight@4300: if (!_gw.active || _gw.quit_thread || !_gw.threaded) return false; truelight@4300: truelight@4300: return _gw.wait_for_draw; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Tells if the world generation is done in a thread or not. truelight@4300: */ rubidium@6247: bool IsGenerateWorldThreaded() truelight@4300: { truelight@4300: return _gw.threaded && !_gw.quit_thread; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * The internal, real, generate function. truelight@4300: */ rubidium@9476: static void _GenerateWorld(void *arg) truelight@4300: { glx@8963: try { glx@8963: _generating_world = true; glx@8963: if (_network_dedicated) DEBUG(net, 0, "Generating map, please wait..."); glx@8963: /* Set the Random() seed to generation_seed so we produce the same map with the same seed */ rubidium@9413: if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = _settings_newgame.game_creation.generation_seed = InteractiveRandom(); rubidium@9413: _random.SetSeed(_settings_game.game_creation.generation_seed); glx@8963: SetGeneratingWorldProgress(GWP_MAP_INIT, 2); glx@8963: SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0); rubidium@8303: glx@8963: IncreaseGeneratingWorldProgress(GWP_MAP_INIT); glx@8963: /* Must start economy early because of the costs. */ glx@8963: StartupEconomy(); truelight@4300: glx@8963: /* Don't generate landscape items when in the scenario editor. */ glx@8963: if (_gw.mode == GW_EMPTY) { glx@8963: SetGeneratingWorldProgress(GWP_UNMOVABLE, 1); truelight@4300: glx@8963: /* Make the map the height of the patch setting */ rubidium@9413: if (_game_mode != GM_MENU) FlatEmptyWorld(_settings_game.game_creation.se_flat_world_height); truelight@4300: glx@8963: ConvertGroundTilesIntoWaterTiles(); glx@8963: IncreaseGeneratingWorldProgress(GWP_UNMOVABLE); glx@8963: } else { terom@10438: //GenerateLandscape(_gw.mode); terom@10439: GenerateClearTile(); truelight@4300: glx@8963: /* only generate towns, tree and industries in newgame mode. */ glx@8963: if (_game_mode != GM_EDITOR) { glx@8963: GenerateTowns(); terom@10439: GenerateIndustries(); terom@10439: GenerateUnmovables(); glx@8963: GenerateTrees(); glx@8963: } glx@8963: } truelight@4300: glx@8963: ClearStorageChanges(true); maedhros@6543: glx@8963: /* These are probably pointless when inside the scenario editor. */ glx@8963: SetGeneratingWorldProgress(GWP_GAME_INIT, 3); rubidium@10207: StartupCompanies(); glx@8963: IncreaseGeneratingWorldProgress(GWP_GAME_INIT); glx@8963: StartupEngines(); glx@8963: IncreaseGeneratingWorldProgress(GWP_GAME_INIT); glx@8963: StartupDisasters(); glx@8963: _generating_world = false; glx@8963: glx@8963: /* No need to run the tile loop in the scenario editor. */ glx@8963: if (_gw.mode != GW_EMPTY) { glx@8963: uint i; glx@8963: glx@8963: SetGeneratingWorldProgress(GWP_RUNTILELOOP, 0x500); glx@8963: for (i = 0; i < 0x500; i++) { glx@8963: RunTileLoop(); glx@8963: IncreaseGeneratingWorldProgress(GWP_RUNTILELOOP); glx@8963: } glx@8963: } glx@8963: glx@8963: ResetObjectToPlace(); rubidium@10207: _local_company = _gw.lc; glx@8963: glx@8963: SetGeneratingWorldProgress(GWP_GAME_START, 1); glx@8963: /* Call any callback */ glx@8963: if (_gw.proc != NULL) _gw.proc(); glx@8963: IncreaseGeneratingWorldProgress(GWP_GAME_START); glx@8963: glx@8963: if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE); glx@8963: /* Show all vital windows again, because we have hidden them */ glx@8963: if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows(); glx@8963: _gw.active = false; glx@8963: _gw.proc = NULL; glx@8963: _gw.threaded = false; glx@8963: glx@8963: DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0); glx@8963: MarkWholeScreenDirty(); glx@8963: glx@8963: if (_network_dedicated) DEBUG(net, 0, "Map generated, starting game"); glx@8963: rubidium@9413: if (_settings_client.gui.pause_on_newgame && _game_mode == GM_NORMAL) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); glx@8963: } catch (...) { glx@8963: _generating_world = false; glx@8963: throw; glx@8963: } truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Set here the function, if any, that you want to be called when landscape truelight@4300: * generation is done. truelight@4300: */ truelight@4300: void GenerateWorldSetCallback(gw_done_proc *proc) truelight@4300: { truelight@4300: _gw.proc = proc; truelight@4300: } truelight@4300: truelight@4300: /** rubidium@5145: * Set here the function, if any, that you want to be called when landscape rubidium@5145: * generation is aborted. rubidium@5145: */ rubidium@5145: void GenerateWorldSetAbortCallback(gw_abort_proc *proc) rubidium@5145: { rubidium@5145: _gw.abortp = proc; rubidium@5145: } rubidium@5145: rubidium@5145: /** truelight@4300: * This will wait for the thread to finish up his work. It will not continue truelight@4300: * till the work is done. truelight@4300: */ rubidium@6247: void WaitTillGeneratedWorld() truelight@4300: { truelight@4300: if (_gw.thread == NULL) return; truelight@4300: _gw.quit_thread = true; rubidium@8934: _gw.thread->Join(); rubidium@9479: delete _gw.thread; truelight@4300: _gw.thread = NULL; truelight@4300: _gw.threaded = false; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Initializes the abortion process truelight@4300: */ rubidium@6247: void AbortGeneratingWorld() truelight@4300: { truelight@4300: _gw.abort = true; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Is the generation being aborted? truelight@4300: */ rubidium@6247: bool IsGeneratingWorldAborted() truelight@4300: { truelight@4300: return _gw.abort; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Really handle the abortion, i.e. clean up some of the mess truelight@4300: */ rubidium@6247: void HandleGeneratingWorldAbortion() truelight@4300: { truelight@4300: /* Clean up - in SE create an empty map, otherwise, go to intro menu */ truelight@4300: _switch_mode = (_game_mode == GM_EDITOR) ? SM_EDITOR : SM_MENU; truelight@4300: rubidium@5145: if (_gw.abortp != NULL) _gw.abortp(); rubidium@5145: peter1138@5668: if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE); truelight@4300: /* Show all vital windows again, because we have hidden them */ truelight@4303: if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows(); rubidium@8934: truelight@4300: _gw.active = false; truelight@4300: _gw.proc = NULL; rubidium@5145: _gw.abortp = NULL; truelight@4300: _gw.threaded = false; truelight@4300: truelight@4300: DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0); truelight@4300: MarkWholeScreenDirty(); truelight@4300: rubidium@9479: _gw.thread->Exit(); truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Generate a world. belugas@6179: * @param mode The mode of world generation (see GenerateWorldModes). truelight@4300: * @param size_x The X-size of the map. truelight@4300: * @param size_y The Y-size of the map. truelight@4300: */ rubidium@9409: void GenerateWorld(GenerateWorldMode mode, uint size_x, uint size_y) truelight@4300: { truelight@4300: if (_gw.active) return; truelight@4300: _gw.mode = mode; truelight@4300: _gw.size_x = size_x; truelight@4300: _gw.size_y = size_y; truelight@4300: _gw.active = true; truelight@4300: _gw.abort = false; rubidium@5145: _gw.abortp = NULL; rubidium@10207: _gw.lc = _local_company; truelight@4300: _gw.wait_for_draw = false; truelight@4300: _gw.quit_thread = false; truelight@4300: _gw.threaded = true; truelight@4300: truelight@4300: /* This disables some commands and stuff */ rubidium@10207: SetLocalCompany(COMPANY_SPECTATOR); truelight@4300: /* Make sure everything is done via OWNER_NONE */ rubidium@10207: _current_company = OWNER_NONE; truelight@4300: glx@5340: /* Set the date before loading sprites as some newgrfs check it */ rubidium@9413: SetDate(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1)); glx@5340: peter1138@5155: /* Load the right landscape stuff */ peter1138@5155: GfxLoadSprites(); peter1138@5155: LoadStringWidthTable(); peter1138@5155: rubidium@9409: InitializeGame(_gw.size_x, _gw.size_y, false); truelight@4300: PrepareGenerateWorldProgress(); truelight@4300: truelight@4300: /* Re-init the windowing system */ truelight@4300: ResetWindowSystem(); truelight@4300: truelight@4300: /* Create toolbars */ truelight@4300: SetupColorsAndInitialWindow(); truelight@4300: rubidium@9479: if (_gw.thread != NULL) { rubidium@9479: _gw.thread->Join(); rubidium@9479: delete _gw.thread; rubidium@9479: _gw.thread = NULL; rubidium@9479: } rubidium@9479: Darkvater@5048: if (_network_dedicated || rubidium@9479: (_gw.thread = ThreadObject::New(&_GenerateWorld, NULL)) == NULL) { Darkvater@5380: DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode"); truelight@4300: _gw.threaded = false; truelight@4300: _GenerateWorld(NULL); Darkvater@5048: return; truelight@4300: } truelight@4300: Darkvater@5048: /* Remove any open window */ Darkvater@5048: DeleteAllNonVitalWindows(); Darkvater@5045: /* Hide vital windows, because we don't allow to use them */ Darkvater@5048: HideVitalWindows(); Darkvater@5048: Darkvater@5048: /* Don't show the dialog if we don't have a thread */ Darkvater@5048: ShowGenerateWorldProgress(); Darkvater@5045: Darkvater@5045: /* Centre the view on the map */ truelight@4300: if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) { peter1138@6760: ScrollMainWindowToTile(TileXY(MapSizeX() / 2, MapSizeY() / 2), true); truelight@4300: } truelight@4300: }