changeset 20384:0bb5569144f9 draft

(svn r25342) -Add: StoryPage data structures and GS API
author zuu <zuu@openttd.org>
date Sun, 09 Jun 2013 12:19:09 +0000
parents c34dc911cf68
children 9c9510cb8e82
files projects/openttd_vs100.vcxproj projects/openttd_vs100.vcxproj.filters projects/openttd_vs80.vcproj projects/openttd_vs90.vcproj source.list src/command.cpp src/command_type.h src/economy.cpp src/game/game_instance.cpp src/saveload/saveload.cpp src/saveload/story_sl.cpp src/script/api/game/game_story_page.hpp.sq src/script/api/script_object.cpp src/script/api/script_object.hpp src/script/api/script_story_page.cpp src/script/api/script_story_page.hpp src/script/api/script_types.hpp src/script/api/template/template_story_page.hpp.sq src/script/script_instance.cpp src/script/script_instance.hpp src/script/script_storage.hpp src/story.cpp src/story_base.h src/story_type.h
diffstat 24 files changed, 975 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/projects/openttd_vs100.vcxproj
+++ b/projects/openttd_vs100.vcxproj
@@ -366,6 +366,7 @@
     <ClCompile Include="..\src\string.cpp" />
     <ClCompile Include="..\src\stringfilter.cpp" />
     <ClCompile Include="..\src\strings.cpp" />
+    <ClCompile Include="..\src\story.cpp" />
     <ClCompile Include="..\src\subsidy.cpp" />
     <ClCompile Include="..\src\textbuf.cpp" />
     <ClCompile Include="..\src\texteff.cpp" />
@@ -575,6 +576,8 @@
     <ClInclude Include="..\src\station_type.h" />
     <ClInclude Include="..\src\statusbar_gui.h" />
     <ClInclude Include="..\src\stdafx.h" />
+    <ClInclude Include="..\src\story_base.h" />
+    <ClInclude Include="..\src\story_type.h" />
     <ClInclude Include="..\src\strgen\strgen.h" />
     <ClInclude Include="..\src\string_func.h" />
     <ClInclude Include="..\src\string_type.h" />
@@ -833,6 +836,7 @@
     <ClCompile Include="..\src\saveload\station_sl.cpp" />
     <ClCompile Include="..\src\saveload\storage_sl.cpp" />
     <ClCompile Include="..\src\saveload\strings_sl.cpp" />
+    <ClCompile Include="..\src\saveload\story_sl.cpp" />
     <ClCompile Include="..\src\saveload\subsidy_sl.cpp" />
     <ClCompile Include="..\src\saveload\town_sl.cpp" />
     <ClCompile Include="..\src\saveload\vehicle_sl.cpp" />
@@ -1005,6 +1009,7 @@
     <ClInclude Include="..\src\script\api\script_signlist.hpp" />
     <ClInclude Include="..\src\script\api\script_station.hpp" />
     <ClInclude Include="..\src\script\api\script_stationlist.hpp" />
+    <ClInclude Include="..\src\script\api\script_story_page.hpp" />
     <ClInclude Include="..\src\script\api\script_subsidy.hpp" />
     <ClInclude Include="..\src\script\api\script_subsidylist.hpp" />
     <ClInclude Include="..\src\script\api\script_testmode.hpp" />
@@ -1066,6 +1071,7 @@
     <ClCompile Include="..\src\script\api\script_signlist.cpp" />
     <ClCompile Include="..\src\script\api\script_station.cpp" />
     <ClCompile Include="..\src\script\api\script_stationlist.cpp" />
+    <ClCompile Include="..\src\script\api\script_story_page.cpp" />
     <ClCompile Include="..\src\script\api\script_subsidy.cpp" />
     <ClCompile Include="..\src\script\api\script_subsidylist.cpp" />
     <ClCompile Include="..\src\script\api\script_testmode.cpp" />
--- a/projects/openttd_vs100.vcxproj.filters
+++ b/projects/openttd_vs100.vcxproj.filters
@@ -327,6 +327,9 @@
     <ClCompile Include="..\src\strings.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\story.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\subsidy.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -954,6 +957,12 @@
     <ClInclude Include="..\src\stdafx.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\story_base.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\story_type.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\strgen\strgen.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -1728,6 +1737,9 @@
     <ClCompile Include="..\src\saveload\strings_sl.cpp">
       <Filter>Save/Load handlers</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\saveload\story_sl.cpp">
+      <Filter>Save/Load handlers</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\saveload\subsidy_sl.cpp">
       <Filter>Save/Load handlers</Filter>
     </ClCompile>
@@ -2244,6 +2256,9 @@
     <ClInclude Include="..\src\script\api\script_stationlist.hpp">
       <Filter>Script API</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\script\api\script_story_page.hpp">
+      <Filter>Script API</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\script\api\script_subsidy.hpp">
       <Filter>Script API</Filter>
     </ClInclude>
@@ -2427,6 +2442,9 @@
     <ClCompile Include="..\src\script\api\script_stationlist.cpp">
       <Filter>Script API Implementation</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\script\api\script_story_page.cpp">
+      <Filter>Script API Implementation</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\script\api\script_subsidy.cpp">
       <Filter>Script API Implementation</Filter>
     </ClCompile>
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -735,6 +735,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\story.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\subsidy.cpp"
 				>
 			</File>
@@ -1575,6 +1579,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\story_base.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\story_type.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\strgen\strgen.h"
 				>
 			</File>
@@ -2627,6 +2639,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\saveload\story_sl.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\saveload\subsidy_sl.cpp"
 				>
 			</File>
@@ -3355,6 +3371,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\script\api\script_story_page.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\script\api\script_subsidy.hpp"
 				>
 			</File>
@@ -3603,6 +3623,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\script\api\script_story_page.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\script\api\script_subsidy.cpp"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -732,6 +732,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\story.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\subsidy.cpp"
 				>
 			</File>
