truelight@4300: /* $Id$ */ truelight@4300: truelight@4300: #include "stdafx.h" truelight@4300: #include "openttd.h" truelight@4300: #include "functions.h" truelight@4300: #include "player.h" truelight@4300: #include "table/sprites.h" truelight@4300: #include "variables.h" truelight@4300: #include "thread.h" truelight@4300: #include "genworld.h" truelight@4300: #include "gfx.h" truelight@4300: #include "gfxinit.h" truelight@4300: #include "gui.h" truelight@4300: #include "network.h" truelight@4300: #include "debug.h" truelight@4300: #include "settings.h" truelight@4300: #include "heightmap.h" glx@5340: #include "date.h" truelight@4300: truelight@4300: void GenerateLandscape(byte mode); truelight@4300: void GenerateClearTile(void); truelight@4300: void GenerateIndustries(void); truelight@4300: void GenerateUnmovables(void); truelight@4300: bool GenerateTowns(void); truelight@4300: void GenerateTrees(void); truelight@4300: truelight@4300: void StartupEconomy(void); truelight@4300: void StartupPlayers(void); truelight@4300: void StartupDisasters(void); truelight@4300: truelight@4300: void InitializeGame(int mode, uint size_x, uint size_y); truelight@4300: truelight@4300: void ConvertGroundTilesIntoWaterTiles(void); 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: */ truelight@4300: bool IsGeneratingWorldReadyForPaint(void) 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: */ truelight@4300: bool IsGenerateWorldThreaded(void) 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: */ truelight@4300: static void *_GenerateWorld(void *arg) truelight@4300: { truelight@4300: _generating_world = true; Darkvater@5568: if (_network_dedicated) DEBUG(net, 0, "Generating map, please wait..."); truelight@4300: /* Set the Random() seed to generation_seed so we produce the same map with the same seed */ truelight@4300: if (_patches.generation_seed == GENERATE_NEW_SEED) _patches.generation_seed = _patches_newgame.generation_seed = InteractiveRandom(); truelight@4300: _random_seeds[0][0] = _random_seeds[0][1] = _patches.generation_seed; truelight@4300: SetGeneratingWorldProgress(GWP_MAP_INIT, 2); truelight@4300: SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0); truelight@4300: truelight@4300: IncreaseGeneratingWorldProgress(GWP_MAP_INIT); truelight@4300: // Must start economy early because of the costs. truelight@4300: StartupEconomy(); truelight@4300: truelight@4300: // Don't generate landscape items when in the scenario editor. truelight@4300: if (_gw.mode == GW_EMPTY) { truelight@4300: SetGeneratingWorldProgress(GWP_UNMOVABLE, 1); truelight@4300: truelight@4300: /* Make the map the height of the patch setting */ truelight@4300: if (_game_mode != GM_MENU) FlatEmptyWorld(_patches.se_flat_world_height); truelight@4300: truelight@4300: ConvertGroundTilesIntoWaterTiles(); truelight@4300: IncreaseGeneratingWorldProgress(GWP_UNMOVABLE); truelight@4300: } else { truelight@4300: GenerateLandscape(_gw.mode); truelight@4300: GenerateClearTile(); truelight@4300: truelight@4300: // only generate towns, tree and industries in newgame mode. truelight@4300: if (_game_mode != GM_EDITOR) { truelight@4300: GenerateTowns(); truelight@4300: GenerateIndustries(); truelight@4300: GenerateUnmovables(); truelight@4300: GenerateTrees(); truelight@4300: } truelight@4300: } truelight@4300: truelight@4300: // These are probably pointless when inside the scenario editor. truelight@4300: SetGeneratingWorldProgress(GWP_GAME_INIT, 3); truelight@4300: StartupPlayers(); truelight@4300: IncreaseGeneratingWorldProgress(GWP_GAME_INIT); truelight@4300: StartupEngines(); truelight@4300: IncreaseGeneratingWorldProgress(GWP_GAME_INIT); truelight@4300: StartupDisasters(); truelight@4300: _generating_world = false; truelight@4300: truelight@4300: // No need to run the tile loop in the scenario editor. truelight@4300: if (_gw.mode != GW_EMPTY) { truelight@4300: uint i; truelight@4300: truelight@4300: SetGeneratingWorldProgress(GWP_RUNTILELOOP, 0x500); truelight@4300: for (i = 0; i < 0x500; i++) { truelight@4300: RunTileLoop(); truelight@4300: IncreaseGeneratingWorldProgress(GWP_RUNTILELOOP); truelight@4300: } truelight@4300: } truelight@4300: truelight@4300: ResetObjectToPlace(); rubidium@5564: SetLocalPlayer(_gw.lp); truelight@4300: truelight@4300: SetGeneratingWorldProgress(GWP_GAME_START, 1); truelight@4300: /* Call any callback */ truelight@4300: if (_gw.proc != NULL) _gw.proc(); truelight@4300: IncreaseGeneratingWorldProgress(GWP_GAME_START); truelight@4300: truelight@4300: if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE); truelight@4300: /* Show all vital windows again, because we have hidden them */ truelight@4303: if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows(); truelight@4300: _gw.active = false; truelight@4300: _gw.thread = NULL; truelight@4300: _gw.proc = NULL; truelight@4300: _gw.threaded = false; truelight@4300: truelight@4300: DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0); truelight@4300: MarkWholeScreenDirty(); truelight@4300: Darkvater@5568: if (_network_dedicated) DEBUG(net, 0, "Map generated, starting game"); truelight@4300: truelight@4300: return NULL; 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: */ truelight@4300: void WaitTillGeneratedWorld(void) truelight@4300: { truelight@4300: if (_gw.thread == NULL) return; truelight@4300: _gw.quit_thread = true; truelight@4300: OTTDJoinThread(_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: */ truelight@4300: void AbortGeneratingWorld(void) truelight@4300: { truelight@4300: _gw.abort = true; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Is the generation being aborted? truelight@4300: */ truelight@4300: bool IsGeneratingWorldAborted(void) 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: */ truelight@4300: void HandleGeneratingWorldAbortion(void) 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: truelight@4300: if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE); truelight@4300: /* Show all vital windows again, because we have hidden them */ truelight@4303: if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows(); truelight@4300: _gw.active = false; truelight@4300: _gw.thread = NULL; 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: truelight@4300: OTTDExitThread(); truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Generate a world. truelight@4300: * @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: */ truelight@4300: void GenerateWorld(int 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; truelight@4300: _gw.lp = _local_player; 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@5564: SetLocalPlayer(PLAYER_SPECTATOR); truelight@4300: /* Make sure everything is done via OWNER_NONE */ truelight@4300: _current_player = OWNER_NONE; truelight@4300: glx@5340: /* Set the date before loading sprites as some newgrfs check it */ glx@5340: SetDate(ConvertYMDToDate(_patches.starting_year, 0, 1)); glx@5340: peter1138@5155: /* Load the right landscape stuff */ peter1138@5155: GfxLoadSprites(); peter1138@5155: LoadStringWidthTable(); peter1138@5155: glx@5340: InitializeGame(IG_NONE, _gw.size_x, _gw.size_y); 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: Darkvater@5048: if (_network_dedicated || Darkvater@5048: (_gw.thread = OTTDCreateThread(&_GenerateWorld, NULL)) == NULL) { Darkvater@5568: 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) { truelight@4300: ScrollMainWindowToTile(TileXY(MapSizeX() / 2, MapSizeY() / 2)); truelight@4300: } truelight@4300: }