OpenTTD Source  20241108-master-g80f628063a
story.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
10 #include "stdafx.h"
11 #include "story_base.h"
12 #include "core/pool_func.hpp"
13 #include "command_func.h"
14 #include "company_base.h"
15 #include "company_func.h"
16 #include "string_func.h"
18 #include "tile_map.h"
19 #include "goal_type.h"
20 #include "goal_base.h"
21 #include "window_func.h"
22 #include "gui.h"
23 #include "vehicle_base.h"
24 #include "game/game.hpp"
25 #include "script/api/script_story_page.hpp"
26 #include "script/api/script_event_types.hpp"
27 #include "story_cmd.h"
28 
29 #include "safeguards.h"
30 
31 
32 uint32_t _story_page_element_next_sort_value;
33 uint32_t _story_page_next_sort_value;
34 
35 StoryPageElementPool _story_page_element_pool("StoryPageElement");
36 StoryPagePool _story_page_pool("StoryPage");
39 
40 
50 static bool VerifyElementContentParameters(StoryPageID page_id, StoryPageElementType type, TileIndex tile, uint32_t reference, const std::string &text)
51 {
52  StoryPageButtonData button_data{ reference };
53 
54  switch (type) {
55  case SPET_TEXT:
56  if (text.empty()) return false;
57  break;
58  case SPET_LOCATION:
59  if (text.empty()) return false;
60  if (!IsValidTile(tile)) return false;
61  break;
62  case SPET_GOAL:
63  if (!Goal::IsValidID((GoalID)reference)) return false;
64  /* Reject company specific goals on global pages */
65  if (StoryPage::Get(page_id)->company == INVALID_COMPANY && Goal::Get((GoalID)reference)->company != INVALID_COMPANY) return false;
66  break;
67  case SPET_BUTTON_PUSH:
68  if (!button_data.ValidateColour()) return false;
69  if (!button_data.ValidateFlags()) return false;
70  return true;
71  case SPET_BUTTON_TILE:
72  if (!button_data.ValidateColour()) return false;
73  if (!button_data.ValidateFlags()) return false;
74  if (!button_data.ValidateCursor()) return false;
75  return true;
77  if (!button_data.ValidateColour()) return false;
78  if (!button_data.ValidateFlags()) return false;
79  if (!button_data.ValidateCursor()) return false;
80  if (!button_data.ValidateVehicleType()) return false;
81  return true;
82  default:
83  return false;
84  }
85 
86  return true;
87 }
88 
97 static void UpdateElement(StoryPageElement &pe, TileIndex tile, uint32_t reference, const std::string &text)
98 {
99  switch (pe.type) {
100  case SPET_TEXT:
101  pe.text = text;
102  break;
103  case SPET_LOCATION:
104  pe.text = text;
105  pe.referenced_id = tile.base();
106  break;
107  case SPET_GOAL:
108  pe.referenced_id = (GoalID)reference;
109  break;
110  case SPET_BUTTON_PUSH:
111  case SPET_BUTTON_TILE:
112  case SPET_BUTTON_VEHICLE:
113  pe.text = text;
114  pe.referenced_id = reference;
115  break;
116  default: NOT_REACHED();
117  }
118 }
119 
121 void StoryPageButtonData::SetColour(Colours button_colour)
122 {
123  assert(button_colour < COLOUR_END);
124  SB(this->referenced_id, 0, 8, button_colour);
125 }
126 
127 void StoryPageButtonData::SetFlags(StoryPageButtonFlags flags)
128 {
129  SB(this->referenced_id, 24, 8, flags);
130 }
131 
134 {
135  assert(cursor < SPBC_END);
136  SB(this->referenced_id, 8, 8, cursor);
137 }
138 
141 {
142  assert(vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END);
143  SB(this->referenced_id, 16, 8, vehtype);
144 }
145 
148 {
149  Colours colour = static_cast<Colours>(GB(this->referenced_id, 0, 8));
150  if (!IsValidColours(colour)) return INVALID_COLOUR;
151  return colour;
152 }
153 
154 StoryPageButtonFlags StoryPageButtonData::GetFlags() const
155 {
156  return (StoryPageButtonFlags)GB(this->referenced_id, 24, 8);
157 }
158 
161 {
162  StoryPageButtonCursor cursor = (StoryPageButtonCursor)GB(this->referenced_id, 8, 8);
163  if (!IsValidStoryPageButtonCursor(cursor)) return INVALID_SPBC;
164  return cursor;
165 }
166 
169 {
170  return (VehicleType)GB(this->referenced_id, 16, 8);
171 }
172 
175 {
176  return GB(this->referenced_id, 0, 8) < COLOUR_END;
177 }
178 
179 bool StoryPageButtonData::ValidateFlags() const
180 {
181  uint8_t flags = GB(this->referenced_id, 24, 8);
182  /* Don't allow float left and right together */
183  if ((flags & SPBF_FLOAT_LEFT) && (flags & SPBF_FLOAT_RIGHT)) return false;
184  /* Don't allow undefined flags */
185  if (flags & ~(SPBF_FLOAT_LEFT | SPBF_FLOAT_RIGHT)) return false;
186  return true;
187 }
188 
191 {
192  return GB(this->referenced_id, 8, 8) < SPBC_END;
193 }
194 
197 {
198  uint8_t vehtype = GB(this->referenced_id, 16, 8);
199  return vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END;
200 }
201 
209 std::tuple<CommandCost, StoryPageID> CmdCreateStoryPage(DoCommandFlag flags, CompanyID company, const std::string &text)
210 {
211  if (!StoryPage::CanAllocateItem()) return { CMD_ERROR, INVALID_STORY_PAGE };
212 
213  if (_current_company != OWNER_DEITY) return { CMD_ERROR, INVALID_STORY_PAGE };
214  if (company != INVALID_COMPANY && !Company::IsValidID(company)) return { CMD_ERROR, INVALID_STORY_PAGE };
215 
216  if (flags & DC_EXEC) {
217  if (_story_page_pool.items == 0) {
218  /* Initialize the next sort value variable. */
219  _story_page_next_sort_value = 0;
220  }
221 
222  StoryPage *s = new StoryPage();
223  s->sort_value = _story_page_next_sort_value;
225  s->company = company;
226  s->title = text;
227 
230 
231  _story_page_next_sort_value++;
232  return { CommandCost(), s->index };
233  }
234 
235  return { CommandCost(), INVALID_STORY_PAGE };
236 }
237 
248 std::tuple<CommandCost, StoryPageElementID> CmdCreateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageID page_id, StoryPageElementType type, uint32_t reference, const std::string &text)
249 {
250  if (!StoryPageElement::CanAllocateItem()) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
251 
252  /* Allow at most 128 elements per page. */
253  uint16_t element_count = 0;
255  if (iter->page == page_id) element_count++;
256  }
257  if (element_count >= 128) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
258 
259  if (_current_company != OWNER_DEITY) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
260  if (!StoryPage::IsValidID(page_id)) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
261  if (!VerifyElementContentParameters(page_id, type, tile, reference, text)) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
262 
263 
264  if (flags & DC_EXEC) {
265  if (_story_page_element_pool.items == 0) {
266  /* Initialize the next sort value variable. */
267  _story_page_element_next_sort_value = 0;
268  }
269 
271  pe->sort_value = _story_page_element_next_sort_value;
272  pe->type = type;
273  pe->page = page_id;
274  UpdateElement(*pe, tile, reference, text);
275 
277 
278  _story_page_element_next_sort_value++;
279  return { CommandCost(), pe->index };
280  }
281 
282  return { CommandCost(), INVALID_STORY_PAGE_ELEMENT };
283 }
284 
294 CommandCost CmdUpdateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageElementID page_element_id, uint32_t reference, const std::string &text)
295 {
296  if (_current_company != OWNER_DEITY) return CMD_ERROR;
297  if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
298 
299  StoryPageElement *pe = StoryPageElement::Get(page_element_id);
300  StoryPageID page_id = pe->page;
301  StoryPageElementType type = pe->type;
302 
303  if (!VerifyElementContentParameters(page_id, type, tile, reference, text)) return CMD_ERROR;
304 
305  if (flags & DC_EXEC) {
306  UpdateElement(*pe, tile, reference, text);
308  }
309 
310  return CommandCost();
311 }
312 
320 CommandCost CmdSetStoryPageTitle(DoCommandFlag flags, StoryPageID page_id, const std::string &text)
321 {
322  if (_current_company != OWNER_DEITY) return CMD_ERROR;
323  if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
324 
325  if (flags & DC_EXEC) {
326  StoryPage *p = StoryPage::Get(page_id);
327  p->title = text;
328 
330  }
331 
332  return CommandCost();
333 }
334 
342 CommandCost CmdSetStoryPageDate(DoCommandFlag flags, StoryPageID page_id, TimerGameCalendar::Date date)
343 {
344  if (_current_company != OWNER_DEITY) return CMD_ERROR;
345  if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
346 
347  if (flags & DC_EXEC) {
348  StoryPage *p = StoryPage::Get(page_id);
349  p->date = date;
350 
352  }
353 
354  return CommandCost();
355 }
356 
365 {
366  if (_current_company != OWNER_DEITY) return CMD_ERROR;
367  if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
368 
369  if (flags & DC_EXEC) {
370  StoryPage *g = StoryPage::Get(page_id);
372  }
373 
374  return CommandCost();
375 }
383 {
384  if (_current_company != OWNER_DEITY) return CMD_ERROR;
385  if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
386 
387  if (flags & DC_EXEC) {
388  StoryPage *p = StoryPage::Get(page_id);
389 
391  if (pe->page == p->index) {
392  delete pe;
393  }
394  }
395 
396  delete p;
397 
400  }
401 
402  return CommandCost();
403 }
404 
412 {
413  if (_current_company != OWNER_DEITY) return CMD_ERROR;
414  if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
415 
416  if (flags & DC_EXEC) {
417  StoryPageElement *pe = StoryPageElement::Get(page_element_id);
418  StoryPageID page_id = pe->page;
419 
420  delete pe;
421 
423  }
424 
425  return CommandCost();
426 }
427 
437 {
438  if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
439  const StoryPageElement *const pe = StoryPageElement::Get(page_element_id);
440 
441  /* Check the player belongs to the company that owns the page. */
442  const StoryPage *const sp = StoryPage::Get(pe->page);
443  if (sp->company != INVALID_COMPANY && sp->company != _current_company) return CMD_ERROR;
444 
445  switch (pe->type) {
446  case SPET_BUTTON_PUSH:
447  /* No validation required */
448  if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageButtonClick(_current_company, pe->page, page_element_id));
449  break;
450  case SPET_BUTTON_TILE:
451  if (!IsValidTile(tile)) return CMD_ERROR;
452  if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageTileSelect(_current_company, pe->page, page_element_id, tile));
453  break;
454  case SPET_BUTTON_VEHICLE:
455  if (!Vehicle::IsValidID(reference)) return CMD_ERROR;
456  if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageVehicleSelect(_current_company, pe->page, page_element_id, reference));
457  break;
458  default:
459  /* Invalid page element type, not a button. */
460  return CMD_ERROR;
461  }
462 
463  return CommandCost();
464 }
465 
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Common return value for all commands.
Definition: command_type.h:23
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
Definition: game_core.cpp:146
static Date date
Current date in days (day counter).
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
Definition: command_func.h:28
DoCommandFlag
List of flags for a command.
Definition: command_type.h:374
@ DC_EXEC
execute the given command
Definition: command_type.h:376
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:52
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:53
Functions related to companies.
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ INVALID_COMPANY
An invalid company.
Definition: company_type.h:30
@ OWNER_DEITY
The object is owned by a superuser / goal script.
Definition: company_type.h:27
Base functions for all Games.
Goal base class.
basic types related to goals
uint16_t GoalID
ID of a goal.
Definition: goal_type.h:37
GUI functions that shouldn't be here.
void ShowStoryBook(CompanyID company, uint16_t page_id=INVALID_STORY_PAGE, bool centered=false)
Raise or create the story book window for company, at page page_id.
Definition: story_gui.cpp:1051
bool IsValidColours(Colours colours)
Checks if a Colours value is valid.
Definition: palette_func.h:36
Some methods of Pool are placed here in order to reduce compilation time and binary size.
#define INSTANTIATE_POOL_METHODS(name)
Force instantiation of pool methods so we don't get linker errors.
Definition: pool_func.hpp:237
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
CommandCost CmdStoryPageButton(DoCommandFlag flags, TileIndex tile, StoryPageElementID page_element_id, VehicleID reference)
Clicked/used a button on a story page.
Definition: story.cpp:436
CommandCost CmdRemoveStoryPage(DoCommandFlag flags, StoryPageID page_id)
Remove a story page and associated story page elements.
Definition: story.cpp:382
CommandCost CmdUpdateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageElementID page_element_id, uint32_t reference, const std::string &text)
Update a new story page element.
Definition: story.cpp:294
std::tuple< CommandCost, StoryPageElementID > CmdCreateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageID page_id, StoryPageElementType type, uint32_t reference, const std::string &text)
Create a new story page element.
Definition: story.cpp:248
static void UpdateElement(StoryPageElement &pe, TileIndex tile, uint32_t reference, const std::string &text)
This helper for Create/Update PageElement Cmd procedure updates a page element with new content data.
Definition: story.cpp:97
CommandCost CmdSetStoryPageTitle(DoCommandFlag flags, StoryPageID page_id, const std::string &text)
Update title of a story page.
Definition: story.cpp:320
static bool VerifyElementContentParameters(StoryPageID page_id, StoryPageElementType type, TileIndex tile, uint32_t reference, const std::string &text)
This helper for Create/Update PageElement Cmd procedure verifies if the page element parameters are c...
Definition: story.cpp:50
CommandCost CmdShowStoryPage(DoCommandFlag flags, StoryPageID page_id)
Display a story page for all clients that are allowed to view the story page.
Definition: story.cpp:364
std::tuple< CommandCost, StoryPageID > CmdCreateStoryPage(DoCommandFlag flags, CompanyID company, const std::string &text)
Create a new story page.
Definition: story.cpp:209
CommandCost CmdRemoveStoryPageElement(DoCommandFlag flags, StoryPageElementID page_element_id)
Remove a story page element.
Definition: story.cpp:411
CommandCost CmdSetStoryPageDate(DoCommandFlag flags, StoryPageID page_id, TimerGameCalendar::Date date)
Update date of a story page.
Definition: story.cpp:342
StoryPage base class.
StoryPageButtonFlags
Flags available for buttons.
Definition: story_base.h:42
StoryPageElementType
Definition: story_base.h:30
@ SPET_LOCATION
An element that references a tile along with a one-line text.
Definition: story_base.h:32
@ SPET_GOAL
An element that references a goal.
Definition: story_base.h:33
@ SPET_BUTTON_PUSH
A push button that triggers an immediate event.
Definition: story_base.h:34
@ SPET_BUTTON_TILE
A button that allows the player to select a tile, and triggers an event with the tile.
Definition: story_base.h:35
@ SPET_TEXT
A text element.
Definition: story_base.h:31
@ SPET_BUTTON_VEHICLE
A button that allows the player to select a vehicle, and triggers an event wih the vehicle.
Definition: story_base.h:36
StoryPageButtonCursor
Mouse cursors usable by story page buttons.
Definition: story_base.h:50
bool IsValidStoryPageButtonCursor(StoryPageButtonCursor cursor)
Checks if a StoryPageButtonCursor value is valid.
Definition: story_base.h:116
Command definitions related to stories.
uint16_t StoryPageID
ID of a story page.
Definition: story_type.h:16
uint16_t StoryPageElementID
ID of a story page element.
Definition: story_type.h:15
Functions related to low-level strings.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:238
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
static size_t GetNumItems()
Returns number of valid items in the pool.
Definition: pool_type.hpp:369
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:328
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
Definition: pool_type.hpp:309
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
Base class for all pools.
Definition: pool_type.hpp:80
size_t items
Number of used indexes (non-nullptr)
Definition: pool_type.hpp:94
Helper to construct packed "id" values for button-type StoryPageElement.
Definition: story_base.h:122
Colours GetColour() const
Get the button background colour.
Definition: story.cpp:147
VehicleType GetVehicleType() const
Get the type of vehicles that are accepted by the button.
Definition: story.cpp:168
void SetColour(Colours button_colour)
Set the button background colour.
Definition: story.cpp:121
bool ValidateVehicleType() const
Verity that the data stored a valid VehicleType value.
Definition: story.cpp:196
bool ValidateColour() const
Verify that the data stored a valid Colour value.
Definition: story.cpp:174
void SetVehicleType(VehicleType vehtype)
Set the type of vehicles that are accepted by the button.
Definition: story.cpp:140
StoryPageButtonCursor GetCursor() const
Get the mouse cursor used while waiting for input for the button.
Definition: story.cpp:160
bool ValidateCursor() const
Verify that the data stores a valid StoryPageButtonCursor value.
Definition: story.cpp:190
void SetCursor(StoryPageButtonCursor cursor)
Set the mouse cursor used while waiting for input for the button.
Definition: story.cpp:133
Struct about story page elements.
Definition: story_base.h:144
uint32_t referenced_id
Id of referenced object (location, goal etc.)
Definition: story_base.h:149
std::string text
Static content text of page element.
Definition: story_base.h:150
uint32_t sort_value
A number that increases for every created story page element. Used for sorting. The id of a story pag...
Definition: story_base.h:145
StoryPageElementType type
Type of page element.
Definition: story_base.h:147
StoryPageID page
Id of the page which the page element belongs to.
Definition: story_base.h:146
Struct about stories, current and completed.
Definition: story_base.h:164
uint32_t sort_value
A number that increases for every created story page. Used for sorting. The id of a story page is the...
Definition: story_base.h:165
CompanyID company
StoryPage is for a specific company; INVALID_COMPANY if it is global.
Definition: story_base.h:167
std::string title
Title of story page.
Definition: story_base.h:169
TimerGameCalendar::Date date
Date when the page was created.
Definition: story_base.h:166
Map writing/reading functions for tiles.
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition: tile_map.h:161
Definition of the game-calendar-timer.
Base class for all vehicles.
VehicleType
Available vehicle types.
Definition: vehicle_type.h:21
@ VEH_INVALID
Non-existing type of vehicle.
Definition: vehicle_type.h:35
@ VEH_COMPANY_END
Last company-ownable type.
Definition: vehicle_type.h:29
uint32_t VehicleID
The type all our vehicle IDs have.
Definition: vehicle_type.h:16
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition: window.cpp:3211
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition: window.cpp:3228
Window functions not directly related to making/drawing windows.
@ WC_STORY_BOOK
Story book; Window numbers:
Definition: window_type.h:296
@ WC_MAIN_TOOLBAR
Main toolbar (the long bar at the top); Window numbers:
Definition: window_type.h:58