@@ -1572,6 +1576,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\story_base.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\story_type.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\strgen\strgen.h"
 				>
 			</File>
@@ -2624,6 +2636,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\saveload\story_sl.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\saveload\subsidy_sl.cpp"
 				>
 			</File>
@@ -3352,6 +3368,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\script\api\script_story_page.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\script\api\script_subsidy.hpp"
 				>
 			</File>
@@ -3600,6 +3620,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\script\api\script_story_page.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\script\api\script_subsidy.cpp"
 				>
 			</File>
--- a/source.list
+++ b/source.list
@@ -76,6 +76,7 @@
 string.cpp
 stringfilter.cpp
 strings.cpp
+story.cpp
 subsidy.cpp
 textbuf.cpp
 texteff.cpp
@@ -308,6 +309,8 @@
 station_type.h
 statusbar_gui.h
 stdafx.h
+story_base.h
+story_type.h
 strgen/strgen.h
 string_func.h
 string_type.h
@@ -591,6 +594,7 @@
 saveload/station_sl.cpp
 saveload/storage_sl.cpp
 saveload/strings_sl.cpp
+saveload/story_sl.cpp
 saveload/subsidy_sl.cpp
 saveload/town_sl.cpp
 saveload/vehicle_sl.cpp
@@ -783,6 +787,7 @@
 script/api/script_signlist.hpp
 script/api/script_station.hpp
 script/api/script_stationlist.hpp
+script/api/script_story_page.hpp
 script/api/script_subsidy.hpp
 script/api/script_subsidylist.hpp
 script/api/script_testmode.hpp
@@ -846,6 +851,7 @@
 script/api/script_signlist.cpp
 script/api/script_station.cpp
 script/api/script_stationlist.cpp
+script/api/script_story_page.cpp
 script/api/script_subsidy.cpp
 script/api/script_subsidylist.cpp
 script/api/script_testmode.cpp
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -155,6 +155,11 @@
 CommandProc CmdSetGoalCompleted;
 CommandProc CmdGoalQuestion;
 CommandProc CmdGoalQuestionAnswer;
+CommandProc CmdCreateStoryPage;
+CommandProc CmdCreateStoryPageElement;
+CommandProc CmdUpdateStoryPageElement;
+CommandProc CmdSetStoryPageTitle;
+CommandProc CmdRemoveStoryPage;
 
 CommandProc CmdLevelLand;
 
