Changeset - r24220:eba5f923a47c
[Not reviewed]
master
0 17 0
Niels Martin Hansen - 4 years ago 2020-05-22 20:22:55
nielsm@indvikleren.dk
Feature: Push-buttons on storybook pages (#7896)

Allow more direct player-initiated interaction for Game Scripts, by letting the GS put push-buttons on storybook pages. These buttons can either trigger an immediate event, or require the player to first select a tile on the map, or a vehicle.

Additionally this reworks how the storybook pages are layouted and rendered, to allow for slightly more complex layouts, and maybe speeding drawing up a bit.
17 files changed with 1159 insertions and 82 deletions:
0 comments (0 inline, 0 general)
src/command.cpp
Show inline comments
 
@@ -170,6 +170,7 @@ CommandProc CmdShowStoryPage;
 
CommandProc CmdRemoveStoryPage;
 
CommandProc CmdRemoveStoryPageElement;
 
CommandProc CmdScrollViewport;
 
CommandProc CmdStoryPageButton;
 

	
 
CommandProc CmdLevelLand;
 

	
 
@@ -330,6 +331,7 @@ static const Command _command_proc_table
 
	DEF_CMD(CmdRemoveStoryPage,                        CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_REMOVE_STORY_PAGE
 
	DEF_CMD(CmdRemoveStoryPageElement,                 CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_REMOVE_STORY_ELEMENT_PAGE
 
	DEF_CMD(CmdScrollViewport,                         CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_SCROLL_VIEWPORT
 
	DEF_CMD(CmdStoryPageButton,                        CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_STORY_PAGE_BUTTON
 

	
 
	DEF_CMD(CmdLevelLand, CMD_ALL_TILES | CMD_NO_TEST | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_LEVEL_LAND; test run might clear tiles multiple times, in execution that only happens once
 

	
src/command_type.h
Show inline comments
 
@@ -295,6 +295,7 @@ enum Commands {
 
	CMD_REMOVE_STORY_PAGE,            ///< remove a story page
 
	CMD_REMOVE_STORY_PAGE_ELEMENT,    ///< remove a story page element
 
	CMD_SCROLL_VIEWPORT,              ///< scroll main viewport of players
 
	CMD_STORY_PAGE_BUTTON,            ///< selection via story page button
 

	
 
	CMD_LEVEL_LAND,                   ///< level land
 

	
src/game/game_instance.cpp
Show inline comments
 
@@ -147,6 +147,9 @@ void GameInstance::RegisterAPI()
 
	SQGSEventIndustryOpen_Register(this->engine);
 
	SQGSEventRoadReconstruction_Register(this->engine);
 
	SQGSEventStationFirstVehicle_Register(this->engine);
 
	SQGSEventStoryPageButtonClick_Register(this->engine);
 
	SQGSEventStoryPageTileSelect_Register(this->engine);
 
	SQGSEventStoryPageVehicleSelect_Register(this->engine);
 
	SQGSEventSubsidyAwarded_Register(this->engine);
 
	SQGSEventSubsidyExpired_Register(this->engine);
 
	SQGSEventSubsidyOffer_Register(this->engine);
src/script/api/ai/ai_event.hpp.sq
Show inline comments
 
@@ -49,6 +49,9 @@ void SQAIEvent_Register(Squirrel *engine
 
	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_EXCLUSIVE_TRANSPORT_RIGHTS,  "ET_EXCLUSIVE_TRANSPORT_RIGHTS");
 
	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_ROAD_RECONSTRUCTION,         "ET_ROAD_RECONSTRUCTION");
 
	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_VEHICLE_AUTOREPLACED,        "ET_VEHICLE_AUTOREPLACED");
 
	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_STORYPAGE_BUTTON_CLICK,      "ET_STORYPAGE_BUTTON_CLICK");
 
	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_STORYPAGE_TILE_SELECT,       "ET_STORYPAGE_TILE_SELECT");
 
	SQAIEvent.DefSQConst(engine, ScriptEvent::ET_STORYPAGE_VEHICLE_SELECT,    "ET_STORYPAGE_VEHICLE_SELECT");
 

	
 
	SQAIEvent.DefSQMethod(engine, &ScriptEvent::GetEventType, "GetEventType", 1, "x");
 

	
src/script/api/game/game_event.hpp.sq
Show inline comments
 
@@ -49,6 +49,9 @@ void SQGSEvent_Register(Squirrel *engine
 
	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_EXCLUSIVE_TRANSPORT_RIGHTS,  "ET_EXCLUSIVE_TRANSPORT_RIGHTS");
 
	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_ROAD_RECONSTRUCTION,         "ET_ROAD_RECONSTRUCTION");
 
	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_VEHICLE_AUTOREPLACED,        "ET_VEHICLE_AUTOREPLACED");
 
	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_STORYPAGE_BUTTON_CLICK,      "ET_STORYPAGE_BUTTON_CLICK");
 
	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_STORYPAGE_TILE_SELECT,       "ET_STORYPAGE_TILE_SELECT");
 
	SQGSEvent.DefSQConst(engine, ScriptEvent::ET_STORYPAGE_VEHICLE_SELECT,    "ET_STORYPAGE_VEHICLE_SELECT");
 

	
 
	SQGSEvent.DefSQMethod(engine, &ScriptEvent::GetEventType, "GetEventType", 1, "x");
 

	
src/script/api/game/game_event_types.hpp.sq
Show inline comments
 
@@ -308,3 +308,56 @@ void SQGSEventRoadReconstruction_Registe
 

	
 
	SQGSEventRoadReconstruction.PostRegister(engine);
 
}
 

	
 

	
 
template <> const char *GetClassName<ScriptEventStoryPageButtonClick, ST_GS>() { return "GSEventStoryPageButtonClick"; }
 

	
 
void SQGSEventStoryPageButtonClick_Register(Squirrel *engine)
 
{
 
	DefSQClass<ScriptEventStoryPageButtonClick, ST_GS> SQGSEventStoryPageButtonClick("GSEventStoryPageButtonClick");
 
	SQGSEventStoryPageButtonClick.PreRegister(engine, "GSEvent");
 

	
 
	SQGSEventStoryPageButtonClick.DefSQStaticMethod(engine, &ScriptEventStoryPageButtonClick::Convert, "Convert", 2, ".x");
 

	
 
	SQGSEventStoryPageButtonClick.DefSQMethod(engine, &ScriptEventStoryPageButtonClick::GetCompanyID,   "GetCompanyID",   1, "x");
 
	SQGSEventStoryPageButtonClick.DefSQMethod(engine, &ScriptEventStoryPageButtonClick::GetStoryPageID, "GetStoryPageID", 1, "x");
 
	SQGSEventStoryPageButtonClick.DefSQMethod(engine, &ScriptEventStoryPageButtonClick::GetElementID,   "GetElementID",   1, "x");
 

	
 
	SQGSEventStoryPageButtonClick.PostRegister(engine);
 
}
 

	
 

	
 
template <> const char *GetClassName<ScriptEventStoryPageTileSelect, ST_GS>() { return "GSEventStoryPageTileSelect"; }
 

	
 
void SQGSEventStoryPageTileSelect_Register(Squirrel *engine)
 
{
 
	DefSQClass<ScriptEventStoryPageTileSelect, ST_GS> SQGSEventStoryPageTileSelect("GSEventStoryPageTileSelect");
 
	SQGSEventStoryPageTileSelect.PreRegister(engine, "GSEvent");
 

	
 
	SQGSEventStoryPageTileSelect.DefSQStaticMethod(engine, &ScriptEventStoryPageTileSelect::Convert, "Convert", 2, ".x");
 

	
 
	SQGSEventStoryPageTileSelect.DefSQMethod(engine, &ScriptEventStoryPageTileSelect::GetCompanyID,   "GetCompanyID",   1, "x");
 
	SQGSEventStoryPageTileSelect.DefSQMethod(engine, &ScriptEventStoryPageTileSelect::GetStoryPageID, "GetStoryPageID", 1, "x");
 
	SQGSEventStoryPageTileSelect.DefSQMethod(engine, &ScriptEventStoryPageTileSelect::GetElementID,   "GetElementID",   1, "x");
 
	SQGSEventStoryPageTileSelect.DefSQMethod(engine, &ScriptEventStoryPageTileSelect::GetTile,        "GetTile",        1, "x");
 

	
 
	SQGSEventStoryPageTileSelect.PostRegister(engine);
 
}
 

	
 

	
 
template <> const char *GetClassName<ScriptEventStoryPageVehicleSelect, ST_GS>() { return "GSEventStoryPageVehicleSelect"; }
 

	
 
void SQGSEventStoryPageVehicleSelect_Register(Squirrel *engine)
 
{
 
	DefSQClass<ScriptEventStoryPageVehicleSelect, ST_GS> SQGSEventStoryPageVehicleSelect("GSEventStoryPageVehicleSelect");
 
	SQGSEventStoryPageVehicleSelect.PreRegister(engine, "GSEvent");
 

	
 
	SQGSEventStoryPageVehicleSelect.DefSQStaticMethod(engine, &ScriptEventStoryPageVehicleSelect::Convert, "Convert", 2, ".x");
 

	
 
	SQGSEventStoryPageVehicleSelect.DefSQMethod(engine, &ScriptEventStoryPageVehicleSelect::GetCompanyID,   "GetCompanyID",   1, "x");
 
	SQGSEventStoryPageVehicleSelect.DefSQMethod(engine, &ScriptEventStoryPageVehicleSelect::GetStoryPageID, "GetStoryPageID", 1, "x");
 
	SQGSEventStoryPageVehicleSelect.DefSQMethod(engine, &ScriptEventStoryPageVehicleSelect::GetElementID,   "GetElementID",   1, "x");
 
	SQGSEventStoryPageVehicleSelect.DefSQMethod(engine, &ScriptEventStoryPageVehicleSelect::GetVehicleID,   "GetVehicleID",   1, "x");
 

	
 
	SQGSEventStoryPageVehicleSelect.PostRegister(engine);
 
}
src/script/api/game/game_story_page.hpp.sq
Show inline comments
 
