diff options
-rw-r--r-- | projects/openttd_vs100.vcxproj | 1 | ||||
-rw-r--r-- | projects/openttd_vs100.vcxproj.filters | 3 | ||||
-rw-r--r-- | projects/openttd_vs80.vcproj | 4 | ||||
-rw-r--r-- | projects/openttd_vs90.vcproj | 4 | ||||
-rw-r--r-- | source.list | 1 | ||||
-rw-r--r-- | src/bootstrap_gui.cpp | 269 | ||||
-rw-r--r-- | src/lang/english.txt | 5 | ||||
-rw-r--r-- | src/openttd.cpp | 30 | ||||
-rw-r--r-- | src/openttd.h | 1 | ||||
-rw-r--r-- | src/window.cpp | 4 | ||||
-rw-r--r-- | src/window_type.h | 1 |
11 files changed, 314 insertions, 9 deletions
diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index 70a10baef..8731a072e 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -635,6 +635,7 @@ <ClCompile Include="..\src\aircraft_gui.cpp" /> <ClCompile Include="..\src\airport_gui.cpp" /> <ClCompile Include="..\src\autoreplace_gui.cpp" /> + <ClCompile Include="..\src\bootstrap_gui.cpp" /> <ClCompile Include="..\src\bridge_gui.cpp" /> <ClCompile Include="..\src\build_vehicle_gui.cpp" /> <ClCompile Include="..\src\cheat_gui.cpp" /> diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index c8d1f3d19..19200a0cf 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -1125,6 +1125,9 @@ <ClCompile Include="..\src\autoreplace_gui.cpp"> <Filter>GUI Source Code</Filter> </ClCompile> + <ClCompile Include="..\src\bootstrap_gui.cpp"> + <Filter>GUI Source Code</Filter> + </ClCompile> <ClCompile Include="..\src\bridge_gui.cpp"> <Filter>GUI Source Code</Filter> </ClCompile> diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index d6d95483c..f12f56261 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -1823,6 +1823,10 @@ > </File> <File + RelativePath=".\..\src\bootstrap_gui.cpp" + > + </File> + <File RelativePath=".\..\src\bridge_gui.cpp" > </File> diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 6cb0b680a..082206aa2 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -1820,6 +1820,10 @@ > </File> <File + RelativePath=".\..\src\bootstrap_gui.cpp" + > + </File> + <File RelativePath=".\..\src\bridge_gui.cpp" > </File> diff --git a/source.list b/source.list index 3646b3d60..7c63b528a 100644 --- a/source.list +++ b/source.list @@ -387,6 +387,7 @@ core/string_compare_type.hpp aircraft_gui.cpp airport_gui.cpp autoreplace_gui.cpp +bootstrap_gui.cpp bridge_gui.cpp build_vehicle_gui.cpp cheat_gui.cpp diff --git a/src/bootstrap_gui.cpp b/src/bootstrap_gui.cpp new file mode 100644 index 000000000..63bde102b --- /dev/null +++ b/src/bootstrap_gui.cpp @@ -0,0 +1,269 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD 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, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file bootstrap_gui.cpp Barely used user interface for bootstrapping OpenTTD, i.e. downloading the required content. */ + +#include "stdafx.h" +#include "base_media_base.h" +#include "blitter/factory.hpp" +#include "core/geometry_func.hpp" +#include "fileio_func.h" +#include "fontcache.h" +#include "gfx_func.h" +#include "network/network.h" +#include "network/network_content_gui.h" +#include "openttd.h" +#include "strings_func.h" +#include "video/video_driver.hpp" +#include "window_func.h" +#include "window_gui.h" + +#include "table/strings.h" + +/** Widgets for the background window to prevent smearing. */ +static const struct NWidgetPart _background_widgets[] = { + NWidget(WWT_PANEL, COLOUR_DARK_BLUE, 0), SetResize(1, 1), +}; + +/** + * Window description for the background window to prevent smearing. + */ +static const WindowDesc _background_desc( + WDP_MANUAL, 0, 0, + WC_BOOTSTRAP, WC_NONE, + 0, + _background_widgets, lengthof(_background_widgets) +); + +/** The background for the game. */ +class BootstrapBackground : public Window { +public: + BootstrapBackground() : Window() + { + this->InitNested(&_background_desc, 0); + CLRBITS(this->flags4, WF_WHITE_BORDER_MASK); + ResizeWindow(this, _screen.width, _screen.height); + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + GfxFillRect(r.left, r.top, r.right, r.bottom, 4, FILLRECT_OPAQUE); + GfxFillRect(r.left, r.top, r.right, r.bottom, 0, FILLRECT_CHECKER); + } +}; + +/** Nested widgets for the download window. */ +static const NWidgetPart _nested_boostrap_download_status_window_widgets[] = { + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_CONTENT_DOWNLOAD_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_PANEL, COLOUR_GREY, NCDSWW_BACKGROUND), + NWidget(NWID_SPACER), SetMinimalSize(350, 0), SetMinimalTextLines(3, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 30), + EndContainer(), +}; + +/** Window description for the download window */ +static const WindowDesc _bootstrap_download_status_window_desc( + WDP_CENTER, 0, 0, + WC_NETWORK_STATUS_WINDOW, WC_NONE, + WDF_MODAL, + _nested_boostrap_download_status_window_widgets, lengthof(_nested_boostrap_download_status_window_widgets) +); + + +/** Window for showing the download status of content */ +struct BootstrapContentDownloadStatusWindow : public BaseNetworkContentDownloadStatusWindow { +public: + /** Simple call the constructor of the superclass. */ + BootstrapContentDownloadStatusWindow() : BaseNetworkContentDownloadStatusWindow(&_bootstrap_download_status_window_desc) + { + } + + virtual void OnDownloadComplete(ContentID cid) + { + /* We have completed downloading. We can trigger finding the right set now. */ + BaseGraphics::FindSets(); + + /* And continue going into the menu. */ + _game_mode = GM_MENU; + + /* _exit_game is used to break out of the outer video driver's MainLoop. */ + _exit_game = true; + delete this; + } +}; + +/** Widgets in the query window. */ +enum BootstrapAskForDownloadWidgets { + BAFDW_QUESTION, ///< The question whether to download. + BAFDW_YES, ///< An affirmative answer to the question. + BAFDW_NO, ///< An negative answer to the question. +}; + +/** The widgets for the query. It has no close box as that sprite does not exist yet. */ +static const NWidgetPart _bootstrap_query_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_MISSING_GRAPHICS_SET_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(WWT_PANEL, COLOUR_GREY, BAFDW_QUESTION), EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BAFDW_YES), SetDataTip(STR_MISSING_GRAPHICS_YES_DOWNLOAD, STR_NULL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BAFDW_NO), SetDataTip(STR_MISSING_GRAPHICS_NO_QUIT, STR_NULL), + EndContainer(), + EndContainer(), +}; + +/** The window description for the query. */ +static const WindowDesc _bootstrap_query_desc( + WDP_CENTER, 0, 0, + WC_CONFIRM_POPUP_QUERY, WC_NONE, + WDF_UNCLICK_BUTTONS, + _bootstrap_query_widgets, lengthof(_bootstrap_query_widgets) +); + +/** The window for the query. It can't use the generic query window as that uses sprites that don't exist yet. */ +class BootstrapAskForDownloadWindow : public Window, ContentCallback { + Dimension button_size; ///< The dimension of the button + +public: + /** Start listening to the content client events. */ + BootstrapAskForDownloadWindow() : Window() + { + this->InitNested(&_bootstrap_query_desc); + _network_content_client.AddCallback(this); + } + + /** Stop listening to the content client events. */ + ~BootstrapAskForDownloadWindow() + { + _network_content_client.RemoveCallback(this); + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + /* We cache the button size. This is safe as no reinit can happen here. */ + if (this->button_size.width == 0) { + this->button_size = maxdim(GetStringBoundingBox(STR_MISSING_GRAPHICS_YES_DOWNLOAD), GetStringBoundingBox(STR_MISSING_GRAPHICS_NO_QUIT)); + this->button_size.width += WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT; + this->button_size.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM; + } + + switch (widget) { + case BAFDW_QUESTION: + /* The question is twice as wide as the buttons, and determine the height based on the width. */ + size->width = this->button_size.width * 2; + size->height = GetStringHeight(STR_MISSING_GRAPHICS_SET_MESSAGE, size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT) + WD_FRAMETEXT_BOTTOM + WD_FRAMETEXT_TOP; + break; + + case BAFDW_YES: + case BAFDW_NO: + *size = this->button_size; + break; + } + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + if (widget != 0) return; + + DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMETEXT_TOP, r.bottom - WD_FRAMETEXT_BOTTOM, STR_MISSING_GRAPHICS_SET_MESSAGE, TC_FROMSTRING, SA_CENTER); + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + switch (widget) { + case BAFDW_YES: + /* We got permission to connect! Yay! */ + _network_content_client.Connect(); + break; + + case BAFDW_NO: + _exit_game = true; + break; + + default: + break; + } + } + + virtual void OnConnect(bool success) + { + /* Once connected, request the metadata. */ + _network_content_client.RequestContentList(CONTENT_TYPE_BASE_GRAPHICS); + } + + virtual void OnReceiveContentInfo(const ContentInfo *ci) + { + /* And once the meta data is received, start downloading it. */ + _network_content_client.Select(ci->id); + new BootstrapContentDownloadStatusWindow(); + delete this; + } +}; + +/** + * Handle all procedures for bootstrapping OpenTTD without a base grapics set. + * This requires all kinds of trickery that is needed to avoid the use of + * sprites from the base graphics set which are pretty interwoven. + * @return True if a base set exists, otherwise false. + */ +bool HandleBootstrap() +{ + if (BaseGraphics::GetUsedSet() != NULL) return true; + + /* No user interface, bail out with an error. */ + if (BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 0) goto failure; + + /* If there is no network or no freetype, then there is nothing we can do. Go straight to failure. */ +#if defined(ENABLE_NETWORK) && defined(WITH_FREETYPE) && !defined(__APPLE__) + if (!_network_available) goto failure; + + /* First tell the game we're bootstrapping. */ + _game_mode = GM_BOOTSTRAP; + + /* Initialise the freetype font code. */ + InitializeUnicodeGlyphMap(); + /* Next "force" finding a suitable freetype font as the local font is missing. */ + CheckForMissingGlyphsInLoadedLanguagePack(false); + + /* Initialise the palette. The biggest step is 'faking' some recolour sprites. + * This way the mauve and gray colours work and we can show the user interface. */ + GfxInitPalettes(); + static const int offsets[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0x04, 0x08 }; + for (uint i = 0; i != 16; i++) { + for (int j = 0; j < 8; j++) { + _colour_gradient[i][j] = offsets[i] + j; + } + } + + /* Finally ask the question. */ + new BootstrapBackground(); + new BootstrapAskForDownloadWindow(); + + /* Process the user events. */ + _video_driver->MainLoop(); + + /* _exit_game is used to get out of the video driver's main loop. + * In case GM_BOOTSTRAP is still set we did not exit it via the + * "download complete" event, so it was a manual exit. Obey it. */ + _exit_game = _game_mode == GM_BOOTSTRAP; + if (_exit_game) return false; + + /* Try to probe the graphics. Should work this time. */ + if (!BaseGraphics::SetSet(NULL)) goto failure; + + /* Finally we can continue heading for the menu. */ + _game_mode = GM_MENU; + return true; +#endif + + /* Failure to get enough working to get a graphics set. */ +failure: + usererror("Failed to find a graphics set. Please acquire a graphics set for OpenTTD. See section 4.1 of readme.txt."); + return false; +} diff --git a/src/lang/english.txt b/src/lang/english.txt index d620f47e7..acc2ea710 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1875,6 +1875,11 @@ STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_CONNECTION_LOST :{WHITE}... conn STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE :{WHITE}... file not writable STR_CONTENT_ERROR_COULD_NOT_EXTRACT :{WHITE}Could not decompress the downloaded file +STR_MISSING_GRAPHICS_SET_CAPTION :{WHITE}Missing graphics +STR_MISSING_GRAPHICS_SET_MESSAGE :{BLACK}OpenTTD requires graphics to function but none could be found. Do you allow OpenTTD to download and install these graphics? +STR_MISSING_GRAPHICS_YES_DOWNLOAD :{BLACK}Yes, download the graphics +STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}No, quit OpenTTD + # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}Transparency Options STR_TRANSPARENT_SIGNS_TOOLTIP :{BLACK}Toggle transparency for station signs. Ctrl+Click to lock diff --git a/src/openttd.cpp b/src/openttd.cpp index 00ddade4b..7f699b234 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -79,6 +79,7 @@ void DoPaletteAnimations(); void MusicLoop(); void ResetMusic(); void CallWindowTickEvent(); +bool HandleBootstrap(); extern void SetDifficultyLevel(int mode, DifficultySettings *gm_opt); extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY); @@ -297,7 +298,8 @@ static void ShutdownGame() PoolBase::Clean(PT_ALL); - ResetNewGRFData(); + /* No NewGRFs were loaded when it was still bootstrapping. */ + if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData(); /* Close all and any open filehandles */ FioCloseAll(); @@ -717,10 +719,8 @@ int ttd_main(int argc, char *argv[]) free(sounds_set); if (graphics_set == NULL && BaseGraphics::ini_set != NULL) graphics_set = strdup(BaseGraphics::ini_set); - if (!BaseGraphics::SetSet(graphics_set)) { - StrEmpty(graphics_set) ? - usererror("Failed to find a graphics set. Please acquire a graphics set for OpenTTD. See section 4.1 of readme.txt.") : - usererror("Failed to select requested graphics set '%s'", graphics_set); + if (!BaseGraphics::SetSet(graphics_set) && !StrEmpty(graphics_set)) { + usererror("Failed to select requested graphics set '%s'", graphics_set); } free(graphics_set); @@ -739,7 +739,7 @@ int ttd_main(int argc, char *argv[]) if (blitter == NULL && _ini_blitter != NULL) blitter = strdup(_ini_blitter); _blitter_autodetected = StrEmpty(blitter); /* If we have a 32 bpp base set, try to select the 32 bpp blitter first, but only if we autoprobe the blitter. */ - if (!_blitter_autodetected || BaseGraphics::GetUsedSet()->blitter == BLT_8BPP || BlitterFactoryBase::SelectBlitter("32bpp-anim") == NULL) { + if (!_blitter_autodetected || BaseGraphics::GetUsedSet() == NULL || BaseGraphics::GetUsedSet()->blitter == BLT_8BPP || BlitterFactoryBase::SelectBlitter("32bpp-anim") == NULL) { if (BlitterFactoryBase::SelectBlitter(blitter) == NULL) { StrEmpty(blitter) ? usererror("Failed to autoprobe blitter") : @@ -780,12 +780,14 @@ int ttd_main(int argc, char *argv[]) /* Initialize the zoom level of the screen to normal */ _screen.zoom = ZOOM_LVL_NORMAL; + NetworkStartUp(); // initialize network-core + + if (!HandleBootstrap()) goto exit; + /* restore saved music volume */ _music_driver->SetVolume(_settings_client.music.music_vol); _video_driver->ClaimMousePointer(); - NetworkStartUp(); // initialize network-core - #if defined(ENABLE_NETWORK) if (debuglog_conn != NULL && _network_available) { const char *not_used = NULL; @@ -825,6 +827,7 @@ int ttd_main(int argc, char *argv[]) SaveToHighScore(); } +exit: /* Reset windowing system, stop drivers, free used memory, ... */ ShutdownGame(); @@ -841,7 +844,7 @@ int ttd_main(int argc, char *argv[]) void HandleExitGameRequest() { - if (_game_mode == GM_MENU) { // do not ask to quit on the main screen + if (_game_mode == GM_MENU || _game_mode == GM_BOOTSTRAP) { // do not ask to quit on the main screen _exit_game = true; } else if (_settings_client.gui.autosave_on_exit) { DoExitSave(); @@ -1317,6 +1320,15 @@ static void DoAutosave() void GameLoop() { + if (_game_mode == GM_BOOTSTRAP) { +#ifdef ENABLE_NETWORK + /* Check for UDP stuff */ + if (_network_available) NetworkUDPGameLoop(); +#endif + InputLoop(); + return; + } + ProcessAsyncSaveFinish(); /* autosave game? */ diff --git a/src/openttd.h b/src/openttd.h index 40a78c48c..7fcd1ceaa 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -19,6 +19,7 @@ enum GameMode { GM_MENU, GM_NORMAL, GM_EDITOR, + GM_BOOTSTRAP }; /** Mode which defines what mode we're switching to. */ diff --git a/src/window.cpp b/src/window.cpp index 02b1b918d..bc61d0c73 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2786,6 +2786,10 @@ void RelocateAllWindows(int neww, int newh) /* XXX - this probably needs something more sane. For example specifying * in a 'backup'-desc that the window should always be centered. */ switch (w->window_class) { + case WC_BOOTSTRAP: + ResizeWindow(w, neww, newh); + continue; + case WC_MAIN_TOOLBAR: ResizeWindow(w, min(neww, *_preferred_toolbar_size) - w->width, 0); diff --git a/src/window_type.h b/src/window_type.h index 1a078b217..b003cfea8 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -84,6 +84,7 @@ enum WindowClass { WC_CHEATS, WC_PERFORMANCE_DETAIL, WC_CONSOLE, + WC_BOOTSTRAP, WC_EXTRA_VIEW_PORT, WC_CLIENT_LIST, WC_NETWORK_STATUS_WINDOW, |