@@ -300,6 +305,11 @@
 	DEF_CMD(CmdSetGoalCompleted,        CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_SET_GOAL_COMPLETED
 	DEF_CMD(CmdGoalQuestion,            CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_GOAL_QUESTION
 	DEF_CMD(CmdGoalQuestionAnswer,                     CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_GOAL_QUESTION_ANSWER
+	DEF_CMD(CmdCreateStoryPage,         CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_CREATE_STORY_PAGE
+	DEF_CMD(CmdCreateStoryPageElement,  CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_CREATE_STORY_PAGE_ELEMENT
+	DEF_CMD(CmdUpdateStoryPageElement,  CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_UPDATE_STORY_PAGE_ELEMENT
+	DEF_CMD(CmdSetStoryPageTitle,       CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_SET_STORY_PAGE_TITLE
+	DEF_CMD(CmdRemoveStoryPage,         CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT      ), // CMD_REMOVE_STORY_PAGE
 
 	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
 
--- a/src/command_type.h
+++ b/src/command_type.h
@@ -271,6 +271,11 @@
 	CMD_SET_GOAL_COMPLETED,           ///< update goal completed status of a goal
 	CMD_GOAL_QUESTION,                ///< ask a goal related question
 	CMD_GOAL_QUESTION_ANSWER,         ///< answer(s) to CMD_GOAL_QUESTION
+	CMD_CREATE_STORY_PAGE,            ///< create a new story page
+	CMD_CREATE_STORY_PAGE_ELEMENT,    ///< create a new story page element
+	CMD_UPDATE_STORY_PAGE_ELEMENT,    ///< update a story page element
+	CMD_SET_STORY_PAGE_TITLE,         ///< update title of a story page
+	CMD_REMOVE_STORY_PAGE,            ///< remove a story page
 	CMD_LEVEL_LAND,                   ///< level land
 
 	CMD_BUILD_LOCK,                   ///< build a lock
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -46,6 +46,7 @@
 #include "game/game.hpp"
 #include "cargomonitor.h"
 #include "goal_base.h"
+#include "story_base.h"
 
 #include "table/strings.h"
 #include "table/pricebase.h"
@@ -509,7 +510,7 @@
 		if (si->owner == old_owner) si->owner = new_owner == INVALID_OWNER ? OWNER_NONE : new_owner;
 	}
 
-	/* Remove Game Script created Goals and CargoMonitors. */
+	/* Remove Game Script created Goals, CargoMonitors and Story pages. */
 	Goal *g;
 	FOR_ALL_GOALS(g) {
 		if (g->company == old_owner) delete g;
@@ -518,6 +519,11 @@
 	ClearCargoPickupMonitoring(old_owner);
 	ClearCargoDeliveryMonitoring(old_owner);
 
+	StoryPage *sp;
+	FOR_ALL_STORY_PAGES(sp) {
+		if (sp->company == old_owner) delete sp;
+	}
+
 	/* Change colour of existing windows */
 	if (new_owner != INVALID_OWNER) ChangeWindowOwner(old_owner, new_owner);
 
--- a/src/game/game_instance.cpp
+++ b/src/game/game_instance.cpp
@@ -66,6 +66,7 @@
 #include "../script/api/game/game_signlist.hpp.sq"
 #include "../script/api/game/game_station.hpp.sq"
 #include "../script/api/game/game_stationlist.hpp.sq"
+#include "../script/api/game/game_story_page.hpp.sq"
 #include "../script/api/game/game_subsidy.hpp.sq"
 #include "../script/api/game/game_subsidylist.hpp.sq"
 #include "../script/api/game/game_testmode.hpp.sq"
@@ -169,6 +170,7 @@
 	SQGSStation_Register(this->engine);
 	SQGSStationList_Register(this->engine);
 	SQGSStationList_Vehicle_Register(this->engine);
+	SQGSStoryPage_Register(this->engine);
 	SQGSSubsidy_Register(this->engine);
 	SQGSSubsidyList_Register(this->engine);
 	SQGSTestMode_Register(this->engine);
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -419,6 +419,7 @@
 extern const ChunkHandler _subsidy_chunk_handlers[];
 extern const ChunkHandler _cargomonitor_chunk_handlers[];
 extern const ChunkHandler _goal_chunk_handlers[];
+extern const ChunkHandler _story_page_chunk_handlers[];
 extern const ChunkHandler _ai_chunk_handlers[];
 extern const ChunkHandler _game_chunk_handlers[];
 extern const ChunkHandler _animated_tile_chunk_handlers[];
@@ -449,6 +450,7 @@
 	_subsidy_chunk_handlers,
 	_cargomonitor_chunk_handlers,
 	_goal_chunk_handlers,
+	_story_page_chunk_handlers,
 	_engine_chunk_handlers,
 	_town_chunk_handlers,
 	_sign_chunk_handlers,
new file mode 100644
--- /dev/null
+++ b/src/saveload/story_sl.cpp
@@ -0,0 +1,89 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file story_page_sl.cpp Code handling saving and loading of story pages */
+
+#include "../stdafx.h"
+#include "../story_base.h"
+
+#include "saveload.h"
+
+static const SaveLoad _story_page_elements_desc[] = {
+	    SLE_VAR(StoryPageElement, sort_value,    SLE_UINT16),
+	    SLE_VAR(StoryPageElement, page,          SLE_UINT16),
+	    SLE_VAR(StoryPageElement, type,          SLE_UINT16),
+	    SLE_VAR(StoryPageElement, referenced_id, SLE_UINT32),
+	    SLE_STR(StoryPageElement, text,          SLE_STR | SLF_ALLOW_CONTROL, 0),
+	    SLE_END()
+};
+
+static void Save_STORY_PAGE_ELEMENT()
+{
+	StoryPageElement *s;
+	FOR_ALL_STORY_PAGE_ELEMENTS(s) {
+		SlSetArrayIndex(s->index);
+		SlObject(s, _story_page_elements_desc);
+	}
+}
+
+static void Load_STORY_PAGE_ELEMENT()
+{
+	int index;
+	uint32 max_sort_value = 0;
+	while ((index = SlIterateArray()) != -1) {
+		StoryPageElement *s = new (index) StoryPageElement();
+		SlObject(s, _story_page_elements_desc);
+		if (s->sort_value > max_sort_value) {
+			max_sort_value = s->sort_value;
+		}
+	}
+	/* Update the next sort value, so that the next
+	 * created page is shown after all existing pages.
+	 */
+	_story_page_element_next_sort_value = max_sort_value + 1;
+}
+
+static const SaveLoad _story_pages_desc[] = {
+	    SLE_VAR(StoryPage, sort_value, SLE_UINT16),
+	    SLE_VAR(StoryPage, date,       SLE_UINT32),
+	    SLE_VAR(StoryPage, company,    SLE_UINT16),
+	    SLE_STR(StoryPage, title,      SLE_STR | SLF_ALLOW_CONTROL, 0),
+	    SLE_END()
+};
+
+static void Save_STORY_PAGE()
+{
+	StoryPage *s;
+	FOR_ALL_STORY_PAGES(s) {
+		SlSetArrayIndex(s->index);
+		SlObject(s, _story_pages_desc);
+	}
+}
+
+static void Load_STORY_PAGE()
+{
+	int index;
+	uint32 max_sort_value = 0;
+	while ((index = SlIterateArray()) != -1) {
+		StoryPage *s = new (index) StoryPage();
+		SlObject(s, _story_pages_desc);
+		if (s->sort_value > max_sort_value) {
+			max_sort_value = s->sort_value;
+		}
+	}
+	/* Update the next sort value, so that the next
+	 * created page is shown after all existing pages.
+	 */
+	_story_page_next_sort_value = max_sort_value + 1;
+}
+
+extern const ChunkHandler _story_page_chunk_handlers[] = {
+	{ 'STPE', Save_STORY_PAGE_ELEMENT, Load_STORY_PAGE_ELEMENT, NULL, NULL, CH_ARRAY},
+	{ 'STPA', Save_STORY_PAGE, Load_STORY_PAGE, NULL, NULL, CH_ARRAY | CH_LAST},
+};
new file mode 100644
--- /dev/null
+++ b/src/script/api/game/game_story_page.hpp.sq
@@ -0,0 +1,39 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "../script_story_page.hpp"
+#include "../template/template_story_page.hpp.sq"
+
+
+template <> const char *GetClassName<ScriptStoryPage, ST_GS>() { return "GSStoryPage"; }
+
+void SQGSStoryPage_Register(Squirrel *engine)
+{
+	DefSQClass<ScriptStoryPage, ST_GS> SQGSStoryPage("GSStoryPage");
+	SQGSStoryPage.PreRegister(engine);
+	SQGSStoryPage.AddConstructor<void (ScriptStoryPage::*)(), 1>(engine, "x");
+
+	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::STORY_PAGE_INVALID,         "STORY_PAGE_INVALID");
+	SQGSStoryPage.DefSQConst(engine, ScriptStoryPage::STORY_PAGE_ELEMENT_INVALID, "STORY_PAGE_ELEMENT_INVALID");
+	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.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::SetTitle,                "SetTitle",                3, ".i.");
+	SQGSStoryPage.DefSQStaticMethod(engine, &ScriptStoryPage::Remove,                  "Remove",                  2, ".i");
+
+	SQGSStoryPage.PostRegister(engine);
+}
--- a/src/script/api/script_object.cpp
+++ b/src/script/api/script_object.cpp
@@ -144,6 +144,8 @@
 	SetNewSignID(_new_sign_id);
 	SetNewGroupID(_new_group_id);
 	SetNewGoalID(_new_goal_id);
+	SetNewStoryPageID(_new_story_page_id);
+	SetNewStoryPageElementID(_new_story_page_element_id);
 }
 
 /* static */ bool ScriptObject::GetLastCommandRes()
@@ -191,6 +193,26 @@
 	return GetStorage()->new_goal_id;
 }
 
+/* static */ void ScriptObject::SetNewStoryPageID(StoryPageID story_page_id)
+{
+	GetStorage()->new_story_page_id = story_page_id;
+}
+
+/* static */ GroupID ScriptObject::GetNewStoryPageID()
+{
+	return GetStorage()->new_story_page_id;
+}
+
+/* static */ void ScriptObject::SetNewStoryPageElementID(StoryPageElementID story_page_element_id)
+{
+	GetStorage()->new_story_page_element_id = story_page_element_id;
+}
+
+/* static */ GroupID ScriptObject::GetNewStoryPageElementID()
+{
+	return GetStorage()->new_story_page_element_id;
+}
+
 /* static */ void ScriptObject::SetAllowDoCommand(bool allow)
 {
 	GetStorage()->allow_do_command = allow;
--- a/src/script/api/script_object.hpp
+++ b/src/script/api/script_object.hpp
@@ -167,6 +167,16 @@
 	static GoalID GetNewGoalID();
 
 	/**
+	 * Get the latest stored new_story_page_id.
+	 */
+	static StoryPageID GetNewStoryPageID();
+
+	/**
+	 * Get the latest stored new_story_page_id.
+	 */
+	static StoryPageID GetNewStoryPageElementID();
+
+	/**
 	 * Store a allow_do_command per company.
 	 * @param allow The new allow.
 	 */
@@ -266,6 +276,18 @@
 	 * @param goal_id The new GoalID.
 	 */
 	static void SetNewGoalID(GoalID goal_id);
+
+	/**
+	 * Store a new_story_page_id per company.
+	 * @param story_page_id The new StoryPageID.
+	 */
+	static void SetNewStoryPageID(StoryPageID story_page_id);
+
+	/**
+	 * Store a new_story_page_id per company.
+	 * @param story_page_id The new StoryPageID.
+	 */
+	static void SetNewStoryPageElementID(StoryPageElementID story_page_element_id);
 };
 
 #endif /* SCRIPT_OBJECT_HPP */
new file mode 100644
--- /dev/null
+++ b/src/script/api/script_story_page.cpp
@@ -0,0 +1,119 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file script_story_page.cpp Implementation of ScriptStoryPage. */
+
+#include "../../stdafx.h"
+#include "script_story_page.hpp"
+#include "script_error.hpp"
+#include "script_industry.hpp"
+#include "script_map.hpp"
+#include "script_town.hpp"
+#include "script_goal.hpp"
+#include "../script_instance.hpp"
+#include "../../story_base.h"
+#include "../../goal_base.h"
+#include "../../string_func.h"
+#include "../../tile_map.h"
+
+/* static */ bool ScriptStoryPage::IsValidStoryPage(StoryPageID story_page_id)
+{
+	return ::StoryPage::IsValidID(story_page_id);
+}
+
+/* static */ bool ScriptStoryPage::IsValidStoryPageElement(StoryPageElementID story_page_element_id)
+{
+	return ::StoryPageElement::IsValidID(story_page_element_id);
+}
+
+/* static */ ScriptStoryPage::StoryPageID ScriptStoryPage::New(ScriptCompany::CompanyID company, Text *title)
+{
+	CCountedPtr<Text> counter(title);
+
+	EnforcePrecondition(STORY_PAGE_INVALID, ScriptObject::GetCompany() == OWNER_DEITY);
+	EnforcePrecondition(STORY_PAGE_INVALID, company == ScriptCompany::COMPANY_INVALID || ScriptCompany::ResolveCompanyID(company) != ScriptCompany::COMPANY_INVALID);
+
+	uint8 c = company;
+	if (company == ScriptCompany::COMPANY_INVALID) c = INVALID_COMPANY;
+
+	if (!ScriptObject::DoCommand(0,
+		c,
+		0,
+		CMD_CREATE_STORY_PAGE,
+		title != NULL? title->GetEncodedText() : NULL,
+		&ScriptInstance::DoCommandReturnStoryPageID)) return STORY_PAGE_INVALID;
+
+	/* In case of test-mode, we return StoryPageID 0 */
+	return (ScriptStoryPage::StoryPageID)0;
+}
+
+/* static */ ScriptStoryPage::StoryPageElementID ScriptStoryPage::NewElement(StoryPageID story_page_id, StoryPageElementType type, uint32 reference, Text *text)
+{
+	CCountedPtr<Text> counter(text);
+
+	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 != NULL && !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,
+			story_page_id + (type << 16),
+			type == SPET_GOAL ? reference : 0,
+			CMD_CREATE_STORY_PAGE_ELEMENT,
+			type == SPET_TEXT || type == SPET_LOCATION ? text->GetEncodedText() : NULL,
+			&ScriptInstance::DoCommandReturnStoryPageElementID)) return STORY_PAGE_ELEMENT_INVALID;
+
+	/* In case of test-mode, we return StoryPageElementID 0 */
+	return (ScriptStoryPage::StoryPageElementID)0;
+}
+
+/* static */ bool ScriptStoryPage::UpdateElement(StoryPageElementID story_page_element_id, uint32 reference, Text *text)
+{
+	CCountedPtr<Text> counter(text);
+
+	EnforcePrecondition(false, ScriptObject::GetCompany() == OWNER_DEITY);
+	EnforcePrecondition(false, IsValidStoryPageElement(story_page_element_id));
+
+	StoryPageElement *pe = StoryPageElement::Get(story_page_element_id);
+	StoryPage *p = StoryPage::Get(pe->page);
+	::StoryPageElementType type = pe->type;
+
+	EnforcePrecondition(false, (type != SPET_TEXT && type != SPET_LOCATION) || (text != NULL && !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,
+			pe->page,
+			type == SPET_GOAL ? reference : 0,
+			CMD_UPDATE_STORY_PAGE_ELEMENT,
+			type == SPET_TEXT || type == SPET_LOCATION ? text->GetEncodedText() : NULL,
+			&ScriptInstance::DoCommandReturnStoryPageElementID);
+}
+
+/* static */ bool ScriptStoryPage::SetTitle(StoryPageID story_page_id, Text *title)
+{
+	CCountedPtr<Text> counter(title);
+
+	EnforcePrecondition(false, IsValidStoryPage(story_page_id));
+	EnforcePrecondition(false, ScriptObject::GetCompany() == OWNER_DEITY);
+
+	return ScriptObject::DoCommand(0, story_page_id, 0, CMD_SET_STORY_PAGE_TITLE, title != NULL? title->GetEncodedText() : NULL);
+}
+
+/* static */ bool ScriptStoryPage::Remove(StoryPageID story_page_id)
+{
+	EnforcePrecondition(false, ScriptObject::GetCompany() == OWNER_DEITY);
+	EnforcePrecondition(false, IsValidStoryPage(story_page_id));
+
+	return ScriptObject::DoCommand(0, story_page_id, 0, CMD_REMOVE_STORY_PAGE);
+}
+
new file mode 100644
--- /dev/null
+++ b/src/script/api/script_story_page.hpp
@@ -0,0 +1,142 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file script_story_page.hpp Everything to manipulate a story page. */
+
+#ifndef SCRIPT_STORY_HPP
+#define SCRIPT_STORY_HPP
+
+#include "script_company.hpp"
+#include "../../story_type.h"
+#include "../../story_base.h"
+
+/**
+ * Class that handles story page related functions.
+ *
+ * To create a page:
+ * 1. Create the page
+ * 2. Create page elements that will be appended to the page in the order which they are created.
+ *
+ * Pages can be either global or company specific. It is possible to mix, but the only mixed solution
+ * that will work is to have all global pages first. Once you create the first company specific page,
+ * it is not recommended to add additional global pages unless you clear up all pages first.
+ *
+ * Page elements are stacked vertically on a page. If goal elements are used, the element will
+ * become empty if the goal is removed while the page still exist. Instead of removing the goal,
+ * you can mark it as complete and the Story Book will show that the goal is completed.
+ *
+ * Mind that users might want to go back to old pages later on. Thus do not remove pages in
+ * the story book unless you really need to.
+ *
+ * @api game
+ */
+class ScriptStoryPage : public ScriptObject {
+public:
+	/**
+	 * The story page IDs.
+	 */
+	enum StoryPageID {
+		/* Note: these values represent part of the in-game StoryPageID enum */
+		STORY_PAGE_INVALID = ::INVALID_STORY_PAGE, ///< An invalid story page id.
+	};
+
+	/**
+	 * The story page element IDs.
+	 */
+	enum StoryPageElementID {
+		/* Note: these values represent part of the in-game StoryPageElementID enum */
+		STORY_PAGE_ELEMENT_INVALID = ::INVALID_STORY_PAGE_ELEMENT, ///< An invalid story page element id.
+	};
+
+	/**
+	 * 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.
+	};
+
+	/**
+	 * Check whether this is a valid story page ID.
+	 * @param story_page_id The StoryPageID to check.
+	 * @return True if and only if this story page is valid.
+	 */
+	static bool IsValidStoryPage(StoryPageID story_page_id);
+
+	/**
+	 * Check whether this is a valid story page element ID.
+	 * @param story_page_element_id The StoryPageElementID to check.
+	 * @return True if and only if this story page element is valid.
+	 */
+	static bool IsValidStoryPageElement(StoryPageElementID story_page_element_id);
+
+	/**
+	 * Create a new story page.
+	 * @param company The company to create the story page for, or ScriptCompany::COMPANY_INVALID for all.
+	 * @param title Page title (can be either a raw string, a ScriptText object, or null).
+	 * @return The new StoryPageID, or STORY_INVALID if it failed.
+	 * @pre No ScriptCompanyMode may be in scope.
+	 * @pre company == COMPANY_INVALID || ResolveCompanyID(company) != COMPANY_INVALID.
+	 */
+	static StoryPageID New(ScriptCompany::CompanyID company, Text *title);
+
+	/**
+	 * 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 refered 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 text The body text of page elements that allow custom text. (SPET_TEXT and SPET_LOCATION)
+	 * @return The new StoryPageID, or STORY_INVALID if it failed.
+	 * @pre No ScriptCompanyMode may be in scope.
+	 * @pre IsValidStoryPage(story_page).
+	 * @pre (type != SPET_TEXT && type != SPET_LOCATION) || (text != NULL && len(text) != 0).
+	 * @pre type != SPET_LOCATION || ScriptMap::IsValidTile(reference).
+	 * @pre type != SPET_GOAL || ScriptGoal::IsValidGoal(reference).
+	 * @pre if type is SPET_GOAL and story_page is a global page, then referenced goal must be global.
+	 */
+	static StoryPageElementID NewElement(StoryPageID story_page_id, StoryPageElementType type, uint32 reference, Text *text);
+
+	/**
+	 * Update the content of a page element
+	 * @param story_page_element_id The page id of the story page which the page element should be appended to.
+	 * @param reference A reference value to the object that is refered to by some page element types. See also NewElement.
+	 * @param text The body text of page elements that allow custom text. See also NewElement.
+	 * @return The new StoryPageID, or STORY_INVALID if it failed.
+	 * @pre No ScriptCompanyMode may be in scope.
+	 * @pre IsValidStoryPage(story_page).
+	 * @pre (type != SPET_TEXT && type != SPET_LOCATION) || (text != NULL && len(text) != 0).
+	 * @pre type != SPET_LOCATION || ScriptMap::IsValidTile(reference).
+	 * @pre type != SPET_GOAL || ScriptGoal::IsValidGoal(reference).
+	 * @pre if type is SPET_GOAL and story_page is a global page, then referenced goal must be global.
+	 */
+	static bool UpdateElement(StoryPageElementID story_page_element_id, uint32 reference, Text *text);
+
+	/**
+	 * Update title of a story page. The title is shown in the page selector drop down.
+	 * @param story_page_id The story page to update.
+	 * @param title Page title (can be either a raw string, a ScriptText object, or null).
+	 * @return True if the action succeeded.
+	 * @pre No ScriptCompanyMode may be in scope.
+	 * @pre IsValidStoryPage(story_page_id).
+	 */
+	static bool SetTitle(StoryPageID story_page_id, Text *title);
+
+	/**
+	 * Remove a story page from the list.
+	 * @param story_page_id The story page to remove.
+	 * @return True if the action succeeded.
+	 * @pre No ScriptCompanyMode may be in scope.
+	 * @pre IsValidStoryPage(story_page_id).
+	 */
+	static bool Remove(StoryPageID story_page_id);
+};
+
+#endif /* SCRIPT_STORY_HPP */
+
--- a/src/script/api/script_types.hpp
+++ b/src/script/api/script_types.hpp
@@ -100,6 +100,8 @@
 typedef uint16 StationID;    ///< The ID of a station.
 typedef uint16 StringID;     ///< The ID of a string.
 typedef uint16 SubsidyID;    ///< The ID of a subsidy.
+typedef uint16 StoryPageID;  ///< The ID of a story page.
+typedef uint16 StoryPageElementID; ///< The ID of a story page element.
 typedef uint32 TileIndex;    ///< The ID of a tile (just named differently).
 typedef uint16 TownID;       ///< The ID of a town.
 typedef uint32 VehicleID;    ///< The ID of a vehicle.
new file mode 100644
--- /dev/null
+++ b/src/script/api/template/template_story_page.hpp.sq
@@ -0,0 +1,29 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "../script_story_page.hpp"
+
+namespace SQConvert {
+	/* Allow enums to be used as Squirrel parameters */
+	template <> inline ScriptStoryPage::StoryPageID GetParam(ForceType<ScriptStoryPage::StoryPageID>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptStoryPage::StoryPageID)tmp; }
+	template <> inline int Return<ScriptStoryPage::StoryPageID>(HSQUIRRELVM vm, ScriptStoryPage::StoryPageID res) { sq_pushinteger(vm, (int32)res); return 1; }
+	template <> inline ScriptStoryPage::StoryPageElementID GetParam(ForceType<ScriptStoryPage::StoryPageElementID>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptStoryPage::StoryPageElementID)tmp; }
+	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; }
+
+	/* 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; }
+	template <> inline ScriptStoryPage &GetParam(ForceType<ScriptStoryPage &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptStoryPage *)instance; }
+	template <> inline const ScriptStoryPage *GetParam(ForceType<const ScriptStoryPage *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (ScriptStoryPage *)instance; }
+	template <> inline const ScriptStoryPage &GetParam(ForceType<const ScriptStoryPage &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptStoryPage *)instance; }
+	template <> inline int Return<ScriptStoryPage *>(HSQUIRRELVM vm, ScriptStoryPage *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "StoryPage", res, NULL, DefSQDestructorCallback<ScriptStoryPage>, true); return 1; }
+} // namespace SQConvert
--- a/src/script/script_instance.cpp
+++ b/src/script/script_instance.cpp
@@ -275,6 +275,16 @@
 	instance->engine->InsertResult(ScriptObject::GetNewGoalID());
 }
 
+/* static */ void ScriptInstance::DoCommandReturnStoryPageID(ScriptInstance *instance)
+{
+	instance->engine->InsertResult(ScriptObject::GetNewStoryPageID());
+}
+
+/* static */ void ScriptInstance::DoCommandReturnStoryPageElementID(ScriptInstance *instance)
+{
+	instance->engine->InsertResult(ScriptObject::GetNewStoryPageElementID());
+}
+
 ScriptStorage *ScriptInstance::GetStorage()
 {
 	return this->storage;
--- a/src/script/script_instance.hpp
+++ b/src/script/script_instance.hpp
@@ -108,6 +108,16 @@
 	static void DoCommandReturnGoalID(ScriptInstance *instance);
 
 	/**
+	 * Return a StoryPageID reply for a DoCommand.
+	 */
+	static void DoCommandReturnStoryPageID(ScriptInstance *instance);
+
+	/**
+	 * Return a StoryPageElementID reply for a DoCommand.
+	 */
+	static void DoCommandReturnStoryPageElementID(ScriptInstance *instance);
+
+	/**
 	 * Get the controller attached to the instance.
 	 */
 	class ScriptController *GetController() { return controller; }
--- a/src/script/script_storage.hpp
+++ b/src/script/script_storage.hpp
@@ -17,6 +17,7 @@
 #include "../road_type.h"
 #include "../group.h"
 #include "../goal_type.h"
+#include "../story_type.h"
 
 #include "table/strings.h"
 #include <vector>
@@ -49,6 +50,8 @@
 	SignID new_sign_id;              ///< The ID of the new Sign.
 	GroupID new_group_id;            ///< The ID of the new Group.
 	GoalID new_goal_id;              ///< The ID of the new Goal.
+	StoryPageID new_story_page_id;   ///< The ID of the new StoryPage.
+	StoryPageID new_story_page_element_id; ///< The ID of the new StoryPageElement.
 
 	std::vector<int> callback_value; ///< The values which need to survive a callback.
 
new file mode 100644
--- /dev/null
+++ b/src/story.cpp
@@ -0,0 +1,271 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file story.cpp Handling of stories. */
+
+#include "stdafx.h"
+#include "story_base.h"
+#include "core/pool_func.hpp"
+#include "command_func.h"
+#include "company_base.h"
+#include "company_func.h"
+#include "string_func.h"
+#include "date_func.h"
+#include "tile_map.h"
+#include "goal_type.h"
+#include "goal_base.h"
+
+
+StoryPageElementID _new_story_page_element_id;
+StoryPageID _new_story_page_id;
+uint32 _story_page_element_next_sort_value;
+uint32 _story_page_next_sort_value;
+
+StoryPageElementPool _story_page_element_pool("StoryPageElement");
+StoryPagePool _story_page_pool("StoryPage");
+INSTANTIATE_POOL_METHODS(StoryPageElement)
+INSTANTIATE_POOL_METHODS(StoryPage)
+
+/**
+ * This helper for Create/Update PageElement Cmd procedure verifies if the page
+ * element parameters are correct for the given page element type.
+ * @param page_id The page id of the page which the page element (will) belong to
+ * @param type The type of the page element to create/update
+ * @param tile The tile parameter of the DoCommand proc
+ * @param reference The reference parameter of the DoCommand proc (p2)
+ * @param text The text parameter of the DoCommand proc
+ * @return true, if and only if the given parameters are valid for the given page elment type and page id.
+ */
+static bool VerifyElementContentParameters(uint32 page_id, StoryPageElementType type, TileIndex tile, uint32 reference, const char *text)
+{
+	switch (type) {
+		case SPET_TEXT:
+			if (StrEmpty(text)) return false;
+			break;
+		case SPET_LOCATION:
+			if (StrEmpty(text)) return false;
+			if (!IsValidTile(tile)) return false;
+			break;
+		case SPET_GOAL:
+			if (!Goal::IsValidID((GoalID)reference)) return false;
+			/* 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;
+		default:
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * This helper for Create/Update PageElement Cmd procedure updates a page
+ * element with new content data.
+ * @param pe The page element to update
+ * @param tile The tile parameter of the DoCommand proc
+ * @param reference The reference parameter of the DoCommand proc (p2)
+ * @param text The text parameter of the DoCommand proc
+ */
+static void UpdateElement(StoryPageElement &pe, TileIndex tile, uint32 reference, const char *text)
+{
+	switch (pe.type) {
+		case SPET_TEXT:
+			pe.text = strdup(text);
+			break;
+		case SPET_LOCATION:
+			pe.text = strdup(text);
+			pe.referenced_id = tile;
+			break;
+		case SPET_GOAL:
+			pe.referenced_id = (GoalID)reference;
+			break;
+	}
+}
+
+/**
+ * Create a new story page.
+ * @param tile unused.
+ * @param flags type of operation
+ * @param p1 various bitstuffed elements
+ * - p1 = (bit  0 -  7) - Company for which this story page belongs to.
+ * @param p2 unused.
+ * @param text Title of the story page. Null is allowed in wich case a generic page title is provided by OpenTTD.
+ * @return the cost of this operation or an error
+ */
+CommandCost CmdCreateStoryPage(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
+{
+	if (!StoryPage::CanAllocateItem()) return CMD_ERROR;
+
+	CompanyID company = (CompanyID)GB(p1, 0, 8);
+
+	if (_current_company != OWNER_DEITY) return CMD_ERROR;
+	if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		if (_story_page_pool.items == 0) {
+			/* Initialize the next sort value variable. */
+			_story_page_next_sort_value = 0;
+		}
+
+		StoryPage *s = new StoryPage();
+		s->sort_value = _story_page_next_sort_value;
+		s->date = _date;
+		s->company = company;
+		if (StrEmpty(text)) {
+			s->title = NULL;
+		} else {
+			s->title = strdup(text);
+		}
+
+		_new_story_page_id = s->index;
+		_story_page_next_sort_value++;
+	}
+
+	return CommandCost();
+}
+
+/**
+ * Create a new story page element.
+ * @param tile Tile location if it is a location page element, otherwise unused.
+ * @param flags type of operation
+ * @param p1 various bitstuffed elements
+ * - p1 = (bit  0 -  15) - The page which the element belongs to.
+ *        (bit  16 -  31) - Page element type
+ * @param p2 Id of referenced object
+ * @param text Text content in case it is a text or location page element
+ * @return the cost of this operation or an error
+ */
+CommandCost CmdCreateStoryPageElement(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
+{
+	if (!StoryPageElement::CanAllocateItem()) return CMD_ERROR;
+
+	StoryPageID page_id = (CompanyID)GB(p1, 0, 16);
+	StoryPageElementType type = (StoryPageElementType) GB(p1, 16, 16);
+
+	/* Allow at most 128 elements per page. */
+	uint16 element_count = 0;
+	StoryPageElement *iter;
+	FOR_ALL_STORY_PAGE_ELEMENTS(iter) {
+		if (iter->page == page_id) element_count++;
+	}
+	if (element_count >= 128) return CMD_ERROR;
+
+	if (_current_company != OWNER_DEITY) return CMD_ERROR;
+	if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
+	if (!VerifyElementContentParameters(page_id, type, tile, p2, text)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		if (_story_page_element_pool.items == 0) {
+			/* Initialize the next sort value variable. */
+			_story_page_element_next_sort_value = 0;
+		}
+
+		StoryPageElement *pe = new StoryPageElement();
+		pe->sort_value = _story_page_element_next_sort_value;
+		pe->type = type;
+		pe->page = page_id;
+		UpdateElement(*pe, tile, p2, text);
+
+		_new_story_page_element_id = pe->index;
+		_story_page_element_next_sort_value++;
+	}
+
+	return CommandCost();
+}
+
+/**
+ * Update a new story page element.
+ * @param tile Tile location if it is a location page element, otherwise unused.
+ * @param flags type of operation
+ * @param p1 various bitstuffed elements
+ * - p1 = (bit  0 -  15) - The page element to update.
+ *        (bit  16 -  31) - unused
+ * @param p2 Id of referenced object
+ * @param text Text content in case it is a text or location page element
+ * @return the cost of this operation or an error
+ */
+CommandCost CmdUpdateStoryPageElement(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
+{
+	StoryPageElementID page_element_id = (StoryPageElementID)GB(p1, 0, 16);
+
+	if (_current_company != OWNER_DEITY) return CMD_ERROR;
+	if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
+
+	StoryPageElement *pe = StoryPageElement::Get(page_element_id);
+	StoryPageID page_id = pe->page;
+	StoryPageElementType type = pe->type;
+
+	if (!VerifyElementContentParameters(page_id, type, tile, p2, text)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		UpdateElement(*pe, tile, p2, text);
+	}
+
+	return CommandCost();
+}
+
+/**
+ * Update title of a story page.
+ * @param tile unused.
+ * @param flags type of operation
+ * @param p1 StoryPageID to update.
+ * @param p2 unused
+ * @param text title text of the story page.
+ * @return the cost of this operation or an error
+ */
+CommandCost CmdSetStoryPageTitle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
+{
+	if (_current_company != OWNER_DEITY) return CMD_ERROR;
+	StoryPageID page_id = (StoryPageID)p1;
+	if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		StoryPage *p = StoryPage::Get(page_id);
+		free(p->title);
+		if (StrEmpty(text)) {
+			p->title = NULL;
+		} else {
+			p->title = strdup(text);
+		}
+	}
+
+	return CommandCost();
+}
+
+/**
+ * Remove a story page and associated story page elements.
+ * @param tile unused.
+ * @param flags type of operation
+ * @param p1 StoryPageID to remove.
+ * @param p2 unused.
+ * @param text unused.
+ * @return the cost of this operation or an error
+ */
+CommandCost CmdRemoveStoryPage(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
+{
+	if (_current_company != OWNER_DEITY) return CMD_ERROR;
+	StoryPageID page_id = (StoryPageID)p1;
+	if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		StoryPage *p = StoryPage::Get(page_id);
+
+		StoryPageElement *pe;
+		FOR_ALL_STORY_PAGE_ELEMENTS(pe) {
+			if (pe->page == p->index) {
+				delete pe;
+			}
+		}
+
+		delete p;
+	}
+
+	return CommandCost();
+}
+
new file mode 100644
--- /dev/null
+++ b/src/story_base.h
@@ -0,0 +1,85 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file story_base.h %StoryPage base class. */
+
+#ifndef STORY_BASE_H
+#define STORY_BASE_H
+
+#include "company_type.h"
+#include "story_type.h"
+#include "date_type.h"
+#include "core/pool_type.hpp"
+
+typedef Pool<StoryPageElement, StoryPageElementID, 64, 64000> StoryPageElementPool;
+typedef Pool<StoryPage, StoryPageID, 64, 64000> StoryPagePool;
+extern StoryPageElementPool _story_page_element_pool;
+extern StoryPagePool _story_page_pool;
+extern uint32 _story_page_element_next_sort_value;
+extern uint32 _story_page_next_sort_value;
+
+/*
+ * Each story page element is one of these types.
+ */
+enum StoryPageElementType {
+	SPET_TEXT,     ///< 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.
+};
+
+/**
+ * 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.
+	uint16 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
+
+	/**
+	 * We need an (empty) constructor so struct isn't zeroed (as C++ standard states)
+	 */
+	inline StoryPageElement() { }
+
+	/**
+	 * (Empty) destructor has to be defined else operator delete might be called with NULL parameter
+	 */
+	inline ~StoryPageElement() { free(this->text); }
+};
+
+/** Struct about stories, current and completed */
+struct StoryPage : StoryPagePool::PoolItem<&_story_page_pool> {
+	uint32 sort_value;   ///< A number that increases for every created story page. Used for sorting. The id of a story page is the pool index.
+	Date date;           ///< Date when the page was created.
+	CompanyByte company; ///< StoryPage is for a specific company; INVALID_COMPANY if it is global
+
+	char *title;         ///< Title of story page
+
+	/**
+	 * We need an (empty) constructor so struct isn't zeroed (as C++ standard states)
+	 */
+	inline StoryPage() { }
+
+	/**
+	 * (Empty) destructor has to be defined else operator delete might be called with NULL parameter
+	 */
+	inline ~StoryPage() { free(this->title); }
+};
+
+#define FOR_ALL_STORY_PAGE_ELEMENTS_FROM(var, start) FOR_ALL_ITEMS_FROM(StoryPageElement, story_page_element_index, var, start)
+#define FOR_ALL_STORY_PAGE_ELEMENTS(var) FOR_ALL_STORY_PAGE_ELEMENTS_FROM(var, 0)
+#define FOR_ALL_STORY_PAGES_FROM(var, start) FOR_ALL_ITEMS_FROM(StoryPage, story_page_index, var, start)
+#define FOR_ALL_STORY_PAGES(var) FOR_ALL_STORY_PAGES_FROM(var, 0)
+
+#endif /* STORY_BASE_H */
+
new file mode 100644
--- /dev/null
+++ b/src/story_type.h
@@ -0,0 +1,28 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file story_type.h basic types related to story pages */
+
+#ifndef STORY_TYPE_H
+#define STORY_TYPE_H
+
+#include "core/enum_type.hpp"
+
+typedef uint16 StoryPageElementID; ///< ID of a story page element
+typedef uint16 StoryPageID; ///< ID of a story page
+struct StoryPageElement;
+struct StoryPage;
+
+extern StoryPageElementID _new_story_page_element_id;
+extern StoryPageID _new_story_page_id;
+static const StoryPageElementID INVALID_STORY_PAGE_ELEMENT = 0xFFFF; ///< Constant representing a non-existing story page element.
+static const StoryPageID INVALID_STORY_PAGE = 0xFFFF; ///< Constant representing a non-existing story page.
+
+#endif /* STORY_TYPE_H */
+