@@ -24,21 +24,101 @@ void SQGSStoryPage_Register(Squirrel *en
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPET_TEXT,                  "SPET_TEXT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPET_LOCATION,              "SPET_LOCATION");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPET_GOAL,                  "SPET_GOAL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPET_BUTTON_PUSH,           "SPET_BUTTON_PUSH");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPET_BUTTON_TILE,           "SPET_BUTTON_TILE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPET_BUTTON_VEHICLE,        "SPET_BUTTON_VEHICLE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBF_NONE,                  "SPBF_NONE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBF_FLOAT_LEFT,            "SPBF_FLOAT_LEFT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBF_FLOAT_RIGHT,           "SPBF_FLOAT_RIGHT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_MOUSE,                 "SPBC_MOUSE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_ZZZ,                   "SPBC_ZZZ");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_BUOY,                  "SPBC_BUOY");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_QUERY,                 "SPBC_QUERY");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_HQ,                    "SPBC_HQ");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_SHIP_DEPOT,            "SPBC_SHIP_DEPOT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_SIGN,                  "SPBC_SIGN");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_TREE,                  "SPBC_TREE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_BUY_LAND,              "SPBC_BUY_LAND");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_LEVEL_LAND,            "SPBC_LEVEL_LAND");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_TOWN,                  "SPBC_TOWN");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_INDUSTRY,              "SPBC_INDUSTRY");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_ROCKY_AREA,            "SPBC_ROCKY_AREA");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_DESERT,                "SPBC_DESERT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_TRANSMITTER,           "SPBC_TRANSMITTER");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_AIRPORT,               "SPBC_AIRPORT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_DOCK,                  "SPBC_DOCK");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CANAL,                 "SPBC_CANAL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_LOCK,                  "SPBC_LOCK");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_RIVER,                 "SPBC_RIVER");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_AQUEDUCT,              "SPBC_AQUEDUCT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_BRIDGE,                "SPBC_BRIDGE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_RAIL_STATION,          "SPBC_RAIL_STATION");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_TUNNEL_RAIL,           "SPBC_TUNNEL_RAIL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_TUNNEL_ELRAIL,         "SPBC_TUNNEL_ELRAIL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_TUNNEL_MONO,           "SPBC_TUNNEL_MONO");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_TUNNEL_MAGLEV,         "SPBC_TUNNEL_MAGLEV");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_AUTORAIL,              "SPBC_AUTORAIL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_AUTOELRAIL,            "SPBC_AUTOELRAIL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_AUTOMONO,              "SPBC_AUTOMONO");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_AUTOMAGLEV,            "SPBC_AUTOMAGLEV");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_WAYPOINT,              "SPBC_WAYPOINT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_RAIL_DEPOT,            "SPBC_RAIL_DEPOT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_ELRAIL_DEPOT,          "SPBC_ELRAIL_DEPOT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_MONO_DEPOT,            "SPBC_MONO_DEPOT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_MAGLEV_DEPOT,          "SPBC_MAGLEV_DEPOT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CONVERT_RAIL,          "SPBC_CONVERT_RAIL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CONVERT_ELRAIL,        "SPBC_CONVERT_ELRAIL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CONVERT_MONO,          "SPBC_CONVERT_MONO");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CONVERT_MAGLEV,        "SPBC_CONVERT_MAGLEV");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_AUTOROAD,              "SPBC_AUTOROAD");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_AUTOTRAM,              "SPBC_AUTOTRAM");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_ROAD_DEPOT,            "SPBC_ROAD_DEPOT");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_BUS_STATION,           "SPBC_BUS_STATION");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_TRUCK_STATION,         "SPBC_TRUCK_STATION");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_ROAD_TUNNEL,           "SPBC_ROAD_TUNNEL");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CLONE_TRAIN,           "SPBC_CLONE_TRAIN");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CLONE_ROADVEH,         "SPBC_CLONE_ROADVEH");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CLONE_SHIP,            "SPBC_CLONE_SHIP");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CLONE_AIRPLANE,        "SPBC_CLONE_AIRPLANE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_DEMOLISH,              "SPBC_DEMOLISH");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_LOWERLAND,             "SPBC_LOWERLAND");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_RAISELAND,             "SPBC_RAISELAND");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_PICKSTATION,           "SPBC_PICKSTATION");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_BUILDSIGNALS,          "SPBC_BUILDSIGNALS");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_DARK_BLUE,             "SPBC_DARK_BLUE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_PALE_GREEN,            "SPBC_PALE_GREEN");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_PINK,                  "SPBC_PINK");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_YELLOW,                "SPBC_YELLOW");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_RED,                   "SPBC_RED");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_LIGHT_BLUE,            "SPBC_LIGHT_BLUE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_GREEN,                 "SPBC_GREEN");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_DARK_GREEN,            "SPBC_DARK_GREEN");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_BLUE,                  "SPBC_BLUE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_CREAM,                 "SPBC_CREAM");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_MAUVE,                 "SPBC_MAUVE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_PURPLE,                "SPBC_PURPLE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_ORANGE,                "SPBC_ORANGE");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_BROWN,                 "SPBC_BROWN");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_GREY,                  "SPBC_GREY");
 
	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::SPBC_WHITE,                 "SPBC_WHITE");
 

	
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::IsValidStoryPage,        "IsValidStoryPage",        2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::IsValidStoryPageElement, "IsValidStoryPageElement", 2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::New,                     "New",                     3, ".i.");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::NewElement,              "NewElement",              5, ".iii.");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::UpdateElement,           "UpdateElement",           4, ".ii.");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::GetPageSortValue,        "GetPageSortValue",        2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::GetPageElementSortValue, "GetPageElementSortValue", 2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::GetCompany,              "GetCompany",              2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::GetDate,                 "GetDate",                 2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::SetDate,                 "SetDate",                 3, ".ii");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::SetTitle,                "SetTitle",                3, ".i.");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::Show,                    "Show",                    2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::Remove,                  "Remove",                  2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::RemoveElement,           "RemoveElement",           2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::IsValidStoryPage,           "IsValidStoryPage",           2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::IsValidStoryPageElement,    "IsValidStoryPageElement",    2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::New,                        "New",                        3, ".i.");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::NewElement,                 "NewElement",                 5, ".iii.");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::UpdateElement,              "UpdateElement",              4, ".ii.");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::GetPageSortValue,           "GetPageSortValue",           2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::GetPageElementSortValue,    "GetPageElementSortValue",    2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::GetCompany,                 "GetCompany",                 2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::GetDate,                    "GetDate",                    2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::SetDate,                    "SetDate",                    3, ".ii");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::SetTitle,                   "SetTitle",                   3, ".i.");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::Show,                       "Show",                       2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::Remove,                     "Remove",                     2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::RemoveElement,              "RemoveElement",              2, ".i");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::MakePushButtonReference,    "MakePushButtonReference",    3, ".ii");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::MakeTileButtonReference,    "MakeTileButtonReference",    4, ".iii");
 
	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::MakeVehicleButtonReference, "MakeVehicleButtonReference", 5, ".iiii");
 

	
 
	SQGSStoryPage.PostRegister(engine);
 
}
src/script/api/game_changelog.hpp
Show inline comments
 
@@ -17,6 +17,14 @@
 
 *
 
 * This version is not yet released. The following changes are not set in stone yet.
 
 *
 
 * API additions:
 
 * \li GSEventStoryPageButtonClick
 
 * \li GSEventStoryPageTileSelect
 
 * \li GSEventStoryPageVehicleSelect
 
 * \li GSStoryPage::MakePushButtonReference
 
 * \li GSStoryPage::MakeTileButtonReference
 
 * \li GSStoryPage::MakeVehicleButtonReference
 
 *
 
 * \b 1.10.0
 
 *
 
 * API additions:
src/script/api/script_event.hpp
Show inline comments
 
@@ -54,6 +54,9 @@ public:
 
		ET_EXCLUSIVE_TRANSPORT_RIGHTS,
 
		ET_ROAD_RECONSTRUCTION,
 
		ET_VEHICLE_AUTOREPLACED,
 
		ET_STORYPAGE_BUTTON_CLICK,
 
		ET_STORYPAGE_TILE_SELECT,
 
		ET_STORYPAGE_VEHICLE_SELECT,
 
	};
 

	
 
	/**
src/script/api/script_event_types.hpp
Show inline comments
 
@@ -1098,4 +1098,136 @@ private:
 
	VehicleID new_id; ///< The vehicle that has been created in replacement.
 
};
 

	
 
/**
 
 * Event StoryPageButtonClick, indicating a player clicked a push button on a storybook page.
 
 * @api game
 
 */
 
class ScriptEventStoryPageButtonClick : public ScriptEvent {
 
public:
 
	/**
 
	 * @param company_id  Which company triggered the event.
 
	 * @param page_id     Which page was the clicked button on.
 
	 * @param element_id  Which button element was clicked.
 
	 */
 
	ScriptEventStoryPageButtonClick(CompanyID company_id, StoryPageID page_id, StoryPageElementID element_id) :
 
		ScriptEvent(ET_STORYPAGE_BUTTON_CLICK),
 
		company_id((ScriptCompany::CompanyID)company_id),
 
		page_id(page_id),
 
		element_id(element_id)
 
	{ }
 

	
 
	/**
 
	 * Convert an ScriptEvent to the real instance.
 
	 * @param instance The instance to convert.
 
	 * @return The converted instance.
 
	 */
 
	static ScriptEventStoryPageButtonClick *Convert(ScriptEvent *instance) { return (ScriptEventStoryPageButtonClick *)instance; }
 

	
 
	/** Get the CompanyID of the player that selected a tile. */
 
	ScriptCompany::CompanyID GetCompanyID() { return this->company_id; }
 

	
 
	/** Get the StoryPageID of the storybook page the clicked button is located on. */
 
	StoryPageID GetStoryPageID() { return this->page_id; }
 

	
 
	/** Get the StoryPageElementID of the button element that was clicked. */
 
	StoryPageElementID GetElementID() { return this->element_id; }
 

	
 
private:
 
	ScriptCompany::CompanyID company_id;
 
	StoryPageID page_id;
 
	StoryPageElementID element_id;
 
};
 

	
 
/**
 
 * Event StoryPageTileSelect, indicating a player clicked a tile selection button on a storybook page, and selected a tile.
 
 * @api game
 
 */
 
class ScriptEventStoryPageTileSelect : public ScriptEvent {
 
public:
 
	/**
 
	 * @param company_id  Which company triggered the event.
 
	 * @param page_id     Which page is the used selection button on.
 
	 * @param element_id  Which button element was used to select the tile.
 
	 * @param tile_index  Which tile was selected by the player.
 
	 */
 
	ScriptEventStoryPageTileSelect(CompanyID company_id, StoryPageID page_id, StoryPageElementID element_id, TileIndex tile_index) :
 
		ScriptEvent(ET_STORYPAGE_TILE_SELECT),
 
		company_id((ScriptCompany::CompanyID)company_id),
 
		page_id(page_id),
 
		element_id(element_id),
 
		tile_index(tile_index)
 
	{ }
 

	
 
	/**
 
	 * Convert an ScriptEvent to the real instance.
 
	 * @param instance The instance to convert.
 
	 * @return The converted instance.
 
	 */
 
	static ScriptEventStoryPageTileSelect *Convert(ScriptEvent *instance) { return (ScriptEventStoryPageTileSelect *)instance; }
 

	
 
	/** Get the CompanyID of the player that selected a tile. */
 
	ScriptCompany::CompanyID GetCompanyID() { return this->company_id; }
 

	
 
	/** Get the StoryPageID of the storybook page the used selection button is located on. */
 
	StoryPageID GetStoryPageID() { return this->page_id; }
 

	
 
	/** Get the StoryPageElementID of the selection button used to select the tile. */
 
	StoryPageElementID GetElementID() { return this->element_id; }
 

	
 
	/** Get the TileIndex of the tile the player selected */
 
	TileIndex GetTile() { return this->tile_index; }
 

	
 
private:
 
	ScriptCompany::CompanyID company_id;
 
	StoryPageID page_id;
 
	StoryPageElementID element_id;
 
	TileIndex tile_index;
 
};
 

	
 
/**
 
 * Event StoryPageTileSelect, indicating a player clicked a tile selection button on a storybook page, and selected a tile.
 
 * @api game
 
 */
 
