From 7a5cedf078cf39d56cf6031d26619b2c41f2e934 Mon Sep 17 00:00:00 2001 From: zuu Date: Sun, 9 Jun 2013 12:48:27 +0000 Subject: [PATCH] (svn r25344) -Feature: GUI for viewing story pages --- bin/baseset/openttd.grf | Bin 824193 -> 825364 bytes bin/baseset/orig_dos.obg | 2 +- bin/baseset/orig_dos_de.obg | 2 +- bin/baseset/orig_win.obg | 2 +- media/extra_grf/openttdgui.nfo | 7 +- media/extra_grf/openttdgui.png | Bin 25163 -> 26280 bytes projects/openttd_vs100.vcxproj | 2 + projects/openttd_vs100.vcxproj.filters | 6 + projects/openttd_vs80.vcproj | 8 + projects/openttd_vs90.vcproj | 8 + source.list | 2 + src/gui.h | 1 + src/lang/english.txt | 11 + src/script/api/game/game_window.hpp.sq | 7 + src/script/api/script_window.hpp | 17 + .../api/template/template_window.hpp.sq | 2 + src/story.cpp | 10 + src/story_gui.cpp | 731 ++++++++++++++++++ src/table/sprites.h | 9 +- src/toolbar_gui.cpp | 9 +- src/widgets/story_widget.h | 26 + src/window_type.h | 5 + 22 files changed, 858 insertions(+), 9 deletions(-) create mode 100644 src/story_gui.cpp create mode 100644 src/widgets/story_widget.h diff --git a/bin/baseset/openttd.grf b/bin/baseset/openttd.grf index afee09e4fd0fc08557b94ed58ef086fa37d9bf54..86b70789f0052ec79fb95e536a901bee069b1e3a 100644 GIT binary patch delta 1234 zcmZvbPiz%M9LL}6{$_V)cVB4pcBL9n*=Jy(sHFx7p$)H14Bb*dn&`m;iBNmsPwmco zk5s$Eqb7~7pjYXiCPsffc=N)6=road@%zg+`OPFV z^PA87X6f7E&pN{Zg}*;vf@L7fU@U{V4Dch+<4?%<_okoF7e2mm{}n4}r~!IzQ>`8L z1+8;%PT8G)OUw3LwDPU8-*G)H+w*W6v#cG33!d-!2&QK{7~(eKs=)MoA4|+?ghP=_^mj9!}e{< zVXoU9Q8Rdof^E5OXGX6o`nX^++wLsL5g8klb^^6PXeY*NazqbtrWG4Se?9Ha7=;4< z@Y9DITXb@hkRT{*YU_#PC+hWDy*5>=HEOlmvFhX`&+7F$nyTLsHBqe|yO?1xJoVn? zvM5Jo9#t=ji@eGw`P5uBx{$)WIS&)SqbTE?iztKCoG|CpMRaHiX~c2w)+m`zA&DX? zQqB`IrMxTz6*v^|ED}}9X_ltEQ56x_L2)-^lnXpqf{aI5l+7PKx(9fYrXmt}hHyOV z853ZXqy0t}nK*9fQ%?I6Ny4K2gdAY=2`;=OqBKciH)BRb4j7Cbg+(5TG+Uf76V_+! zH?FV*Z{RfEE+tvU1#Os%LS!_9EQ=CAyB{*KmkJt*ObDKd$z+2OfuDlV!fWaa+=su& zJ~g0PF0YHKs2&rdj1C^c5rd13cU&RnF4x4In5!aH&u-M$XCIzlzp`=uKmHb2pq~NF z+>#C70;d81YXj2N>G)V>tT$i+ROA5+t>N+E@yZy!szg&GERsL)Ku=`i{&(+7hU}gKtP;+;4bmi^l z^rJaqeY13vG@dqv`LrV6)a5Bl(Y#N$CV(RYLe(>>k5Kdo%>z}`v&_*Ym2d=K1eZAs zcM;kc}+=*|>tGJJNm|$h?lH7tP8o0M}N!=g} z&#JF_SJgq8to%CI%kB5FIxU;PpG>|dHT zI4;wXZ{lQ|IKgv0S8ABOQ5BlHZ0R)CVso(VVjzcr!L7EOChgVGyw$#&kLu4ox>m~H z?S{CsJ1aw+#Y5feSS3sy!6|*O>*|3C?Lk?}@1l~wleX>xO?%zOWn-}2d8}f+<6r^9 z@3zYlDYQZZoV?Tu@wdFRx~Eg>95J2tpmk@-xI6e!x?*YD+Kx8UcD22G>5AXy{sTGn Bq>2Cl delta 68 zcmbQT!KiV$5eo~$|A~z(tt^bKEKIE|%&jaeTUl6r|1z%J9`u)0zNej~j}3^~ftUk` RIf0l9h`G13^zjIA0RZrM6ubZc diff --git a/bin/baseset/orig_dos.obg b/bin/baseset/orig_dos.obg index 593257fb64..16cf83a7ff 100644 --- a/bin/baseset/orig_dos.obg +++ b/bin/baseset/orig_dos.obg @@ -64,7 +64,7 @@ TRGI.GRF = da6a6c9dcc451eec88d79211437b76a8 TRGC.GRF = ed446637e034104c5559b32c18afe78d TRGH.GRF = ee6616fb0e6ef6b24892c58c93d86fc9 TRGT.GRF = e30e8a398ae86c03dc534a8ac7dfb3b6 -OPENTTD.GRF = 9285f720c9c6805b4adcfcbf869a7deb +OPENTTD.GRF = 63d40d4bc6cfb3546cab72d94614e6cd [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/bin/baseset/orig_dos_de.obg b/bin/baseset/orig_dos_de.obg index aa8112d5fb..a2132dde9e 100644 --- a/bin/baseset/orig_dos_de.obg +++ b/bin/baseset/orig_dos_de.obg @@ -64,7 +64,7 @@ TRGI.GRF = da6a6c9dcc451eec88d79211437b76a8 TRGC.GRF = ed446637e034104c5559b32c18afe78d TRGH.GRF = ee6616fb0e6ef6b24892c58c93d86fc9 TRGT.GRF = fcde1d7e8a74197d72a62695884b909e -OPENTTD.GRF = 9285f720c9c6805b4adcfcbf869a7deb +OPENTTD.GRF = 63d40d4bc6cfb3546cab72d94614e6cd [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/bin/baseset/orig_win.obg b/bin/baseset/orig_win.obg index 8d50bbca4c..bc3411ba83 100644 --- a/bin/baseset/orig_win.obg +++ b/bin/baseset/orig_win.obg @@ -64,7 +64,7 @@ TRGIR.GRF = 0c2484ff6be49fc63a83be6ab5c38f32 TRGCR.GRF = 3668f410c761a050b5e7095a2b14879b TRGHR.GRF = 06bf2b7a31766f048baac2ebe43457b1 TRGTR.GRF = de53650517fe661ceaa3138c6edb0eb8 -OPENTTD.GRF = 9285f720c9c6805b4adcfcbf869a7deb +OPENTTD.GRF = 63d40d4bc6cfb3546cab72d94614e6cd [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/media/extra_grf/openttdgui.nfo b/media/extra_grf/openttdgui.nfo index 33ad4cb8b4..1d96de8558 100644 --- a/media/extra_grf/openttdgui.nfo +++ b/media/extra_grf/openttdgui.nfo @@ -7,7 +7,7 @@ // 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 . // -1 * 0 0C "OpenTTD GUI graphics" - -1 * 3 05 15 \b 169 // OPENTTD_SPRITE_COUNT + -1 * 3 05 15 \b 174 // OPENTTD_SPRITE_COUNT -1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal @@ -177,3 +177,8 @@ -1 sprites/openttdgui.png 8bpp 312 440 14 10 0 0 normal -1 sprites/openttdgui.png 8bpp 328 440 14 10 0 0 normal -1 sprites/openttdgui.png 8bpp 348 440 8 8 0 0 normal + -1 sprites/openttdgui.png 8bpp 362 440 20 20 0 0 normal + -1 sprites/openttdgui.png 8bpp 388 440 20 20 0 0 normal + -1 sprites/openttdgui.png 8bpp 414 440 20 20 0 0 normal + -1 sprites/openttdgui.png 8bpp 440 440 20 20 0 0 normal + -1 sprites/openttdgui.png 8bpp 466 440 20 20 0 0 normal diff --git a/media/extra_grf/openttdgui.png b/media/extra_grf/openttdgui.png index f03033d2968c39663f2d0dd124c379f6d48f4fff..6f011b654dadfa41a8902be5284f647ec0391866 100644 GIT binary patch delta 25636 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 z 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 a?lIKI@4*E85EBgnf2Nnrjb0nLMEwueht((m diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index 1a772c6e8f..41aeeabacc 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -705,6 +705,7 @@ + @@ -763,6 +764,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index c521cc6d82..f2900c846f 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -1344,6 +1344,9 @@ GUI Source Code + + GUI Source Code + GUI Source Code @@ -1518,6 +1521,9 @@ Widgets + + Widgets + Widgets diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index d6d806212d..5c866b99dd 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -2102,6 +2102,10 @@ RelativePath=".\..\src\statusbar_gui.cpp" > + + @@ -2338,6 +2342,10 @@ RelativePath=".\..\src\widgets\statusbar_widget.h" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 8219894e7a..96120ed473 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -2099,6 +2099,10 @@ RelativePath=".\..\src\statusbar_gui.cpp" > + + @@ -2335,6 +2339,10 @@ RelativePath=".\..\src\widgets\statusbar_widget.h" > + + diff --git a/source.list b/source.list index 1a72fbd8ba..be324f335f 100644 --- a/source.list +++ b/source.list @@ -457,6 +457,7 @@ signs_gui.cpp smallmap_gui.cpp station_gui.cpp statusbar_gui.cpp +story_gui.cpp subsidy_gui.cpp terraform_gui.cpp textfile_gui.cpp @@ -517,6 +518,7 @@ widgets/sign_widget.h widgets/smallmap_widget.h widgets/station_widget.h widgets/statusbar_widget.h +widgets/story_widget.h widgets/subsidy_widget.h widgets/terraform_widget.h widgets/timetable_widget.h diff --git a/src/gui.h b/src/gui.h index 8b31c6c984..79c33e0b37 100644 --- a/src/gui.h +++ b/src/gui.h @@ -51,6 +51,7 @@ void ShowIndustryCargoesWindow(); void ShowSubsidiesList(); void ShowGoalsList(); void ShowGoalQuestion(uint16 id, byte type, uint32 button_mask, const char *question); +void ShowStoryBook(); void ShowEstimatedCostOrIncome(Money cost, int x, int y); diff --git a/src/lang/english.txt b/src/lang/english.txt index 6e5b8eeb14..9fef3802d0 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -399,6 +399,7 @@ STR_GRAPH_MENU_CARGO_PAYMENT_RATES :Cargo payment r ############ range for company league menu starts STR_GRAPH_MENU_GOAL :Current goals +STR_GRAPH_MENU_STORY_BOOK :Story book STR_GRAPH_MENU_COMPANY_LEAGUE_TABLE :Company league table STR_GRAPH_MENU_DETAILED_PERFORMANCE_RATING :Detailed performance rating STR_GRAPH_MENU_HIGHSCORE :Highscore table @@ -2947,6 +2948,16 @@ STR_SUBSIDIES_SUBSIDISED_TITLE :{BLACK}Services STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} ({COMPANY}{YELLOW}, until {DATE_SHORT}) STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Click on service to centre main view on industry/town. Ctrl+Click opens a new viewport on industry/town location +# Story book window +STR_STORY_BOOK_CAPTION :{WHITE}Story Book +STR_STORY_BOOK_TITLE :{YELLOW}{RAW_STRING} +STR_STORY_BOOK_GENERIC_PAGE_ITEM :Page {NUM} +STR_STORY_BOOK_SEL_PAGE_TOOLTIP :{BLACK}Jump to a specific page by selecting it in this drop down list. +STR_STORY_BOOK_PREV_PAGE :{BLACK}Previous +STR_STORY_BOOK_PREV_PAGE_TOOLTIP :{BLACK}Go to previous page +STR_STORY_BOOK_NEXT_PAGE :{BLACK}Next +STR_STORY_BOOK_NEXT_PAGE_TOOLTIP :{BLACK}Go to next page + # Station list window STR_STATION_LIST_TOOLTIP :{BLACK}Station names - click on name to centre main view on station. Ctrl+Click opens a new viewport on station location STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE :{BLACK}Hold Ctrl to select more than one item diff --git a/src/script/api/game/game_window.hpp.sq b/src/script/api/game/game_window.hpp.sq index 01811a2125..73b4d4bac9 100644 --- a/src/script/api/game/game_window.hpp.sq +++ b/src/script/api/game/game_window.hpp.sq @@ -76,6 +76,7 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WC_SIGN_LIST, "WC_SIGN_LIST"); SQGSWindow.DefSQConst(engine, ScriptWindow::WC_AI_LIST, "WC_AI_LIST"); SQGSWindow.DefSQConst(engine, ScriptWindow::WC_GOALS_LIST, "WC_GOALS_LIST"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WC_STORY_BOOK, "WC_STORY_BOOK"); SQGSWindow.DefSQConst(engine, ScriptWindow::WC_STATION_LIST, "WC_STATION_LIST"); SQGSWindow.DefSQConst(engine, ScriptWindow::WC_TRAINS_LIST, "WC_TRAINS_LIST"); SQGSWindow.DefSQConst(engine, ScriptWindow::WC_ROADVEH_LIST, "WC_ROADVEH_LIST"); @@ -1084,6 +1085,12 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_S_LEFT, "WID_S_LEFT"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_S_MIDDLE, "WID_S_MIDDLE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_S_RIGHT, "WID_S_RIGHT"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SB_CAPTION, "WID_SB_CAPTION"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SB_SEL_PAGE, "WID_SB_SEL_PAGE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SB_PAGE_PANEL, "WID_SB_PAGE_PANEL"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SB_SCROLLBAR, "WID_SB_SCROLLBAR"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SB_PREV_PAGE, "WID_SB_PREV_PAGE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SB_NEXT_PAGE, "WID_SB_NEXT_PAGE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SUL_PANEL, "WID_SUL_PANEL"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SUL_SCROLLBAR, "WID_SUL_SCROLLBAR"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_SHOW_PLACE_OBJECT, "WID_TT_SHOW_PLACE_OBJECT"); diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp index 1fae2bfae8..e9e10e3ed5 100644 --- a/src/script/api/script_window.hpp +++ b/src/script/api/script_window.hpp @@ -69,6 +69,7 @@ #include "../../widgets/viewport_widget.h" #include "../../widgets/waypoint_widget.h" #include "../../widgets/link_graph_legend_widget.h" +#include "../../widgets/story_widget.h" /** * Class that handles window interaction. A Window in OpenTTD has two imporant @@ -366,6 +367,11 @@ public: */ WC_GOALS_LIST = ::WC_GOALS_LIST, + /** + * Story book; %Window numbers: + * - CompanyID = #StoryBookWidgets + */ + WC_STORY_BOOK = ::WC_STORY_BOOK, /** * Station list; %Window numbers: @@ -2241,6 +2247,17 @@ public: WID_S_RIGHT = ::WID_S_RIGHT, ///< Right part; bank balance. }; + /* automatically generated from ../../widgets/story_widget.h */ + /** Widgets of the #GoalListWindow class. */ + enum StoryBookWidgets { + WID_SB_CAPTION = ::WID_SB_CAPTION, ///< Caption of the window. + WID_SB_SEL_PAGE = ::WID_SB_SEL_PAGE, ///< Page selector. + WID_SB_PAGE_PANEL = ::WID_SB_PAGE_PANEL, ///< Page body. + WID_SB_SCROLLBAR = ::WID_SB_SCROLLBAR, ///< Scrollbar of the goal list. + WID_SB_PREV_PAGE = ::WID_SB_PREV_PAGE, ///< Prev button. + WID_SB_NEXT_PAGE = ::WID_SB_NEXT_PAGE, ///< Next button. + }; + /* automatically generated from ../../widgets/subsidy_widget.h */ /** Widgets of the #SubsidyListWindow class. */ enum SubsidyListWidgets { diff --git a/src/script/api/template/template_window.hpp.sq b/src/script/api/template/template_window.hpp.sq index 238c403c2e..9f01e42d5a 100644 --- a/src/script/api/template/template_window.hpp.sq +++ b/src/script/api/template/template_window.hpp.sq @@ -209,6 +209,8 @@ namespace SQConvert { template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::JoinStationWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::StatusbarWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::StatusbarWidgets)tmp; } template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::StatusbarWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline ScriptWindow::StoryBookWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::StoryBookWidgets)tmp; } + template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::StoryBookWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::SubsidyListWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::SubsidyListWidgets)tmp; } template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::SubsidyListWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::TerraformToolbarWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::TerraformToolbarWidgets)tmp; } diff --git a/src/story.cpp b/src/story.cpp index 3574cf5252..e492ded4da 100644 --- a/src/story.cpp +++ b/src/story.cpp @@ -20,6 +20,7 @@ #include "tile_map.h" #include "goal_type.h" #include "goal_base.h" +#include "window_func.h" StoryPageElementID _new_story_page_element_id; @@ -123,6 +124,8 @@ CommandCost CmdCreateStoryPage(TileIndex tile, DoCommandFlag flags, uint32 p1, u s->title = strdup(text); } + InvalidateWindowData(WC_STORY_BOOK, -1); + _new_story_page_id = s->index; _story_page_next_sort_value++; } @@ -172,6 +175,8 @@ CommandCost CmdCreateStoryPageElement(TileIndex tile, DoCommandFlag flags, uint3 pe->page = page_id; UpdateElement(*pe, tile, p2, text); + InvalidateWindowData(WC_STORY_BOOK, page_id); + _new_story_page_element_id = pe->index; _story_page_element_next_sort_value++; } @@ -205,6 +210,7 @@ CommandCost CmdUpdateStoryPageElement(TileIndex tile, DoCommandFlag flags, uint3 if (flags & DC_EXEC) { UpdateElement(*pe, tile, p2, text); + InvalidateWindowData(WC_STORY_BOOK, pe->page); } return CommandCost(); @@ -233,6 +239,8 @@ CommandCost CmdSetStoryPageTitle(TileIndex tile, DoCommandFlag flags, uint32 p1, } else { p->title = strdup(text); } + + InvalidateWindowData(WC_STORY_BOOK, page_id); } return CommandCost(); @@ -264,6 +272,8 @@ CommandCost CmdRemoveStoryPage(TileIndex tile, DoCommandFlag flags, uint32 p1, u } delete p; + + InvalidateWindowData(WC_STORY_BOOK, -1); } return CommandCost(); diff --git a/src/story_gui.cpp b/src/story_gui.cpp new file mode 100644 index 0000000000..62ec3606a8 --- /dev/null +++ b/src/story_gui.cpp @@ -0,0 +1,731 @@ +/* $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 . + */ + +/** @file goal_gui.cpp GUI for goals. */ + +#include "stdafx.h" +#include "window_gui.h" +#include "strings_func.h" +#include "date_func.h" +#include "gui.h" +#include "story_base.h" +#include "core/geometry_func.hpp" +#include "company_func.h" +#include "command_func.h" +#include "widgets/dropdown_type.h" +#include "widgets/dropdown_func.h" +#include "sortlist_type.h" +#include "goal_base.h" +#include "viewport_func.h" + +#include "widgets/story_widget.h" + +#include "table/strings.h" +#include "table/sprites.h" + +typedef GUIList GUIStoryPageList; +typedef GUIList GUIStoryPageElementList; + +struct StoryBookWindow : Window { +protected: + Scrollbar *vscroll; ///< Scrollbar of the page text. + + GUIStoryPageList story_pages; ///< Sorted list of pages. + GUIStoryPageElementList story_page_elements; ///< Sorted list of page elements that belong to the current page. + StoryPageID selected_page_id; ///< Pool index of selected page. + char selected_generic_title[255]; ///< If the selected page doesn't have a custom title, this buffer is used to store a generic page title. + + static GUIStoryPageList::SortFunction * const page_sorter_funcs[]; + static GUIStoryPageElementList::SortFunction * const page_element_sorter_funcs[]; + + /** (Re)Build story page list. */ + void BuildStoryPageList() + { + if (this->story_pages.NeedRebuild()) { + this->story_pages.Clear(); + + const StoryPage *p; + FOR_ALL_STORY_PAGES(p) { + if (this->IsPageAvailable(p)) { + *this->story_pages.Append() = p; + } + } + + this->story_pages.Compact(); + this->story_pages.RebuildDone(); + } + + this->story_pages.Sort(); + } + + /** Sort story pages by order value. */ + static int CDECL PageOrderSorter(const StoryPage * const *a, const StoryPage * const *b) + { + return (*a)->sort_value - (*b)->sort_value; + } + + /** (Re)Build story page element list. */ + void BuildStoryPageElementList() + { + if (this->story_page_elements.NeedRebuild()) { + this->story_page_elements.Clear(); + + const StoryPage *p = GetSelPage(); + if (p != NULL) { + const StoryPageElement *pe; + FOR_ALL_STORY_PAGE_ELEMENTS(pe) { + if (pe->page == p->index) { + *this->story_page_elements.Append() = pe; + } + } + } + + this->story_page_elements.Compact(); + this->story_page_elements.RebuildDone(); + } + + this->story_page_elements.Sort(); + } + + /** Sort story page elements by order value. */ + static int CDECL PageElementOrderSorter(const StoryPageElement * const *a, const StoryPageElement * const *b) + { + return (*a)->sort_value - (*b)->sort_value; + } + + /* + * Checks if a given page should be visible in the story book. + * @param page The page to check. + * @return True if the page should be visible, otherwise false. + */ + bool IsPageAvailable(const StoryPage *page) const + { + return page->company == INVALID_COMPANY || page->company == _local_company; + } + + /** + * Get instance of selected page. + * @return Instance of selected page or NULL if no page is selected. + */ + StoryPage *GetSelPage() const + { + if (!_story_page_pool.IsValidID(selected_page_id)) return NULL; + return _story_page_pool.Get(selected_page_id); + } + + /** + * Get the page number of selected page. + * @return Number of available pages before to the selected one, or -1 if no page is selected. + */ + int GetSelPageNum() const + { + int page_number = 0; + for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) { + const StoryPage *p = *iter; + if (p->index == this->selected_page_id) { + return page_number; + } + page_number++; + } + return -1; + } + + /** + * Check if the selected page is also the first available page. + */ + bool IsFirstPageSelected() + { + /* Verify that the selected page exist. */ + if (!_story_page_pool.IsValidID(this->selected_page_id)) return false; + + return (*this->story_pages.Begin())->index == this->selected_page_id; + } + + /** + * Check if the selected page is also the last available page. + */ + bool IsLastPageSelected() + { + /* Verify that the selected page exist. */ + if (!_story_page_pool.IsValidID(this->selected_page_id)) return false; + + if (this->story_pages.Length() <= 1) return true; + const StoryPage *last = *(this->story_pages.End() - 1); + return last->index == this->selected_page_id; + } + + /** + * Updates the content of selected page. + */ + void RefreshSelectedPage() + { + /* Generate generic title if selected page have no custom title. */ + StoryPage *page = this->GetSelPage(); + if (page != NULL && page->title == NULL) { + SetDParam(0, GetSelPageNum() + 1); + GetString(selected_generic_title, STR_STORY_BOOK_GENERIC_PAGE_ITEM, lastof(selected_generic_title)); + } + + this->story_page_elements.ForceRebuild(); + this->BuildStoryPageElementList(); + + this->vscroll->SetCount(this->CountLines()); + this->SetWidgetDirty(WID_SB_SCROLLBAR); + this->SetWidgetDirty(WID_SB_SEL_PAGE); + this->SetWidgetDirty(WID_SB_PAGE_PANEL); + } + + /** + * Selects the previous available page before the currently selected page. + */ + void SelectPrevPage() + { + if (!_story_page_pool.IsValidID(this->selected_page_id)) return; + + /* Find the last available page which is previous to the current selected page. */ + const StoryPage *last_available; + last_available = NULL; + for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) { + const StoryPage *p = *iter; + if (p->index == this->selected_page_id) { + if (last_available == NULL) return; // No previous page available. + this->SetSelectedPage(last_available->index); + return; + } + last_available = p; + } + } + + /** + * Selects the next available page after the currently selected page. + */ + void SelectNextPage() + { + if (!_story_page_pool.IsValidID(this->selected_page_id)) return; + + /* Find selected page. */ + for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) { + const StoryPage *p = *iter; + if (p->index == this->selected_page_id) { + /* Select the page after selected page. */ + iter++; + if (iter != this->story_pages.End()) { + this->SetSelectedPage((*iter)->index); + } + return; + } + } + } + + /** + * Builds the page selector drop down list. + */ + DropDownList *BuildDropDownList() const + { + DropDownList *list = new DropDownList(); + uint16 page_num = 1; + for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) { + const StoryPage *p = *iter; + char *title = p->title; + bool current_page = p->index == this->selected_page_id; + DropDownListStringItem *item = NULL; + if (p->title != NULL) { + item = new DropDownListCharStringItem(p->title, p->index, current_page); + } else { + /* No custom title => use a generic page title with page number. */ + DropDownListParamStringItem *str_item = + new DropDownListParamStringItem(STR_STORY_BOOK_GENERIC_PAGE_ITEM, p->index, current_page); + str_item->SetParam(0, page_num); + item = str_item; + } + + list->push_back(item); + page_num++; + } + + /* Check if list is empty. */ + if (list->size() == 0) { + free(list); + list = NULL; + } + + return list; + } + + /** + * Get the width available for displaying content on the page panel. + */ + uint GetAvailablePageContentWidth() + { + return this->GetWidget(WID_SB_PAGE_PANEL)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT; + } + + /** + * Counts how many lines that are used by Date and Title + * (excluding marginal after Title, as each body element has + * an empty row before the elment). + * @param max_width Available width to display content. + * @return the number of lines. + */ + uint CountHeadLines(int max_width) + { + StoryPage *page = this->GetSelPage(); + if (page == NULL) return 0; + int num_lines = 0; + + /* Title lines */ + num_lines += 1; // Date always use exactly one line. + SetDParamStr(0, page->title != NULL ? page->title : this->selected_generic_title); + num_lines += GetStringLineCount(STR_STORY_BOOK_TITLE, max_width); + + return num_lines; + } + + /** + * Decides which sprite to display for a given page element. + * @param pe The page element. + * @return The SpriteID of the sprite to display. + * @pre pe.type must be SPET_GOAL or SPET_LOCATION. + */ + SpriteID GetPageElementSprite(const StoryPageElement &pe) const + { + switch (pe.type) { + case SPET_GOAL: { + Goal *g = Goal::Get((GoalID) pe.referenced_id); + if (g == NULL) return SPR_IMG_GOAL_BROKEN_REF; + return g->completed ? SPR_IMG_GOAL_COMPLETED : SPR_IMG_GOAL; + } + case SPET_LOCATION: + return SPR_IMG_VIEW_LOCATION; + default: + NOT_REACHED(); + } + } + + /** + * Count the number of lines used by a given page element. + * @param pe The story page element. + * @param max_width Available width to display content. + * @return the number of lines. + */ + uint CountPageElementLines(const StoryPageElement &pe, int max_width) + { + switch (pe.type) { + case SPET_TEXT: + SetDParamStr(0, pe.text); + return GetStringLineCount(STR_BLACK_RAW_STRING, max_width); + break; + + case SPET_GOAL: + case SPET_LOCATION: { + Dimension sprite_dim = GetSpriteSize(GetPageElementSprite(pe)); + int line_height = GetStringHeight(STR_JUST_NOTHING, INT_MAX); + if (line_height == 0) return 1; + return max((uint)1, sprite_dim.height / (uint)line_height); + break; + } + default: + NOT_REACHED(); + } + } + + /** + * Count the number of lines in this window. + * @return the number of lines. + */ + uint CountLines() + { + StoryPage *page = this->GetSelPage(); + if (page == NULL) return 0; + int max_width = GetAvailablePageContentWidth(); + + /* Head lines */ + int num_lines = CountHeadLines(max_width); + + /* Body lines */ + for (const StoryPageElement **iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) { + const StoryPageElement *pe = *iter; + num_lines += 1; // For the space between previous element and current element. + + num_lines += CountPageElementLines(*pe, max_width); + } + + return num_lines; + } + + /** + * Draws a page element that is composed of a sprite to the left and a single line of + * text after that. These page elements are generally clickable and are thus called + * action elements. + * @param y_offset Current y_offset which will get updated when this method has completed its drawing. + * @param width Width of the region available for drawing. + * @param line_height Height of one line of text. + * @param action_sprite The sprite to draw. + * @return the number of lines. + */ + void DrawActionElement(int &y_offset, int width, int line_height, SpriteID action_sprite) const + { + Dimension sprite_dim = GetSpriteSize(action_sprite); + uint element_height = max((uint)1, sprite_dim.height / (uint)line_height) * line_height; + + uint sprite_top = y_offset + (element_height - sprite_dim.height) / 2; + uint text_top = y_offset + (element_height - line_height) / 2; + + DrawSprite(action_sprite, PAL_NONE, 0, y_offset); + DrawString(sprite_dim.width + WD_FRAMETEXT_LEFT, width, text_top, STR_JUST_RAW_STRING, TC_BLACK); + + y_offset += element_height; + } + + /** + * Internal event handler for when a page element is clicked. + * @param pe The clicked page element. + */ + void OnPageElementClick(const StoryPageElement& pe) + { + switch (pe.type) { + case SPET_TEXT: + /* Do nothing. */ + break; + + case SPET_LOCATION: + if (_ctrl_pressed) { + ShowExtraViewPortWindow((TileIndex)pe.referenced_id); + } else { + ScrollMainWindowToTile((TileIndex)pe.referenced_id); + } + break; + + case SPET_GOAL: + ShowGoalsList(); + break; + + default: + NOT_REACHED(); + } + } + +public: + StoryBookWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) + { + this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(WID_SB_SCROLLBAR); + + /* Initalize page sort. */ + this->story_pages.SetSortFuncs(StoryBookWindow::page_sorter_funcs); + this->story_pages.ForceRebuild(); + this->BuildStoryPageList(); + this->story_page_elements.SetSortFuncs(StoryBookWindow::page_element_sorter_funcs); + /* story_page_elements will get built by SetSelectedPage */ + + this->FinishInitNested(window_number); + + /* Initialize selected vars. */ + this->selected_generic_title[0] = '\0'; + this->selected_page_id = INVALID_STORY_PAGE; + + this->OnInvalidateData(-1); + } + + /** + * Updates the disabled state of the prev/next buttons. + */ + void UpdatePrevNextDisabledState() + { + this->SetWidgetDisabledState(WID_SB_PREV_PAGE, story_pages.Length() == 0 || this->IsFirstPageSelected()); + this->SetWidgetDisabledState(WID_SB_NEXT_PAGE, story_pages.Length() == 0 || this->IsLastPageSelected()); + this->SetWidgetDirty(WID_SB_PREV_PAGE); + this->SetWidgetDirty(WID_SB_NEXT_PAGE); + } + + /** + * Sets the selected page. + * @param page_index pool index of the page to select. + */ + void SetSelectedPage(uint16 page_index) + { + if (this->selected_page_id != page_index) { + this->selected_page_id = page_index; + this->RefreshSelectedPage(); + this->UpdatePrevNextDisabledState(); + } + } + + virtual void SetStringParameters(int widget) const + { + if (widget != WID_SB_SEL_PAGE) return; + + StoryPage *page = this->GetSelPage(); + SetDParamStr(0, page != NULL && page->title != NULL ? page->title : this->selected_generic_title); + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + if (widget != WID_SB_PAGE_PANEL) return; + + StoryPage *page = this->GetSelPage(); + if (page == NULL) return; + + const int x = r.left + WD_FRAMETEXT_LEFT; + const int y = r.top + WD_FRAMETEXT_TOP; + const int right = r.right - WD_FRAMETEXT_RIGHT; + const int bottom = r.bottom - WD_FRAMETEXT_BOTTOM; + + /* Set up a clipping region for the panel. */ + DrawPixelInfo tmp_dpi; + if (!FillDrawPixelInfo(&tmp_dpi, x, y, right - x + 1, r.bottom - y + 1)) return; + + DrawPixelInfo *old_dpi = _cur_dpi; + _cur_dpi = &tmp_dpi; + + /* Draw content (now coordinates given to Draw** are local to the new clipping region). */ + int line_height = GetStringHeight(STR_JUST_NOTHING, INT_MAX); + int y_offset = - this->vscroll->GetPosition() * line_height; + + /* Date */ + SetDParam(0, page->date); + DrawString(0, right - x, y_offset, STR_JUST_DATE_LONG, TC_BLACK); + y_offset += line_height; + + /* Title */ + SetDParamStr(0, page->title != NULL ? page->title : this->selected_generic_title); + y_offset = DrawStringMultiLine(0, right - x, y_offset, bottom - y, STR_STORY_BOOK_TITLE, TC_BLACK, SA_TOP | SA_HOR_CENTER); + + /* Page elements */ + for (const StoryPageElement *const*iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) { + const StoryPageElement *const pe = *iter; + y_offset += line_height; // margin to previous element + + switch (pe->type) { + case SPET_TEXT: + SetDParamStr(0, pe->text); + y_offset = DrawStringMultiLine(0, right - x, y_offset, bottom - y, STR_JUST_RAW_STRING, TC_BLACK, SA_TOP | SA_LEFT); + break; + + case SPET_GOAL: { + Goal *g = Goal::Get((GoalID) pe->referenced_id); + if (g != NULL) { + SetDParamStr(0, g->text); + DrawActionElement(y_offset, right - x, line_height, GetPageElementSprite(*pe)); + } else { + y_offset += line_height; + } + break; + } + + case SPET_LOCATION: + SetDParamStr(0, pe->text); + DrawActionElement(y_offset, right - x, line_height, GetPageElementSprite(*pe)); + break; + } + } + + /* Restore clipping region. */ + _cur_dpi = old_dpi; + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + if (widget != WID_SB_SEL_PAGE && widget != WID_SB_PAGE_PANEL) return; + + Dimension d = GetStringBoundingBox(STR_JUST_NOTHING); + d.width = 0; + + switch(widget) { + case WID_SB_SEL_PAGE: { + + /* Get max title width. */ + for (uint16 i = 0; i < this->story_pages.Length(); i++) { + const StoryPage *s = this->story_pages[i]; + + if (s->title != NULL) { + SetDParamStr(0, s->title); + } else { + SetDParamStr(0, this->selected_generic_title); + } + Dimension title_d = GetStringBoundingBox(STR_BLACK_RAW_STRING); + + if (title_d.width > d.width) { + d.width = title_d.width; + } + } + + d.width += padding.width + WD_DROPDOWNTEXT_LEFT + WD_DROPDOWNTEXT_RIGHT; + d.height += padding.height + WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM; + *size = maxdim(*size, d); + break; + } + + case WID_SB_PAGE_PANEL: { + resize->height = d.height; + + d.height *= 5; + d.height += padding.height + WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM; + *size = maxdim(*size, d); + break; + } + } + + } + + virtual void OnResize() + { + this->vscroll->SetCapacityFromWidget(this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM); + this->vscroll->SetCount(this->CountLines()); + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + switch (widget) { + case WID_SB_SEL_PAGE: { + DropDownList *list = this->BuildDropDownList(); + if (list != NULL) { + /* Get the index of selected page. */ + int selected = 0; + for (uint16 i = 0; i < this->story_pages.Length(); i++) { + const StoryPage *p = this->story_pages[i]; + if (p->index == this->selected_page_id) break; + selected++; + } + + ShowDropDownList(this, list, selected, widget); + } + break; + } + + case WID_SB_PREV_PAGE: + this->SelectPrevPage(); + break; + + case WID_SB_NEXT_PAGE: + this->SelectNextPage(); + break; + + case WID_SB_PAGE_PANEL: { + uint clicked_row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP); + uint max_width = GetAvailablePageContentWidth(); + + /* Skip head rows. */ + uint n_head_rows = this->CountHeadLines(max_width); + if (clicked_row < n_head_rows) return; + + /* Detect if a page element was clicked. */ + uint row = n_head_rows; + for (const StoryPageElement *const*iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) { + const StoryPageElement *const pe = *iter; + + row += 1; // margin row + + uint content_rows = CountPageElementLines(*pe, max_width); + if (clicked_row >= row && clicked_row < row + content_rows) { + this->OnPageElementClick(*pe); + return; + } + + row += content_rows; + } + } + } + } + + virtual void OnDropdownSelect(int widget, int index) + { + if (widget != WID_SB_SEL_PAGE) return; + + /* index (which is set in BuildDropDownList) is the page id. */ + this->SetSelectedPage(index); + } + + /** + * Some data on this window has become invalid. + * @param data Information about the changed data. + * -1 Rebuild page list and refresh current page; + * >= 0 Id of the page that needs to be refreshed. If it is not the current page, nothing happens. + * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details. + */ + virtual void OnInvalidateData(int data = 0, bool gui_scope = true) + { + if (!gui_scope) return; + + /* If added/removed page, force rebuild. Sort order never change so just a + * re-sort is never needed. + */ + if (data == -1) { + this->story_pages.ForceRebuild(); + this->BuildStoryPageList(); + + /* Was the last page removed? */ + if (this->story_pages.Length() == 0) { + this->selected_generic_title[0] = '\0'; + } + + /* Verify page selection. */ + if (!_story_page_pool.IsValidID(this->selected_page_id)) { + this->selected_page_id = INVALID_STORY_PAGE; + } + if (this->selected_page_id == INVALID_STORY_PAGE && this->story_pages.Length() > 0) { + /* No page is selected, but there exist at least one available. + * => Select first page. + */ + this->SetSelectedPage(this->story_pages[0]->index); + } + + this->SetWidgetDisabledState(WID_SB_SEL_PAGE, this->story_pages.Length() == 0); + this->SetWidgetDirty(WID_SB_SEL_PAGE); + this->UpdatePrevNextDisabledState(); + } else if (data >= 0 && this->selected_page_id == data) { + this->RefreshSelectedPage(); + } + } +}; + +GUIStoryPageList::SortFunction * const StoryBookWindow::page_sorter_funcs[] = { + &PageOrderSorter, +}; + +GUIStoryPageElementList::SortFunction * const StoryBookWindow::page_element_sorter_funcs[] = { + &PageElementOrderSorter, +}; + +static const NWidgetPart _nested_story_book_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_BROWN), + NWidget(WWT_CAPTION, COLOUR_BROWN, WID_SB_CAPTION), SetDataTip(STR_STORY_BOOK_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_BROWN), + NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN), + NWidget(WWT_STICKYBOX, COLOUR_BROWN), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetFill(1, 1), + NWidget(NWID_VERTICAL), SetFill(1, 1), + NWidget(WWT_PANEL, COLOUR_BROWN, WID_SB_PAGE_PANEL), SetResize(1, 1), SetScrollbar(WID_SB_SCROLLBAR), EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_SB_PREV_PAGE), SetMinimalSize(100, 0), SetFill(0, 0), SetDataTip(STR_STORY_BOOK_PREV_PAGE, STR_STORY_BOOK_PREV_PAGE_TOOLTIP), + NWidget(NWID_BUTTON_DROPDOWN, COLOUR_BROWN, WID_SB_SEL_PAGE), SetMinimalSize(93, 12), SetFill(1, 0), + SetDataTip(STR_BLACK_RAW_STRING, STR_STORY_BOOK_SEL_PAGE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_SB_NEXT_PAGE), SetMinimalSize(100, 0), SetFill(0, 0), SetDataTip(STR_STORY_BOOK_NEXT_PAGE, STR_STORY_BOOK_NEXT_PAGE_TOOLTIP), + EndContainer(), + EndContainer(), + NWidget(NWID_VERTICAL), SetFill(0, 1), + NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_SB_SCROLLBAR), + NWidget(WWT_RESIZEBOX, COLOUR_BROWN), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _story_book_desc( + WDP_CENTER, "view_story", 400, 300, + WC_STORY_BOOK, WC_NONE, + 0, + _nested_story_book_widgets, lengthof(_nested_story_book_widgets) +); + +void ShowStoryBook() +{ + AllocateWindowDescFront(&_story_book_desc, 0); +} diff --git a/src/table/sprites.h b/src/table/sprites.h index cc24868314..576ffb88c5 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -56,7 +56,7 @@ static const SpriteID SPR_LARGE_SMALL_WINDOW = 682; /** Extra graphic spritenumbers */ static const SpriteID SPR_OPENTTD_BASE = 4896; -static const uint16 OPENTTD_SPRITE_COUNT = 169; +static const uint16 OPENTTD_SPRITE_COUNT = 174; /* Halftile-selection sprites */ static const SpriteID SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE; @@ -1088,6 +1088,7 @@ static const SpriteID SPR_IMG_MESSAGES = 680; static const SpriteID SPR_IMG_QUERY = 723; static const SpriteID SPR_IMG_SIGN = 4082; static const SpriteID SPR_IMG_BUY_LAND = 4791; +static const SpriteID SPR_IMG_STORY_BOOK = SPR_OPENTTD_BASE + 169; /* OpenTTD in gamescreen */ static const SpriteID SPR_OTTD_O = 4842; @@ -1317,6 +1318,12 @@ static const SpriteID SPR_IMG_CONVERT_ELRAIL = SPR_OPENTTD_BASE + 59; static const SpriteID SPR_IMG_CONVERT_MONO = SPR_OPENTTD_BASE + 65; static const SpriteID SPR_IMG_CONVERT_MAGLEV = SPR_OPENTTD_BASE + 71; +/* story_gui.cpp */ +static const SpriteID SPR_IMG_VIEW_LOCATION = SPR_OPENTTD_BASE + 170; +static const SpriteID SPR_IMG_GOAL = SPR_OPENTTD_BASE + 171; +static const SpriteID SPR_IMG_GOAL_COMPLETED = SPR_OPENTTD_BASE + 172; +static const SpriteID SPR_IMG_GOAL_BROKEN_REF= SPR_OPENTTD_BASE + 173; + /* intro_gui.cpp, genworld_gui.cpp */ static const SpriteID SPR_SELECT_TEMPERATE = 4882; static const SpriteID SPR_SELECT_TEMPERATE_PUSHED = 4883; diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 7d743d2768..cab5593eb5 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -642,7 +642,7 @@ static CallBackFunction MenuClickGraphs(int index) static CallBackFunction ToolbarLeagueClick(Window *w) { - PopupMainToolbMenu(w, WID_TN_LEAGUE, STR_GRAPH_MENU_GOAL, _networking ? 3 : 4, Game::GetInstance() != NULL ? 0 : 1); + PopupMainToolbMenu(w, WID_TN_LEAGUE, STR_GRAPH_MENU_GOAL, _networking ? 4 : 5, Game::GetInstance() != NULL ? 0 : 2); return CBF_NONE; } @@ -656,9 +656,10 @@ static CallBackFunction MenuClickLeague(int index) { switch (index) { case 0: ShowGoalsList(); break; - case 1: ShowCompanyLeagueTable(); break; - case 2: ShowPerformanceRatingDetail(); break; - case 3: ShowHighscoreTable(); break; + case 1: ShowStoryBook(); break; + case 2: ShowCompanyLeagueTable(); break; + case 3: ShowPerformanceRatingDetail(); break; + case 4: ShowHighscoreTable(); break; } return CBF_NONE; } diff --git a/src/widgets/story_widget.h b/src/widgets/story_widget.h new file mode 100644 index 0000000000..2c1e749146 --- /dev/null +++ b/src/widgets/story_widget.h @@ -0,0 +1,26 @@ + +/* $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 . + */ + +/** @file goal_widget.h Types related to the goal widgets. */ + +#ifndef WIDGETS_STORY_WIDGET_H +#define WIDGETS_STORY_WIDGET_H + +/** Widgets of the #GoalListWindow class. */ +enum StoryBookWidgets { + WID_SB_CAPTION, ///< Caption of the window. + WID_SB_SEL_PAGE, ///< Page selector. + WID_SB_PAGE_PANEL,///< Page body. + WID_SB_SCROLLBAR, ///< Scrollbar of the goal list. + WID_SB_PREV_PAGE, ///< Prev button. + WID_SB_NEXT_PAGE, ///< Next button. +}; + +#endif /* WIDGETS_STORY_WIDGET_H */ \ No newline at end of file diff --git a/src/window_type.h b/src/window_type.h index d8365c9025..29fe15dc0e 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -284,6 +284,11 @@ enum WindowClass { */ WC_GOALS_LIST, + /** + * Story book; %Window numbers: + * - CompanyID = #StoryBookWidgets + */ + WC_STORY_BOOK, /** * Station list; %Window numbers: