summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--projects/openttd_vs100.vcxproj1
-rw-r--r--projects/openttd_vs100.vcxproj.filters3
-rw-r--r--projects/openttd_vs80.vcproj4
-rw-r--r--projects/openttd_vs90.vcproj4
-rw-r--r--source.list1
-rw-r--r--src/bootstrap_gui.cpp269
-rw-r--r--src/lang/english.txt5
-rw-r--r--src/openttd.cpp30
-rw-r--r--src/openttd.h1
-rw-r--r--src/window.cpp4
-rw-r--r--src/window_type.h1
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,