class ScriptEventStoryPageVehicleSelect : public ScriptEvent {
 
public:
 
	/**
 
	 * @param company_id  Which company triggered the event.
 
	 * @param page_id     Which page is the used selection button on.
 
	 * @param element_id  Which button element was used to select the tile.
 
	 * @param vehicle_id  Which vehicle was selected by the player.
 
	 */
 
	ScriptEventStoryPageVehicleSelect(CompanyID company_id, StoryPageID page_id, StoryPageElementID element_id, VehicleID vehicle_id) :
 
		ScriptEvent(ET_STORYPAGE_VEHICLE_SELECT),
 
		company_id((ScriptCompany::CompanyID)company_id),
 
		page_id(page_id),
 
		element_id(element_id),
 
		vehicle_id(vehicle_id)
 
	{ }
 

	
 
	/**
 
	 * Convert an ScriptEvent to the real instance.
 
	 * @param instance The instance to convert.
 
	 * @return The converted instance.
 
	 */
 
	static ScriptEventStoryPageVehicleSelect *Convert(ScriptEvent *instance) { return (ScriptEventStoryPageVehicleSelect *)instance; }
 

	
 
	/** Get the CompanyID of the player that selected a tile. */
 
	ScriptCompany::CompanyID GetCompanyID() { return this->company_id; }
 

	
 
	/** Get the StoryPageID of the storybook page the used selection button is located on. */
 
	StoryPageID GetStoryPageID() { return this->page_id; }
 

	
 
	/** Get the StoryPageElementID of the selection button used to select the vehicle. */
 
	StoryPageElementID GetElementID() { return this->element_id; }
 

	
 
	/** Get the VehicleID of the vehicle the player selected */
 
	VehicleID GetVehicleID() { return this->vehicle_id; }
 

	
 
private:
 
	ScriptCompany::CompanyID company_id;
 
	StoryPageID page_id;
 
	StoryPageElementID element_id;
 
	VehicleID vehicle_id;
 
};
 

	
 
#endif /* SCRIPT_EVENT_TYPES_HPP */
src/script/api/script_story_page.cpp
Show inline comments
 
@@ -22,6 +22,11 @@
 

	
 
#include "../../safeguards.h"
 

	
 
static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type)
 
{
 
	return type == SPET_TEXT || type == SPET_LOCATION || type == SPET_BUTTON_PUSH || type == SPET_BUTTON_TILE || type == SPET_BUTTON_VEHICLE;
 
}
 

	
 
/* static */ bool ScriptStoryPage::IsValidStoryPage(StoryPageID story_page_id)
 
{
 
	return ::StoryPage::IsValidID(story_page_id);
 
@@ -57,18 +62,34 @@
 
{
 
	CCountedPtr<Text> counter(text);
 

	
 
	::StoryPageElementType btype = static_cast<::StoryPageElementType>(type);
 

	
 
	EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, ScriptObject::GetCompany() == OWNER_DEITY);
 
	EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, IsValidStoryPage(story_page_id));
 
	EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, (type != SPET_TEXT && type != SPET_LOCATION) || (text != nullptr && !StrEmpty(text->GetEncodedText())));
 
	EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, !StoryPageElementTypeRequiresText(btype) || (text != nullptr && !StrEmpty(text->GetEncodedText())));
 
	EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, type != SPET_LOCATION || ::IsValidTile(reference));
 
	EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, type != SPET_GOAL || ScriptGoal::IsValidGoal((ScriptGoal::GoalID)reference));
 
	EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, type != SPET_GOAL || !(StoryPage::Get(story_page_id)->company == INVALID_COMPANY && Goal::Get(reference)->company != INVALID_COMPANY));
 

	
 
	if (!ScriptObject::DoCommand(type == SPET_LOCATION ? reference : 0,
 
	uint32 refid = 0;
 
	TileIndex reftile = 0;
 
	switch (type) {
 
		case SPET_LOCATION:
 
			reftile = reference;
 
			break;
 
		case SPET_GOAL:
 
		case SPET_BUTTON_PUSH:
 
		case SPET_BUTTON_TILE:
 
		case SPET_BUTTON_VEHICLE:
 
			refid = reference;
 
			break;
 
	}
 

	
 
	if (!ScriptObject::DoCommand(reftile,
 
			story_page_id + (type << 16),
 
			type == SPET_GOAL ? reference : 0,
 
			refid,
 
			CMD_CREATE_STORY_PAGE_ELEMENT,
 
			type == SPET_TEXT || type == SPET_LOCATION ? text->GetEncodedText() : nullptr,
 
			StoryPageElementTypeRequiresText(btype) ? text->GetEncodedText() : nullptr,
 
			&ScriptInstance::DoCommandReturnStoryPageElementID)) return STORY_PAGE_ELEMENT_INVALID;
 

	
 
	/* In case of test-mode, we return StoryPageElementID 0 */
 
@@ -86,16 +107,30 @@
 
	StoryPage *p = StoryPage::Get(pe->page);
 
	::StoryPageElementType type = pe->type;
 

	
 
	EnforcePrecondition(false, (type != ::SPET_TEXT && type != ::SPET_LOCATION) || (text != nullptr && !StrEmpty(text->GetEncodedText())));
 
	EnforcePrecondition(false, !StoryPageElementTypeRequiresText(type) || (text != nullptr && !StrEmpty(text->GetEncodedText())));
 
	EnforcePrecondition(false, type != ::SPET_LOCATION || ::IsValidTile(reference));
 
	EnforcePrecondition(false, type != ::SPET_GOAL || ScriptGoal::IsValidGoal((ScriptGoal::GoalID)reference));
 
	EnforcePrecondition(false, type != ::SPET_GOAL || !(p->company == INVALID_COMPANY && Goal::Get(reference)->company != INVALID_COMPANY));
 

	
 
	return ScriptObject::DoCommand(type == ::SPET_LOCATION ? reference : 0,
 
	uint32 refid = 0;
 
	TileIndex reftile = 0;
 
	switch (type) {
 
		case SPET_LOCATION:
 
			reftile = reference;
 
			break;
 
		case SPET_GOAL:
 
		case SPET_BUTTON_PUSH:
 
		case SPET_BUTTON_TILE:
 
		case SPET_BUTTON_VEHICLE:
 
			refid = reference;
 
			break;
 
	}
 

	
 
	return ScriptObject::DoCommand(reftile,
 
			story_page_element_id,
 
			type == ::SPET_GOAL ? reference : 0,
 
			refid,
 
			CMD_UPDATE_STORY_PAGE_ELEMENT,
 
			type == ::SPET_TEXT || type == ::SPET_LOCATION ? text->GetEncodedText() : nullptr);
 
			StoryPageElementTypeRequiresText(type) ? text->GetEncodedText() : nullptr);
 
}
 

	
 
/* static */ uint32 ScriptStoryPage::GetPageSortValue(StoryPageID story_page_id)
 
@@ -173,3 +208,39 @@
 
	return ScriptObject::DoCommand(0, story_page_element_id, 0, CMD_REMOVE_STORY_PAGE_ELEMENT);
 
}
 

	
 
/* static */ ScriptStoryPage::StoryPageButtonFormatting ScriptStoryPage::MakePushButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags)
 
{
 
	StoryPageButtonData data;
 
	data.SetColour((Colours)colour);
 
	data.SetFlags((::StoryPageButtonFlags)flags);
 
	if (!data.ValidateColour()) return UINT32_MAX;
 
	if (!data.ValidateFlags()) return UINT32_MAX;
 
	return data.referenced_id;
 
}
 

	
 
/* static */ ScriptStoryPage::StoryPageButtonFormatting ScriptStoryPage::MakeTileButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags, StoryPageButtonCursor cursor)
 
{
 
	StoryPageButtonData data;
 
	data.SetColour((Colours)colour);
 
	data.SetFlags((::StoryPageButtonFlags)flags);
 
	data.SetCursor((::StoryPageButtonCursor)cursor);
 
	if (!data.ValidateColour()) return UINT32_MAX;
 
	if (!data.ValidateFlags()) return UINT32_MAX;
 
	if (!data.ValidateCursor()) return UINT32_MAX;
 
	return data.referenced_id;
 
}
 

	
 
/* static */ ScriptStoryPage::StoryPageButtonFormatting ScriptStoryPage::MakeVehicleButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags, StoryPageButtonCursor cursor, ScriptVehicle::VehicleType vehtype)
 
{
 
	StoryPageButtonData data;
 
	data.SetColour((Colours)colour);
 
	data.SetFlags((::StoryPageButtonFlags)flags);
 
	data.SetCursor((::StoryPageButtonCursor)cursor);
 
	data.SetVehicleType((::VehicleType)vehtype);
 
	if (!data.ValidateColour()) return UINT32_MAX;
 
	if (!data.ValidateFlags()) return UINT32_MAX;
 
	if (!data.ValidateCursor()) return UINT32_MAX;
 
	if (!data.ValidateVehicleType()) return UINT32_MAX;
 
	return data.referenced_id;
 
}
 

	
src/script/api/script_story_page.hpp
Show inline comments
 
@@ -12,6 +12,7 @@
 

	
 
#include "script_company.hpp"
 
#include "script_date.hpp"
 
#include "script_vehicle.hpp"
 
#include "../../story_type.h"
 
#include "../../story_base.h"
 

	
 
@@ -57,9 +58,111 @@ public:
 
	 * Story page element types.
 
	 */
 
	enum StoryPageElementType {
 
		SPET_TEXT = ::SPET_TEXT,         ///< An element that displays a block of text.
 
		SPET_LOCATION = ::SPET_LOCATION, ///< An element that displays a single line of text along with a button to view the referenced location.
 
		SPET_GOAL = ::SPET_GOAL,         ///< An element that displays a goal.
 
		SPET_TEXT = ::SPET_TEXT,                     ///< An element that displays a block of text.
 
		SPET_LOCATION = ::SPET_LOCATION,             ///< An element that displays a single line of text along with a button to view the referenced location.
 
		SPET_GOAL = ::SPET_GOAL,                     ///< An element that displays a goal.
 
		SPET_BUTTON_PUSH = ::SPET_BUTTON_PUSH,       ///< A push button that triggers an immediate event.
 
		SPET_BUTTON_TILE = ::SPET_BUTTON_TILE,       ///< A button that allows the player to select a tile, and triggers an event with the tile.
 
		SPET_BUTTON_VEHICLE = ::SPET_BUTTON_VEHICLE, ///< A button that allows the player to select a vehicle, and triggers an event wih the vehicle.
 
	};
 

	
 
	/**
 
	 * Formatting data for button page elements.
 
	 */
 
	typedef uint32 StoryPageButtonFormatting;
 

	
 
	/**
 
	 * Formatting and layout flags for story page buttons.
 
	 * The SPBF_FLOAT_LEFT and SPBF_FLOAT_RIGHT flags can not be combined.
 
	 */
 
	enum StoryPageButtonFlags {
 
		SPBF_NONE        = ::SPBF_NONE,        ///< No special formatting for button.
 
		SPBF_FLOAT_LEFT  = ::SPBF_FLOAT_LEFT,  ///< Button is placed to the left of the following paragraph.
 
		SPBF_FLOAT_RIGHT = ::SPBF_FLOAT_RIGHT, ///< Button is placed to the right of the following paragraph.
 
	};
 

	
 
	/**
 
	 * Mouse cursors usable by story page buttons.
 
	 */
 
	enum StoryPageButtonCursor {
 
		SPBC_MOUSE          = ::SPBC_MOUSE,
 
		SPBC_ZZZ            = ::SPBC_ZZZ,
 
		SPBC_BUOY           = ::SPBC_BUOY,
 
		SPBC_QUERY          = ::SPBC_QUERY,
 
		SPBC_HQ             = ::SPBC_HQ,
 
		SPBC_SHIP_DEPOT     = ::SPBC_SHIP_DEPOT,
 
		SPBC_SIGN           = ::SPBC_SIGN,
 
		SPBC_TREE           = ::SPBC_TREE,
 
		SPBC_BUY_LAND       = ::SPBC_BUY_LAND,
 
		SPBC_LEVEL_LAND     = ::SPBC_LEVEL_LAND,
 
		SPBC_TOWN           = ::SPBC_TOWN,
 
		SPBC_INDUSTRY       = ::SPBC_INDUSTRY,
 
		SPBC_ROCKY_AREA     = ::SPBC_ROCKY_AREA,
 
		SPBC_DESERT         = ::SPBC_DESERT,
 
		SPBC_TRANSMITTER    = ::SPBC_TRANSMITTER,
 
		SPBC_AIRPORT        = ::SPBC_AIRPORT,
 
		SPBC_DOCK           = ::SPBC_DOCK,
 
		SPBC_CANAL          = ::SPBC_CANAL,
 
		SPBC_LOCK           = ::SPBC_LOCK,
 
		SPBC_RIVER          = ::SPBC_RIVER,
 
		SPBC_AQUEDUCT       = ::SPBC_AQUEDUCT,
 
		SPBC_BRIDGE         = ::SPBC_BRIDGE,
 
		SPBC_RAIL_STATION   = ::SPBC_RAIL_STATION,
 
		SPBC_TUNNEL_RAIL    = ::SPBC_TUNNEL_RAIL,
 
		SPBC_TUNNEL_ELRAIL  = ::SPBC_TUNNEL_ELRAIL,
 
		SPBC_TUNNEL_MONO    = ::SPBC_TUNNEL_MONO,
 
		SPBC_TUNNEL_MAGLEV  = ::SPBC_TUNNEL_MAGLEV,
 
		SPBC_AUTORAIL       = ::SPBC_AUTORAIL,
 
		SPBC_AUTOELRAIL     = ::SPBC_AUTOELRAIL,
 
		SPBC_AUTOMONO       = ::SPBC_AUTOMONO,
 
		SPBC_AUTOMAGLEV     = ::SPBC_AUTOMAGLEV,
 
		SPBC_WAYPOINT       = ::SPBC_WAYPOINT,
 
		SPBC_RAIL_DEPOT     = ::SPBC_RAIL_DEPOT,
 
		SPBC_ELRAIL_DEPOT   = ::SPBC_ELRAIL_DEPOT,
 
		SPBC_MONO_DEPOT     = ::SPBC_MONO_DEPOT,
 
		SPBC_MAGLEV_DEPOT   = ::SPBC_MAGLEV_DEPOT,
 
		SPBC_CONVERT_RAIL   = ::SPBC_CONVERT_RAIL,
 
		SPBC_CONVERT_ELRAIL = ::SPBC_CONVERT_ELRAIL,
 
		SPBC_CONVERT_MONO   = ::SPBC_CONVERT_MONO,
 
		SPBC_CONVERT_MAGLEV = ::SPBC_CONVERT_MAGLEV,
 
		SPBC_AUTOROAD       = ::SPBC_AUTOROAD,
 
		SPBC_AUTOTRAM       = ::SPBC_AUTOTRAM,
 
		SPBC_ROAD_DEPOT     = ::SPBC_ROAD_DEPOT,
 
		SPBC_BUS_STATION    = ::SPBC_BUS_STATION,
 
		SPBC_TRUCK_STATION  = ::SPBC_TRUCK_STATION,
 
		SPBC_ROAD_TUNNEL    = ::SPBC_ROAD_TUNNEL,
 
		SPBC_CLONE_TRAIN    = ::SPBC_CLONE_TRAIN,
 
		SPBC_CLONE_ROADVEH  = ::SPBC_CLONE_ROADVEH,
 
		SPBC_CLONE_SHIP     = ::SPBC_CLONE_SHIP,
 
		SPBC_CLONE_AIRPLANE = ::SPBC_CLONE_AIRPLANE,
 
		SPBC_DEMOLISH       = ::SPBC_DEMOLISH,
 
		SPBC_LOWERLAND      = ::SPBC_LOWERLAND,
 
		SPBC_RAISELAND      = ::SPBC_RAISELAND,
 
		SPBC_PICKSTATION    = ::SPBC_PICKSTATION,
 
		SPBC_BUILDSIGNALS   = ::SPBC_BUILDSIGNALS,
 
	};
 

	
 
	/**
 
	 * Colour codes usable for story page button elements.
 
	 * Place a colour value in the lowest 8 bits of the \c reference parameter to the button.
 
	 */
 
	enum StoryPageButtonColour {
 
		SPBC_DARK_BLUE  = ::COLOUR_DARK_BLUE,
 
		SPBC_PALE_GREEN = ::COLOUR_PALE_GREEN,
 
		SPBC_PINK       = ::COLOUR_PINK,
 
		SPBC_YELLOW     = ::COLOUR_YELLOW,
 
		SPBC_RED        = ::COLOUR_RED,
 
		SPBC_LIGHT_BLUE = ::COLOUR_LIGHT_BLUE,
 
		SPBC_GREEN      = ::COLOUR_GREEN,
 
		SPBC_DARK_GREEN = ::COLOUR_DARK_GREEN,
 
		SPBC_BLUE       = ::COLOUR_BLUE,
 
		SPBC_CREAM      = ::COLOUR_CREAM,
 
		SPBC_MAUVE      = ::COLOUR_MAUVE,
 
		SPBC_PURPLE     = ::COLOUR_PURPLE,
 
		SPBC_ORANGE     = ::COLOUR_ORANGE,
 
		SPBC_BROWN      = ::COLOUR_BROWN,
 
		SPBC_GREY       = ::COLOUR_GREY,
 
		SPBC_WHITE      = ::COLOUR_WHITE,
 
	};
 

	
 
	/**
 
@@ -90,7 +193,11 @@ public:
 
	 * Create a new story page element.
 
	 * @param story_page_id The page id of the story page which the page element should be appended to.
 
	 * @param type Which page element type to create.
 
	 * @param reference A reference value to the object that is referred to by some page element types. When type is SPET_GOAL, this is the goal ID. When type is SPET_LOCATION, this is the TileIndex.
 
	 * @param reference A reference value to the object that is referred to by some page element types.
 
	 *                  When type is SPET_GOAL, this is the goal ID.
 
	 *                  When type is SPET_LOCATION, this is the TileIndex.
 
	 *                  When type is a button, this is additional parameters for the button,
 
	 *                  use the #BuildPushButtonReference, #BuildTileButtonReference, or #BuildVehicleButtonReference functions to make the values.
 
	 * @param text The body text of page elements that allow custom text. (SPET_TEXT and SPET_LOCATION)
 
	 * @return The new StoryPageElementID, or STORY_PAGE_ELEMENT_INVALID if it failed.
 
	 * @pre No ScriptCompanyMode may be in scope.
 
@@ -204,6 +311,30 @@ public:
 
	 * @pre IsValidStoryPageElement(story_page_element_id).
 
	 */
 
	static bool RemoveElement(StoryPageElementID story_page_element_id);
 

	
 
	/**
 
	 * Create a reference value for SPET_BUTTON_PUSH element parameters.
 
	 * @param colour The colour for the face of the button.
 
	 * @return A reference value usable with the #NewElement and #UpdateElement functions.
 
	 */
 
	static StoryPageButtonFormatting MakePushButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags);
 

	
 
	/**
 
	 * Create a reference value for SPET_BUTTON_TILE element parameters.
 
	 * @param colour The colour for the face of the button.
 
	 * @param cursor The mouse cursor to use when the player clicks the button and the game is ready for the player to select a tile.
 
	 * @return A reference value usable with the #NewElement and #UpdateElement functions.
 
	 */
 
	static StoryPageButtonFormatting MakeTileButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags, StoryPageButtonCursor cursor);
 

	
 
	/**
 
	 * Create a reference value for SPET_BUTTON_VEHICLE element parameters.
 
	 * @param colour  The colour for the face of the button.
 
	 * @param cursor  The mouse cursor to use when the player clicks the button and the game is ready for the player to select a vehicle.
 
	 * @param vehtype The type of vehicle that will be selectable, or \c VT_INVALID to allow all types.
 
	 * @return A reference value usable with the #NewElement and #UpdateElement functions.
 
	 */
 
	static StoryPageButtonFormatting MakeVehicleButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags, StoryPageButtonCursor cursor, ScriptVehicle::VehicleType vehtype);
 
};
 

	
 
#endif /* SCRIPT_STORY_HPP */
src/script/api/template/template_event_types.hpp.sq
Show inline comments
 
@@ -273,3 +273,30 @@ namespace SQConvert {
 
	template <> inline const ScriptEventVehicleAutoReplaced &GetParam(ForceType<const ScriptEventVehicleAutoReplaced &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventVehicleAutoReplaced *)instance; }
 
	template <> inline int Return<ScriptEventVehicleAutoReplaced *>(HSQUIRRELVM vm, ScriptEventVehicleAutoReplaced *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "EventVehicleAutoReplaced", res, nullptr, DefSQDestructorCallback<ScriptEventVehicleAutoReplaced>, true); return 1; }
 
} // namespace SQConvert
 

	
 
namespace SQConvert {
 
	/* Allow ScriptEventStoryPageButtonClick to be used as Squirrel parameter */
 
	template <> inline ScriptEventStoryPageButtonClick *GetParam(ForceType<ScriptEventStoryPageButtonClick *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptEventStoryPageButtonClick *)instance; }
 
	template <> inline ScriptEventStoryPageButtonClick &GetParam(ForceType<ScriptEventStoryPageButtonClick &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventStoryPageButtonClick *)instance; }
 
	template <> inline const ScriptEventStoryPageButtonClick *GetParam(ForceType<const ScriptEventStoryPageButtonClick *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptEventStoryPageButtonClick *)instance; }
 
	template <> inline const ScriptEventStoryPageButtonClick &GetParam(ForceType<const ScriptEventStoryPageButtonClick &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventStoryPageButtonClick *)instance; }
 
	template <> inline int Return<ScriptEventStoryPageButtonClick *>(HSQUIRRELVM vm, ScriptEventStoryPageButtonClick *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "EventStoryPageButtonClick", res, nullptr, DefSQDestructorCallback<ScriptEventStoryPageButtonClick>, true); return 1; }
 
} // namespace SQConvert
 

	
 
namespace SQConvert {
 
	/* Allow ScriptEventStoryPageTileSelect to be used as Squirrel parameter */
 
	template <> inline ScriptEventStoryPageTileSelect *GetParam(ForceType<ScriptEventStoryPageTileSelect *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptEventStoryPageTileSelect *)instance; }
 
	template <> inline ScriptEventStoryPageTileSelect &GetParam(ForceType<ScriptEventStoryPageTileSelect &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventStoryPageTileSelect *)instance; }
 
	template <> inline const ScriptEventStoryPageTileSelect *GetParam(ForceType<const ScriptEventStoryPageTileSelect *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptEventStoryPageTileSelect *)instance; }
 
	template <> inline const ScriptEventStoryPageTileSelect &GetParam(ForceType<const ScriptEventStoryPageTileSelect &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventStoryPageTileSelect *)instance; }
 
	template <> inline int Return<ScriptEventStoryPageTileSelect *>(HSQUIRRELVM vm, ScriptEventStoryPageTileSelect *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "EventStoryPageTileSelect", res, nullptr, DefSQDestructorCallback<ScriptEventStoryPageTileSelect>, true); return 1; }
 
} // namespace SQConvert
 

	
 
namespace SQConvert {
 
	/* Allow ScriptEventStoryPageVehicleSelect to be used as Squirrel parameter */
 
	template <> inline ScriptEventStoryPageVehicleSelect *GetParam(ForceType<ScriptEventStoryPageVehicleSelect *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptEventStoryPageVehicleSelect *)instance; }
 
	template <> inline ScriptEventStoryPageVehicleSelect &GetParam(ForceType<ScriptEventStoryPageVehicleSelect &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventStoryPageVehicleSelect *)instance; }
 
	template <> inline const ScriptEventStoryPageVehicleSelect *GetParam(ForceType<const ScriptEventStoryPageVehicleSelect *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptEventStoryPageVehicleSelect *)instance; }
 
	template <> inline const ScriptEventStoryPageVehicleSelect &GetParam(ForceType<const ScriptEventStoryPageVehicleSelect &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptEventStoryPageVehicleSelect *)instance; }
 
	template <> inline int Return<ScriptEventStoryPageVehicleSelect *>(HSQUIRRELVM vm, ScriptEventStoryPageVehicleSelect *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "EventStoryPageVehicleSelect", res, nullptr, DefSQDestructorCallback<ScriptEventStoryPageVehicleSelect>, true); return 1; }
 
} // namespace SQConvert
src/script/api/template/template_story_page.hpp.sq
Show inline comments
 
@@ -17,6 +17,12 @@ namespace SQConvert {
 
	template <> inline int Return<ScriptStoryPage::StoryPageElementID>(HSQUIRRELVM vm, ScriptStoryPage::StoryPageElementID res) { sq_pushinteger(vm, (int32)res); return 1; }
 
	template <> inline ScriptStoryPage::StoryPageElementType GetParam(ForceType<ScriptStoryPage::StoryPageElementType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptStoryPage::StoryPageElementType)tmp; }
 
	template <> inline int Return<ScriptStoryPage::StoryPageElementType>(HSQUIRRELVM vm, ScriptStoryPage::StoryPageElementType res) { sq_pushinteger(vm, (int32)res); return 1; }
 
	template <> inline ScriptStoryPage::StoryPageButtonFlags GetParam(ForceType<ScriptStoryPage::StoryPageButtonFlags>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptStoryPage::StoryPageButtonFlags)tmp; }
 
	template <> inline int Return<ScriptStoryPage::StoryPageButtonFlags>(HSQUIRRELVM vm, ScriptStoryPage::StoryPageButtonFlags res) { sq_pushinteger(vm, (int32)res); return 1; }
 
	template <> inline ScriptStoryPage::StoryPageButtonCursor GetParam(ForceType<ScriptStoryPage::StoryPageButtonCursor>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptStoryPage::StoryPageButtonCursor)tmp; }
 
	template <> inline int Return<ScriptStoryPage::StoryPageButtonCursor>(HSQUIRRELVM vm, ScriptStoryPage::StoryPageButtonCursor res) { sq_pushinteger(vm, (int32)res); return 1; }
 
	template <> inline ScriptStoryPage::StoryPageButtonColour GetParam(ForceType<ScriptStoryPage::StoryPageButtonColour>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptStoryPage::StoryPageButtonColour)tmp; }
 
	template <> inline int Return<ScriptStoryPage::StoryPageButtonColour>(HSQUIRRELVM vm, ScriptStoryPage::StoryPageButtonColour res) { sq_pushinteger(vm, (int32)res); return 1; }
 

	
 
	/* Allow ScriptStoryPage to be used as Squirrel parameter */
 
	template <> inline ScriptStoryPage *GetParam(ForceType<ScriptStoryPage *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptStoryPage *)instance; }
src/story.cpp
Show inline comments
 
@@ -21,6 +21,10 @@
 
#include "goal_base.h"
 
#include "window_func.h"
 
#include "gui.h"
 
#include "vehicle_base.h"
 
#include "game/game.hpp"
 
#include "script/api/script_story_page.hpp"
 
#include "script/api/script_event_types.hpp"
 

	
 
#include "safeguards.h"
 

	
 
@@ -47,6 +51,8 @@ INSTANTIATE_POOL_METHODS(StoryPage)
 
 */
 
static bool VerifyElementContentParameters(StoryPageID page_id, StoryPageElementType type, TileIndex tile, uint32 reference, const char *text)
 
{
 
	StoryPageButtonData button_data{ reference };
 

	
 
	switch (type) {
 
		case SPET_TEXT:
 
			if (StrEmpty(text)) return false;
 
@@ -60,6 +66,18 @@ static bool VerifyElementContentParamete
 
			/* Reject company specific goals on global pages */
 
			if (StoryPage::Get(page_id)->company == INVALID_COMPANY && Goal::Get((GoalID)reference)->company != INVALID_COMPANY) return false;
 
			break;
 
		case SPET_BUTTON_PUSH:
 
			if (!button_data.ValidateColour()) return false;
 
			return true;
 
		case SPET_BUTTON_TILE:
 
			if (!button_data.ValidateColour()) return false;
 
			if (!button_data.ValidateCursor()) return false;
 
			return true;
 
		case SPET_BUTTON_VEHICLE:
 
			if (!button_data.ValidateColour()) return false;
 
			if (!button_data.ValidateCursor()) return false;
 
			if (!button_data.ValidateVehicleType()) return false;
 
			return true;
 
		default:
 
			return false;
 
	}
 
@@ -88,10 +106,94 @@ static void UpdateElement(StoryPageEleme
 
		case SPET_GOAL:
 
			pe.referenced_id = (GoalID)reference;
 
			break;
 
		case SPET_BUTTON_PUSH:
 
		case SPET_BUTTON_TILE:
 
		case SPET_BUTTON_VEHICLE:
 
			pe.text = stredup(text);
 
			pe.referenced_id = reference;
 
			break;
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
/** Set the button background colour. */
 
void StoryPageButtonData::SetColour(Colours button_colour)
 
{
 
	assert(button_colour < COLOUR_END);
 
	SB(this->referenced_id, 0, 8, button_colour);
 
}
 

	
 
void StoryPageButtonData::SetFlags(StoryPageButtonFlags flags)
 
{
 
	SB(this->referenced_id, 24, 8, flags);
 
}
 

	
 
/** Set the mouse cursor used while waiting for input for the button. */
 
void StoryPageButtonData::SetCursor(StoryPageButtonCursor cursor)
 
{
 
	assert(cursor < SPBC_END);
 
	SB(this->referenced_id, 8, 8, cursor);
 
}
 

	
 
/** Set the type of vehicles that are accepted by the button */
 
void StoryPageButtonData::SetVehicleType(VehicleType vehtype)
 
{
 
	assert(vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END);
 
	SB(this->referenced_id, 16, 8, vehtype);
 
}
 

	
 
/** Get the button background colour. */
 
Colours StoryPageButtonData::GetColour() const
 
{
 
	return Extract<Colours, 0, 8>(this->referenced_id);
 
}
 

	
 
StoryPageButtonFlags StoryPageButtonData::GetFlags() const
 
{
 
	return (StoryPageButtonFlags)GB(this->referenced_id, 24, 8);
 
}
 

	
 
/** Get the mouse cursor used while waiting for input for the button. */
 
StoryPageButtonCursor StoryPageButtonData::GetCursor() const
 
{
 
	return Extract<StoryPageButtonCursor, 8, 8>(this->referenced_id);
 
}
 

	
 
/** Get the type of vehicles that are accepted by the button */
 
VehicleType StoryPageButtonData::GetVehicleType() const
 
{
 
	return (VehicleType)GB(this->referenced_id, 16, 8);
 
}
 

	
 
/** Verify that the data stored a valid Colour value */
 
bool StoryPageButtonData::ValidateColour() const
 
{
 
	return GB(this->referenced_id, 0, 8) < COLOUR_END;
 
}
 

	
 
bool StoryPageButtonData::ValidateFlags() const
 
{
 
	byte flags = GB(this->referenced_id, 24, 8);
 
	/* Don't allow float left and right together */
 
	if ((flags & SPBF_FLOAT_LEFT) && (flags & SPBF_FLOAT_RIGHT)) return false;
 
	/* Don't allow undefined flags */
 
	if (flags & ~(SPBF_FLOAT_LEFT | SPBF_FLOAT_RIGHT)) return false;
 
	return true;
 
}
 

	
 
/** Verify that the data stores a valid StoryPageButtonCursor value */
 
bool StoryPageButtonData::ValidateCursor() const
 
{
 
	return GB(this->referenced_id, 8, 8) < SPBC_END;
 
}
 

	
 
/** Verity that the data stored a valid VehicleType value */
 
bool StoryPageButtonData::ValidateVehicleType() const
 
{
 
	byte vehtype = GB(this->referenced_id, 16, 8);
 
	return vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END;
 
}
 

	
 
/**
 
 * Create a new story page.
 
 * @param tile unused.
 
@@ -358,3 +460,44 @@ CommandCost CmdRemoveStoryPageElement(Ti
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Clicked/used a button on a story page.
 
 * @param tile   Tile selected, for tile selection buttons, otherwise unused.
 
 * @param flags  Type of operation.
 
 * @param p1     Bit 0..15 = story page element id of button.
 
 * @param p2     ID of selected item for buttons that select an item (e.g. vehicle), otherwise unused.
 
 * @param text   Unused.
 
 * @return The cost of the operation, or an error.
 
 */
 
CommandCost CmdStoryPageButton(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	StoryPageElementID page_element_id = (StoryPageElementID)GB(p1, 0, 16);
 

	
 
	if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
 
	const StoryPageElement *const pe = StoryPageElement::Get(page_element_id);
 

	
 
	/* Check the player belongs to the company that owns the page. */
 
	const StoryPage *const sp = StoryPage::Get(pe->page);
 
	if (sp->company != INVALID_COMPANY && sp->company != _current_company) return CMD_ERROR;
 

	
 
	switch (pe->type) {
 
		case SPET_BUTTON_PUSH:
 
			/* No validation required */
 
			if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageButtonClick(_current_company, pe->page, page_element_id));
 
			break;
 
		case SPET_BUTTON_TILE:
 
			if (!IsValidTile(tile)) return CMD_ERROR;
 
			if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageTileSelect(_current_company, pe->page, page_element_id, tile));
 
			break;
 
		case SPET_BUTTON_VEHICLE:
 
			if (!Vehicle::IsValidID(p2)) return CMD_ERROR;
 
			if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageVehicleSelect(_current_company, pe->page, page_element_id, (VehicleID)p2));
 
			break;
 
		default:
 
			/* Invalid page element type, not a button. */
 
			return CMD_ERROR;
 
	}
 

	
 
	return CommandCost();
 
}
 

	
src/story_base.h
Show inline comments
 
