OpenTTD Source 20241224-master-gee860a5c8e
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
32uint32_t _story_page_element_next_sort_value;
33uint32_t _story_page_next_sort_value;
34
35StoryPageElementPool _story_page_element_pool("StoryPageElement");
36StoryPagePool _story_page_pool("StoryPage");
39
40
50static 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;
68 if (!button_data.ValidateColour()) return false;
69 if (!button_data.ValidateFlags()) return false;
70 return true;
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
97static 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:
113 pe.text = text;
114 pe.referenced_id = reference;
115 break;
116 default: NOT_REACHED();
117 }
118}
119
121void StoryPageButtonData::SetColour(Colours button_colour)
122{
123 assert(button_colour < COLOUR_END);
124 SB(this->referenced_id, 0, 8, button_colour);
125}
126
127void 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
154StoryPageButtonFlags 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
179bool 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
209std::tuple<CommandCost, StoryPageID> CmdCreateStoryPage(DoCommandFlag flags, CompanyID company, const std::string &text)
210{
212
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
248std::tuple<CommandCost, StoryPageElementID> CmdCreateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageID page_id, StoryPageElementType type, uint32_t reference, const std::string &text)
249{
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
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
283}
284
294CommandCost CmdUpdateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageElementID page_element_id, uint32_t reference, const std::string &text)
295{
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
320CommandCost CmdSetStoryPageTitle(DoCommandFlag flags, StoryPageID page_id, const std::string &text)
321{
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
342CommandCost CmdSetStoryPageDate(DoCommandFlag flags, StoryPageID page_id, TimerGameCalendar::Date date)
343{
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{
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{
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{
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;
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.
debug_inline static constexpr 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.
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
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.
DoCommandFlag
List of flags for a command.
@ DC_EXEC
execute the given command
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.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
Owner
Enum for all companies/owners.
@ INVALID_COMPANY
An invalid company.
@ OWNER_DEITY
The object is owned by a superuser / goal script.
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.
bool IsValidColours(Colours colours)
Checks if a Colours value is valid.
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.
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
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, 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
CommandCost CmdRemoveStoryPageElement(DoCommandFlag flags, StoryPageElementID page_element_id)
Remove a story page element.
Definition story.cpp:411
std::tuple< CommandCost, StoryPageID > CmdCreateStoryPage(DoCommandFlag flags, CompanyID company, const std::string &text)
Create a new story page.
Definition story.cpp:209
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.
static const StoryPageElementID INVALID_STORY_PAGE_ELEMENT
Constant representing a non-existing story page element.
Definition story_type.h:21
static const StoryPageID INVALID_STORY_PAGE
Constant representing a non-existing story page.
Definition story_type.h:22
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.
static size_t GetNumItems()
Returns number of valid items in the pool.
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static Titem * Get(size_t index)
Returns Titem with given index.
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.
@ VEH_INVALID
Non-existing type of vehicle.
@ VEH_COMPANY_END
Last company-ownable type.
uint32_t VehicleID
The type all our vehicle IDs have.
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:3219
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:3236
Window functions not directly related to making/drawing windows.
@ WC_STORY_BOOK
Story book; Window numbers:
@ WC_MAIN_TOOLBAR
Main toolbar (the long bar at the top); Window numbers:
Definition window_type.h:58