@@ -13,6 +13,8 @@
 
#include "company_type.h"
 
#include "story_type.h"
 
#include "date_type.h"
 
#include "gfx_type.h"
 
#include "vehicle_type.h"
 
#include "core/pool_type.hpp"
 

	
 
typedef Pool<StoryPageElement, StoryPageElementID, 64, 64000> StoryPageElementPool;
 
@@ -26,9 +28,12 @@ extern uint32 _story_page_next_sort_valu
 
 * Each story page element is one of these types.
 
 */
 
enum StoryPageElementType : byte {
 
	SPET_TEXT = 0, ///< A text element.
 
	SPET_LOCATION, ///< An element that references a tile along with a one-line text.
 
	SPET_GOAL,     ///< An element that references a goal.
 
	SPET_TEXT = 0,       ///< A text element.
 
	SPET_LOCATION,       ///< An element that references a tile along with a one-line text.
 
	SPET_GOAL,           ///< An element that references a goal.
 
	SPET_BUTTON_PUSH,    ///< A push button that triggers an immediate event.
 
	SPET_BUTTON_TILE,    ///< A button that allows the player to select a tile, and triggers an event with the tile.
 
	SPET_BUTTON_VEHICLE, ///< A button that allows the player to select a vehicle, and triggers an event wih the vehicle.
 
	SPET_END,
 
	INVALID_SPET = 0xFF,
 
};
 
@@ -36,18 +41,108 @@ enum StoryPageElementType : byte {
 
/** Define basic enum properties */
 
template <> struct EnumPropsT<StoryPageElementType> : MakeEnumPropsT<StoryPageElementType, byte, SPET_TEXT, SPET_END, INVALID_SPET, 8> {};
 

	
 
/** Flags available for buttons */
 
enum StoryPageButtonFlags : byte {
 
	SPBF_NONE        = 0,
 
	SPBF_FLOAT_LEFT  = 1 << 0,
 
	SPBF_FLOAT_RIGHT = 1 << 1,
 
};
 
DECLARE_ENUM_AS_BIT_SET(StoryPageButtonFlags)
 

	
 
/** Mouse cursors usable by story page buttons. */
 
enum StoryPageButtonCursor : byte {
 
	SPBC_MOUSE,
 
	SPBC_ZZZ,
 
	SPBC_BUOY,
 
	SPBC_QUERY,
 
	SPBC_HQ,
 
	SPBC_SHIP_DEPOT,
 
	SPBC_SIGN,
 
	SPBC_TREE,
 
	SPBC_BUY_LAND,
 
	SPBC_LEVEL_LAND,
 
	SPBC_TOWN,
 
	SPBC_INDUSTRY,
 
	SPBC_ROCKY_AREA,
 
	SPBC_DESERT,
 
	SPBC_TRANSMITTER,
 
	SPBC_AIRPORT,
 
	SPBC_DOCK,
 
	SPBC_CANAL,
 
	SPBC_LOCK,
 
	SPBC_RIVER,
 
	SPBC_AQUEDUCT,
 
	SPBC_BRIDGE,
 
	SPBC_RAIL_STATION,
 
	SPBC_TUNNEL_RAIL,
 
	SPBC_TUNNEL_ELRAIL,
 
	SPBC_TUNNEL_MONO,
 
	SPBC_TUNNEL_MAGLEV,
 
	SPBC_AUTORAIL,
 
	SPBC_AUTOELRAIL,
 
	SPBC_AUTOMONO,
 
	SPBC_AUTOMAGLEV,
 
	SPBC_WAYPOINT,
 
	SPBC_RAIL_DEPOT,
 
	SPBC_ELRAIL_DEPOT,
 
	SPBC_MONO_DEPOT,
 
	SPBC_MAGLEV_DEPOT,
 
	SPBC_CONVERT_RAIL,
 
	SPBC_CONVERT_ELRAIL,
 
	SPBC_CONVERT_MONO,
 
	SPBC_CONVERT_MAGLEV,
 
	SPBC_AUTOROAD,
 
	SPBC_AUTOTRAM,
 
	SPBC_ROAD_DEPOT,
 
	SPBC_BUS_STATION,
 
	SPBC_TRUCK_STATION,
 
	SPBC_ROAD_TUNNEL,
 
	SPBC_CLONE_TRAIN,
 
	SPBC_CLONE_ROADVEH,
 
	SPBC_CLONE_SHIP,
 
	SPBC_CLONE_AIRPLANE,
 
	SPBC_DEMOLISH,
 
	SPBC_LOWERLAND,
 
	SPBC_RAISELAND,
 
	SPBC_PICKSTATION,
 
	SPBC_BUILDSIGNALS,
 
	SPBC_END,
 
	INVALID_SPBC = 0xFF
 
};
 

	
 
/** Define basic enum properties */
 
template <> struct EnumPropsT<StoryPageButtonCursor> : MakeEnumPropsT<StoryPageButtonCursor, byte, SPBC_MOUSE, SPBC_END, INVALID_SPBC, 8> {};
 

	
 
/** Helper to construct packed "id" values for button-type StoryPageElement */
 
struct StoryPageButtonData {
 
	uint32 referenced_id;
 

	
 
	void SetColour(Colours button_colour);
 
	void SetFlags(StoryPageButtonFlags flags);
 
	void SetCursor(StoryPageButtonCursor cursor);
 
	void SetVehicleType(VehicleType vehtype);
 
	Colours GetColour() const;
 
	StoryPageButtonFlags GetFlags() const;
 
	StoryPageButtonCursor GetCursor() const;
 
	VehicleType GetVehicleType() const;
 
	bool ValidateColour() const;
 
	bool ValidateFlags() const;
 
	bool ValidateCursor() const;
 
	bool ValidateVehicleType() const;
 
};
 

	
 
/**
 
 * Struct about story page elements.
 
 * Each StoryPage is composed of one or more page elements that provide
 
 * page content. Each element only contain one type of content.
 
 **/
 
struct StoryPageElement : StoryPageElementPool::PoolItem<&_story_page_element_pool> {
 
	uint32 sort_value;   ///< A number that increases for every created story page element. Used for sorting. The id of a story page element is the pool index.
 
	StoryPageID page; ///< Id of the page which the page element belongs to
 
	uint32 sort_value;         ///< A number that increases for every created story page element. Used for sorting. The id of a story page element is the pool index.
 
	StoryPageID page;          ///< Id of the page which the page element belongs to
 
	StoryPageElementType type; ///< Type of page element
 

	
 
	uint32 referenced_id; ///< Id of referenced object (location, goal etc.)
 
	char *text;           ///< Static content text of page element
 
	uint32 referenced_id;      ///< Id of referenced object (location, goal etc.)
 
	char *text;                ///< Static content text of page element
 

	
 
	/**
 
	 * We need an (empty) constructor so struct isn't zeroed (as C++ standard states)
src/story_gui.cpp
Show inline comments
 
@@ -23,26 +23,47 @@
 
#include "viewport_func.h"
 
#include "window_func.h"
 
#include "company_base.h"
 
#include "tilehighlight_func.h"
 
#include "vehicle_base.h"
 

	
 
#include "widgets/story_widget.h"
 

	
 
#include "table/strings.h"
 
#include "table/sprites.h"
 

	
 
#include <numeric>
 

	
 
#include "safeguards.h"
 

	
 
static CursorID TranslateStoryPageButtonCursor(StoryPageButtonCursor cursor);
 

	
 
typedef GUIList<const StoryPage*> GUIStoryPageList;
 
typedef GUIList<const StoryPageElement*> GUIStoryPageElementList;
 

	
 
struct StoryBookWindow : Window {
 
protected:
 
	struct LayoutCacheElement {
 
		const StoryPageElement *pe;
 
		Rect bounds;
 
	};
 
	typedef std::vector<LayoutCacheElement> LayoutCache;
 

	
 
	enum class ElementFloat {
 
		None,
 
		Left,
 
		Right,
 
	};
 

	
 
	Scrollbar *vscroll;                ///< Scrollbar of the page text.
 
	mutable LayoutCache layout_cache;  ///< Cached element layout.
 

	
 
	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.
 

	
 
	StoryPageElementID active_button_id; ///< Which button element the player is currently using
 

	
 
	static GUIStoryPageList::SortFunction * const page_sorter_funcs[];
 
	static GUIStoryPageElementList::SortFunction * const page_element_sorter_funcs[];
 

	
 
@@ -91,6 +112,7 @@ protected:
 
		}
 

	
 
		this->story_page_elements.Sort();
 
		this->InvalidateStoryPageElementLayout();
 
	}
 

	
 
	/** Sort story page elements by order value. */
 
@@ -174,6 +196,8 @@ protected:
 
		this->story_page_elements.ForceRebuild();
 
		this->BuildStoryPageElementList();
 

	
 
		if (this->active_button_id != INVALID_STORY_PAGE_ELEMENT) ResetObjectToPlace();
 

	
 
		this->vscroll->SetCount(this->GetContentHeight());
 
		this->SetWidgetDirty(WID_SB_SCROLLBAR);
 
		this->SetWidgetDirty(WID_SB_SEL_PAGE);
 
@@ -251,9 +275,9 @@ protected:
 
	/**
 
	 * Get the width available for displaying content on the page panel.
 
	 */
 
	uint GetAvailablePageContentWidth()
 
	uint GetAvailablePageContentWidth() const
 
	{
 
		return this->GetWidget<NWidgetCore>(WID_SB_PAGE_PANEL)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT;
 
		return this->GetWidget<NWidgetCore>(WID_SB_PAGE_PANEL)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT - 1;
 
	}
 

	
 
	/**
 
@@ -304,20 +328,26 @@ protected:
 
	 * @param max_width Available width to display content.
 
	 * @return the height in pixels.
 
	 */
 
	uint GetPageElementHeight(const StoryPageElement &pe, int max_width)
 
	uint GetPageElementHeight(const StoryPageElement &pe, int max_width) const
 
	{
 
		switch (pe.type) {
 
			case SPET_TEXT:
 
				SetDParamStr(0, pe.text);
 
				return GetStringHeight(STR_BLACK_RAW_STRING, max_width);
 
				break;
 

	
 
			case SPET_GOAL:
 
			case SPET_LOCATION: {
 
				Dimension sprite_dim = GetSpriteSize(GetPageElementSprite(pe));
 
				return sprite_dim.height;
 
				break;
 
			}
 

	
 
			case SPET_BUTTON_PUSH:
 
			case SPET_BUTTON_TILE:
 
			case SPET_BUTTON_VEHICLE: {
 
				Dimension dim = GetStringBoundingBox(pe.text, FS_NORMAL);
 
				return dim.height + WD_BEVEL_TOP + WD_BEVEL_BOTTOM + WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
 
			}
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
@@ -325,27 +355,161 @@ protected:
 
	}
 

	
 
	/**
 
	 * Get the total height of the content displayed
 
	 * in this window.
 
	 * Get the float style of a page element.
 
	 * @param pe The story page element.
 
	 * @return The float style.
 
	 */
 
	ElementFloat GetPageElementFloat(const StoryPageElement &pe) const
 
	{
 
		switch (pe.type) {
 
			case SPET_BUTTON_PUSH:
 
			case SPET_BUTTON_TILE:
 
			case SPET_BUTTON_VEHICLE: {
 
				StoryPageButtonFlags flags = StoryPageButtonData{ pe.referenced_id }.GetFlags();
 
				if (flags & SPBF_FLOAT_LEFT) return ElementFloat::Left;
 
				if (flags & SPBF_FLOAT_RIGHT) return ElementFloat::Right;
 
				return ElementFloat::None;
 
			}
 

	
 
			default:
 
				return ElementFloat::None;
 
		}
 
	}
 

	
 
	/**
 
	 * Get the width a page element would use if it was floating left or right.
 
	 * @param pe The story page element.
 
	 * @return The calculated width of the element.
 
	 */
 
	int GetPageElementFloatWidth(const StoryPageElement &pe) const
 
	{
 
		switch (pe.type) {
 
			case SPET_BUTTON_PUSH:
 
			case SPET_BUTTON_TILE:
 
			case SPET_BUTTON_VEHICLE: {
 
				Dimension dim = GetStringBoundingBox(pe.text, FS_NORMAL);
 
				return dim.width + WD_BEVEL_LEFT + WD_BEVEL_RIGHT + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
 
			}
 

	
 
			default:
 
				NOT_REACHED(); // only buttons can float
 
		}
 
	}
 

	
 
	/** Invalidate the current page layout */
 
	void InvalidateStoryPageElementLayout()
 
	{
 
		this->layout_cache.clear();
 
	}
 

	
 
	/** Create the page layout if it is missing */
 
	void EnsureStoryPageElementLayout() const
 
	{
 
		/* Assume if the layout cache has contents it is valid */
 
		if (!this->layout_cache.empty()) return;
 

	
 
		StoryPage *page = this->GetSelPage();
 
		if (page == nullptr) return;
 
		int max_width = GetAvailablePageContentWidth();
 
		int element_dist = FONT_HEIGHT_NORMAL;
 

	
 
		/* Make space for the header */
 
		int main_y = GetHeadHeight(max_width) + element_dist;
 

	
 
		/* Current bottom of left/right column */
 
		int left_y = main_y;
 
		int right_y = main_y;
 
		/* Current width of left/right column, 0 indicates no content in column */
 
		int left_width = 0;
 
		int right_width = 0;
 
		/* Indexes into element cache for yet unresolved floats */
 
		std::vector<size_t> left_floats;
 
		std::vector<size_t> right_floats;
 

	
 
		/* Build layout */
 
		for (const StoryPageElement *pe : this->story_page_elements) {
 
			ElementFloat fl = this->GetPageElementFloat(*pe);
 

	
 
			if (fl == ElementFloat::None) {
 
				/* Verify available width */
 
				const int min_required_width = 10 * FONT_HEIGHT_NORMAL;
 
				int left_offset = (left_width == 0) ? 0 : (left_width + element_dist);
 
				int right_offset = (right_width == 0) ? 0 : (right_width + element_dist);
 
				if (left_offset + right_offset + min_required_width >= max_width) {
 
					/* Width of floats leave too little for main content, push down */
 
					main_y = max(main_y, left_y);
 
					main_y = max(main_y, right_y);
 
					left_width = right_width = 0;
 
					left_offset = right_offset = 0;
 
					/* Do not add element_dist here, to keep together elements which were supposed to float besides each other. */
 
				}
 
				/* Determine height */
 
				const int available_width = max_width - left_offset - right_offset;
 
				const int height = GetPageElementHeight(*pe, available_width);
 
				/* Check for button that needs extra margin */
 
				if (left_offset == 0 && right_offset == 0) {
 
					switch (pe->type) {
 
						case SPET_BUTTON_PUSH:
 
						case SPET_BUTTON_TILE:
 
						case SPET_BUTTON_VEHICLE:
 
							left_offset = right_offset = available_width / 5;
 
							break;
 
						default:
 
							break;
 
					}
 
				}
 
				/* Position element in main column */
 
				LayoutCacheElement ce{ pe };
 
				ce.bounds.left = left_offset;
 
				ce.bounds.right = max_width - right_offset;
 
				ce.bounds.top = main_y;
 
				main_y += height;
 
				ce.bounds.bottom = main_y;
 
				this->layout_cache.push_back(ce);
 
				main_y += element_dist;
 
				/* Clear all floats */
 
				left_width = right_width = 0;
 
				left_y = right_y = main_y = max(main_y, max(left_y, right_y));
 
				left_floats.clear();
 
				right_floats.clear();
 
			} else {
 
				/* Prepare references to correct column */
 
				int &cur_width = (fl == ElementFloat::Left) ? left_width : right_width;
 
				int &cur_y = (fl == ElementFloat::Left) ? left_y : right_y;
 
				std::vector<size_t> &cur_floats = (fl == ElementFloat::Left) ? left_floats : right_floats;
 
				/* Position element */
 
				cur_width = max(cur_width, this->GetPageElementFloatWidth(*pe));
 
				LayoutCacheElement ce{ pe };
 
				ce.bounds.left = (fl == ElementFloat::Left) ? 0 : (max_width - cur_width);
 
				ce.bounds.right = (fl == ElementFloat::Left) ? cur_width : max_width;
 
				ce.bounds.top = cur_y;
 
				cur_y += GetPageElementHeight(*pe, cur_width);
 
				ce.bounds.bottom = cur_y;
 
				cur_floats.push_back(this->layout_cache.size());
 
				this->layout_cache.push_back(ce);
 
				cur_y += element_dist;
 
				/* Update floats in column to all have the same width */
 
				for (size_t index : cur_floats) {
 
					LayoutCacheElement &ce = this->layout_cache[index];
 
					ce.bounds.left = (fl == ElementFloat::Left) ? 0 : (max_width - cur_width);
 
					ce.bounds.right = (fl == ElementFloat::Left) ? cur_width : max_width;
 
				}
 
			}
 
		}
 
	}
 

	
 
	/**
 
	 * Get the total height of the content displayed in this window.
 
	 * @return the height in pixels
 
	 */
 
	uint GetContentHeight()
 
	{
 
		StoryPage *page = this->GetSelPage();
 
		if (page == nullptr) return 0;
 
		int max_width = GetAvailablePageContentWidth();
 
		uint element_vertical_dist = FONT_HEIGHT_NORMAL;
 

	
 
		/* Head */
 
		uint height = GetHeadHeight(max_width);
 
		this->EnsureStoryPageElementLayout();
 

	
 
		/* Body */
 
		for (const StoryPageElement *pe : this->story_page_elements) {
 
			height += element_vertical_dist;
 
			height += GetPageElementHeight(*pe, max_width);
 
		}
 
		/* The largest bottom coordinate of any element is the height of the content */
 
		uint max_y = std::accumulate(this->layout_cache.begin(), this->layout_cache.end(), 0, [](uint max_y, const LayoutCacheElement &ce) -> uint { return max<uint>(max_y, ce.bounds.bottom); });
 

	
 
		return height;
 
		return max_y;
 
	}
 

	
 
	/**
 
@@ -396,6 +560,39 @@ protected:
 
				ShowGoalsList((CompanyID)this->window_number);
 
				break;
 

	
 
			case SPET_BUTTON_PUSH:
 
				if (this->active_button_id != INVALID_STORY_PAGE_ELEMENT) ResetObjectToPlace();
 
				this->active_button_id = pe.index;
 
				this->SetTimeout();
 
				this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 

	
 
				DoCommandP(0, pe.index, 0, CMD_STORY_PAGE_BUTTON);
 
				break;
 

	
 
			case SPET_BUTTON_TILE:
 
				if (this->active_button_id == pe.index) {
 
					ResetObjectToPlace();
 
					this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
				} else {
 
					CursorID cursor = TranslateStoryPageButtonCursor(StoryPageButtonData{ pe.referenced_id }.GetCursor());
 
					SetObjectToPlaceWnd(cursor, PAL_NONE, HT_RECT, this);
 
					this->active_button_id = pe.index;
 
				}
 
				this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
				break;
 

	
 
			case SPET_BUTTON_VEHICLE:
 
				if (this->active_button_id == pe.index) {
 
					ResetObjectToPlace();
 
					this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
				} else {
 
					CursorID cursor = TranslateStoryPageButtonCursor(StoryPageButtonData{ pe.referenced_id }.GetCursor());
 
					SetObjectToPlaceWnd(cursor, PAL_NONE, HT_VEHICLE, this);
 
					this->active_button_id = pe.index;
 
				}
 
				this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
				break;
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
@@ -422,6 +619,8 @@ public:
 
		this->selected_generic_title[0] = '\0';
 
		this->selected_page_id = INVALID_STORY_PAGE;
 

	
 
		this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 

	
 
		this->OnInvalidateData(-1);
 
	}
 

	
 
@@ -443,6 +642,8 @@ public:
 
	void SetSelectedPage(uint16 page_index)
 
	{
 
		if (this->selected_page_id != page_index) {
 
			if (this->active_button_id) ResetObjectToPlace();
 
			this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
			this->selected_page_id = page_index;
 
			this->RefreshSelectedPage();
 
			this->UpdatePrevNextDisabledState();
 
@@ -504,7 +705,8 @@ public:
 

	
 
		/* Draw content (now coordinates given to Draw** are local to the new clipping region). */
 
		int line_height = FONT_HEIGHT_NORMAL;
 
		int y_offset = - this->vscroll->GetPosition();
 
		const int scrollpos = this->vscroll->GetPosition();
 
		int y_offset = -scrollpos;
 

	
 
		/* Date */
 
		if (page->date != INVALID_DATE) {
 
@@ -518,28 +720,46 @@ public:
 
		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 pe : this->story_page_elements) {
 
			y_offset += line_height; // margin to previous element
 

	
 
			switch (pe->type) {
 
		this->EnsureStoryPageElementLayout();
 
		for (const LayoutCacheElement &ce : this->layout_cache) {
 
			y_offset = ce.bounds.top - scrollpos;
 
			switch (ce.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);
 
					SetDParamStr(0, ce.pe->text);
 
					y_offset = DrawStringMultiLine(ce.bounds.left, ce.bounds.right, ce.bounds.top - scrollpos, ce.bounds.bottom - scrollpos, STR_JUST_RAW_STRING, TC_BLACK, SA_TOP | SA_LEFT);
 
					break;
 

	
 
				case SPET_GOAL: {
 
					Goal *g = Goal::Get((GoalID) pe->referenced_id);
 
					Goal *g = Goal::Get((GoalID) ce.pe->referenced_id);
 
					StringID string_id = g == nullptr ? STR_STORY_BOOK_INVALID_GOAL_REF : STR_JUST_RAW_STRING;
 
					if (g != nullptr) SetDParamStr(0, g->text);
 
					DrawActionElement(y_offset, right - x, line_height, GetPageElementSprite(*pe), string_id);
 
					DrawActionElement(y_offset, ce.bounds.right - ce.bounds.left, line_height, GetPageElementSprite(*ce.pe), string_id);
 
					break;
 
				}
 

	
 
				case SPET_LOCATION:
 
					SetDParamStr(0, pe->text);
 
					DrawActionElement(y_offset, right - x, line_height, GetPageElementSprite(*pe));
 
					SetDParamStr(0, ce.pe->text);
 
					DrawActionElement(y_offset, ce.bounds.right - ce.bounds.left, line_height, GetPageElementSprite(*ce.pe));
 
					break;
 

	
 
				case SPET_BUTTON_PUSH:
 
				case SPET_BUTTON_TILE:
 
				case SPET_BUTTON_VEHICLE: {
 
					const int height = FONT_HEIGHT_NORMAL;
 
					const int tmargin = WD_BEVEL_TOP + WD_FRAMETEXT_TOP;
 
					const int bmargin = WD_BEVEL_BOTTOM + WD_FRAMETEXT_BOTTOM;
 
					const int width = ce.bounds.right - ce.bounds.left;
 
					const int hmargin = width / 5;
 
					const FrameFlags frame = this->active_button_id == ce.pe->index ? FR_LOWERED : FR_NONE;
 
					const Colours bgcolour = StoryPageButtonData{ ce.pe->referenced_id }.GetColour();
 

	
 
					DrawFrameRect(ce.bounds.left, ce.bounds.top - scrollpos, ce.bounds.right, ce.bounds.bottom - scrollpos - 1, bgcolour, frame);
 

	
 
					SetDParamStr(0, ce.pe->text);
 
					DrawString(ce.bounds.left + WD_BEVEL_LEFT, ce.bounds.right - WD_BEVEL_RIGHT, ce.bounds.top + tmargin - scrollpos, STR_JUST_RAW_STRING, TC_WHITE, SA_CENTER);
 
					break;
 
				}
 

	
 
				default: NOT_REACHED();
 
			}
 
		}
 
@@ -593,6 +813,7 @@ public:
 

	
 
	void OnResize() override
 
	{
 
		this->InvalidateStoryPageElementLayout();
 
		this->vscroll->SetCapacityFromWidget(this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM);
 
		this->vscroll->SetCount(this->GetContentHeight());
 
	}
 
@@ -625,27 +846,14 @@ public:
 
				break;
 

	
 
			case WID_SB_PAGE_PANEL: {
 
				uint clicked_y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP);
 
				uint max_width = GetAvailablePageContentWidth();
 

	
 
				/* Skip head rows. */
 
				uint head_height = this->GetHeadHeight(max_width);
 
				if (clicked_y < head_height) return;
 
				int clicked_y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP);
 
				this->EnsureStoryPageElementLayout();
 

	
 
				/* Detect if a page element was clicked. */
 
				uint y = head_height;
 
				uint element_vertical_dist = FONT_HEIGHT_NORMAL;
 
				for (const StoryPageElement *const pe : this->story_page_elements) {
 

	
 
					y += element_vertical_dist; // margin row
 

	
 
					uint content_height = GetPageElementHeight(*pe, max_width);
 
					if (clicked_y >= y && clicked_y < y + content_height) {
 
						this->OnPageElementClick(*pe);
 
				for (const LayoutCacheElement &ce : this->layout_cache) {
 
					if (clicked_y >= ce.bounds.top && clicked_y < ce.bounds.bottom && pt.x >= ce.bounds.left && pt.x < ce.bounds.right) {
 
						this->OnPageElementClick(*ce.pe);
 
						return;
 
					}
 

	
 
					y += content_height;
 
				}
 
			}
 
		}
 
@@ -700,6 +908,52 @@ public:
 
			this->RefreshSelectedPage();
 
		}
 
	}
 

	
 
	void OnTimeout() override
 
	{
 
		this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
		this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
	}
 

	
 
	void OnPlaceObject(Point pt, TileIndex tile) override
 
	{
 
		const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id);
 
		if (pe == nullptr || pe->type != SPET_BUTTON_TILE) {
 
			ResetObjectToPlace();
 
			this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
			this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
			return;
 
		}
 

	
 
		DoCommandP(tile, pe->index, 0, CMD_STORY_PAGE_BUTTON);
 
		ResetObjectToPlace();
 
	}
 

	
 
	bool OnVehicleSelect(const Vehicle *v) override
 
	{
 
		const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id);
 
		if (pe == nullptr || pe->type != SPET_BUTTON_VEHICLE) {
 
			ResetObjectToPlace();
 
			this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
			this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
			return false;
 
		}
 

	
 
		/* Check that the vehicle matches the requested type */
 
		StoryPageButtonData data{ pe->referenced_id };
 
		VehicleType wanted_vehtype = data.GetVehicleType();
 
		if (wanted_vehtype != VEH_INVALID && wanted_vehtype != v->type) return false;
 

	
 
		DoCommandP(0, pe->index, v->index, CMD_STORY_PAGE_BUTTON);
 
		ResetObjectToPlace();
 
		return true;
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
		this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
	}
 
};
 

	
 
GUIStoryPageList::SortFunction * const StoryBookWindow::page_sorter_funcs[] = {
 
@@ -742,6 +996,68 @@ static WindowDesc _story_book_desc(
 
	_nested_story_book_widgets, lengthof(_nested_story_book_widgets)
 
);
 

	
 
static CursorID TranslateStoryPageButtonCursor(StoryPageButtonCursor cursor)
 
{
 
	switch (cursor) {
 
		case SPBC_MOUSE:          return SPR_CURSOR_MOUSE;
 
		case SPBC_ZZZ:            return SPR_CURSOR_ZZZ;
 
		case SPBC_BUOY:           return SPR_CURSOR_BUOY;
 
		case SPBC_QUERY:          return SPR_CURSOR_QUERY;
 
		case SPBC_HQ:             return SPR_CURSOR_HQ;
 
		case SPBC_SHIP_DEPOT:     return SPR_CURSOR_SHIP_DEPOT;
 
		case SPBC_SIGN:           return SPR_CURSOR_SIGN;
 
		case SPBC_TREE:           return SPR_CURSOR_TREE;
 
		case SPBC_BUY_LAND:       return SPR_CURSOR_BUY_LAND;
 
		case SPBC_LEVEL_LAND:     return SPR_CURSOR_LEVEL_LAND;
 
		case SPBC_TOWN:           return SPR_CURSOR_TOWN;
 
		case SPBC_INDUSTRY:       return SPR_CURSOR_INDUSTRY;
 
		case SPBC_ROCKY_AREA:     return SPR_CURSOR_ROCKY_AREA;
 
		case SPBC_DESERT:         return SPR_CURSOR_DESERT;
 
		case SPBC_TRANSMITTER:    return SPR_CURSOR_TRANSMITTER;
 
		case SPBC_AIRPORT:        return SPR_CURSOR_AIRPORT;
 
		case SPBC_DOCK:           return SPR_CURSOR_DOCK;
 
		case SPBC_CANAL:          return SPR_CURSOR_CANAL;
 
		case SPBC_LOCK:           return SPR_CURSOR_LOCK;
 
		case SPBC_RIVER:          return SPR_CURSOR_RIVER;
 
		case SPBC_AQUEDUCT:       return SPR_CURSOR_AQUEDUCT;
 
		case SPBC_BRIDGE:         return SPR_CURSOR_BRIDGE;
 
		case SPBC_RAIL_STATION:   return SPR_CURSOR_RAIL_STATION;
 
		case SPBC_TUNNEL_RAIL:    return SPR_CURSOR_TUNNEL_RAIL;
 
		case SPBC_TUNNEL_ELRAIL:  return SPR_CURSOR_TUNNEL_ELRAIL;
 
		case SPBC_TUNNEL_MONO:    return SPR_CURSOR_TUNNEL_MONO;
 
		case SPBC_TUNNEL_MAGLEV:  return SPR_CURSOR_TUNNEL_MAGLEV;
 
		case SPBC_AUTORAIL:       return SPR_CURSOR_AUTORAIL;
 
		case SPBC_AUTOELRAIL:     return SPR_CURSOR_AUTOELRAIL;
 
		case SPBC_AUTOMONO:       return SPR_CURSOR_AUTOMONO;
 
		case SPBC_AUTOMAGLEV:     return SPR_CURSOR_AUTOMAGLEV;
 
		case SPBC_WAYPOINT:       return SPR_CURSOR_WAYPOINT;
 
		case SPBC_RAIL_DEPOT:     return SPR_CURSOR_RAIL_DEPOT;
 
		case SPBC_ELRAIL_DEPOT:   return SPR_CURSOR_ELRAIL_DEPOT;
 
		case SPBC_MONO_DEPOT:     return SPR_CURSOR_MONO_DEPOT;
 
		case SPBC_MAGLEV_DEPOT:   return SPR_CURSOR_MAGLEV_DEPOT;
 
		case SPBC_CONVERT_RAIL:   return SPR_CURSOR_CONVERT_RAIL;
 
		case SPBC_CONVERT_ELRAIL: return SPR_CURSOR_CONVERT_ELRAIL;
 
		case SPBC_CONVERT_MONO:   return SPR_CURSOR_CONVERT_MONO;
 
		case SPBC_CONVERT_MAGLEV: return SPR_CURSOR_CONVERT_MAGLEV;
 
		case SPBC_AUTOROAD:       return SPR_CURSOR_AUTOROAD;
 
		case SPBC_AUTOTRAM:       return SPR_CURSOR_AUTOTRAM;
 
		case SPBC_ROAD_DEPOT:     return SPR_CURSOR_ROAD_DEPOT;
 
		case SPBC_BUS_STATION:    return SPR_CURSOR_BUS_STATION;
 
		case SPBC_TRUCK_STATION:  return SPR_CURSOR_TRUCK_STATION;
 
		case SPBC_ROAD_TUNNEL:    return SPR_CURSOR_ROAD_TUNNEL;
 
		case SPBC_CLONE_TRAIN:    return SPR_CURSOR_CLONE_TRAIN;
 
		case SPBC_CLONE_ROADVEH:  return SPR_CURSOR_CLONE_ROADVEH;
 
		case SPBC_CLONE_SHIP:     return SPR_CURSOR_CLONE_SHIP;
 
		case SPBC_CLONE_AIRPLANE: return SPR_CURSOR_CLONE_AIRPLANE;
 
		case SPBC_DEMOLISH:       return ANIMCURSOR_DEMOLISH;
 
		case SPBC_LOWERLAND:      return ANIMCURSOR_LOWERLAND;
 
		case SPBC_RAISELAND:      return ANIMCURSOR_RAISELAND;
 
		case SPBC_PICKSTATION:    return ANIMCURSOR_PICKSTATION;
 
		case SPBC_BUILDSIGNALS:   return ANIMCURSOR_BUILDSIGNALS;
 
		default: return SPR_CURSOR_QUERY;
 
	}
 
}
 

	
 
/**
 
 * Raise or create the story book window for \a company, at page \a page_id.
 
 * @param company 'Owner' of the story book, may be #INVALID_COMPANY.
0 comments (0 inline, 0 general)