Codechange: Move flags in CommandProc in front of the command arguments.
[openttd-github.git] / src / story.cpp
blob24b09ade5dd2b3db5a9fc33d2c81e4d4550b1e4c
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 */
8 /** @file story.cpp Handling of stories. */
10 #include "stdafx.h"
11 #include "story_base.h"
12 #include "core/pool_func.hpp"
13 #include "cmd_helper.h"
14 #include "command_func.h"
15 #include "company_base.h"
16 #include "company_func.h"
17 #include "string_func.h"
18 #include "date_func.h"
19 #include "tile_map.h"
20 #include "goal_type.h"
21 #include "goal_base.h"
22 #include "window_func.h"
23 #include "gui.h"
24 #include "vehicle_base.h"
25 #include "game/game.hpp"
26 #include "script/api/script_story_page.hpp"
27 #include "script/api/script_event_types.hpp"
28 #include "story_cmd.h"
30 #include "safeguards.h"
33 StoryPageElementID _new_story_page_element_id;
34 StoryPageID _new_story_page_id;
35 uint32 _story_page_element_next_sort_value;
36 uint32 _story_page_next_sort_value;
38 StoryPageElementPool _story_page_element_pool("StoryPageElement");
39 StoryPagePool _story_page_pool("StoryPage");
40 INSTANTIATE_POOL_METHODS(StoryPageElement)
41 INSTANTIATE_POOL_METHODS(StoryPage)
43 /**
44 * This helper for Create/Update PageElement Cmd procedure verifies if the page
45 * element parameters are correct for the given page element type.
46 * @param page_id The page id of the page which the page element (will) belong to
47 * @param type The type of the page element to create/update
48 * @param tile The tile parameter of the DoCommand proc
49 * @param reference The reference parameter of the DoCommand proc (p2)
50 * @param text The text parameter of the DoCommand proc
51 * @return true, if and only if the given parameters are valid for the given page element type and page id.
53 static bool VerifyElementContentParameters(StoryPageID page_id, StoryPageElementType type, TileIndex tile, uint32 reference, const char *text)
55 StoryPageButtonData button_data{ reference };
57 switch (type) {
58 case SPET_TEXT:
59 if (StrEmpty(text)) return false;
60 break;
61 case SPET_LOCATION:
62 if (StrEmpty(text)) return false;
63 if (!IsValidTile(tile)) return false;
64 break;
65 case SPET_GOAL:
66 if (!Goal::IsValidID((GoalID)reference)) return false;
67 /* Reject company specific goals on global pages */
68 if (StoryPage::Get(page_id)->company == INVALID_COMPANY && Goal::Get((GoalID)reference)->company != INVALID_COMPANY) return false;
69 break;
70 case SPET_BUTTON_PUSH:
71 if (!button_data.ValidateColour()) return false;
72 return true;
73 case SPET_BUTTON_TILE:
74 if (!button_data.ValidateColour()) return false;
75 if (!button_data.ValidateCursor()) return false;
76 return true;
77 case SPET_BUTTON_VEHICLE:
78 if (!button_data.ValidateColour()) return false;
79 if (!button_data.ValidateCursor()) return false;
80 if (!button_data.ValidateVehicleType()) return false;
81 return true;
82 default:
83 return false;
86 return true;
89 /**
90 * This helper for Create/Update PageElement Cmd procedure updates a page
91 * element with new content data.
92 * @param pe The page element to update
93 * @param tile The tile parameter of the DoCommand proc
94 * @param reference The reference parameter of the DoCommand proc (p2)
95 * @param text The text parameter of the DoCommand proc
97 static void UpdateElement(StoryPageElement &pe, TileIndex tile, uint32 reference, const char *text)
99 switch (pe.type) {
100 case SPET_TEXT:
101 pe.text = stredup(text);
102 break;
103 case SPET_LOCATION:
104 pe.text = stredup(text);
105 pe.referenced_id = tile;
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 = stredup(text);
114 pe.referenced_id = reference;
115 break;
116 default: NOT_REACHED();
120 /** Set the button background colour. */
121 void StoryPageButtonData::SetColour(Colours button_colour)
123 assert(button_colour < COLOUR_END);
124 SB(this->referenced_id, 0, 8, button_colour);
127 void StoryPageButtonData::SetFlags(StoryPageButtonFlags flags)
129 SB(this->referenced_id, 24, 8, flags);
132 /** Set the mouse cursor used while waiting for input for the button. */
133 void StoryPageButtonData::SetCursor(StoryPageButtonCursor cursor)
135 assert(cursor < SPBC_END);
136 SB(this->referenced_id, 8, 8, cursor);
139 /** Set the type of vehicles that are accepted by the button */
140 void StoryPageButtonData::SetVehicleType(VehicleType vehtype)
142 assert(vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END);
143 SB(this->referenced_id, 16, 8, vehtype);
146 /** Get the button background colour. */
147 Colours StoryPageButtonData::GetColour() const
149 return Extract<Colours, 0, 8>(this->referenced_id);
152 StoryPageButtonFlags StoryPageButtonData::GetFlags() const
154 return (StoryPageButtonFlags)GB(this->referenced_id, 24, 8);
157 /** Get the mouse cursor used while waiting for input for the button. */
158 StoryPageButtonCursor StoryPageButtonData::GetCursor() const
160 return Extract<StoryPageButtonCursor, 8, 8>(this->referenced_id);
163 /** Get the type of vehicles that are accepted by the button */
164 VehicleType StoryPageButtonData::GetVehicleType() const
166 return (VehicleType)GB(this->referenced_id, 16, 8);
169 /** Verify that the data stored a valid Colour value */
170 bool StoryPageButtonData::ValidateColour() const
172 return GB(this->referenced_id, 0, 8) < COLOUR_END;
175 bool StoryPageButtonData::ValidateFlags() const
177 byte flags = GB(this->referenced_id, 24, 8);
178 /* Don't allow float left and right together */
179 if ((flags & SPBF_FLOAT_LEFT) && (flags & SPBF_FLOAT_RIGHT)) return false;
180 /* Don't allow undefined flags */
181 if (flags & ~(SPBF_FLOAT_LEFT | SPBF_FLOAT_RIGHT)) return false;
182 return true;
185 /** Verify that the data stores a valid StoryPageButtonCursor value */
186 bool StoryPageButtonData::ValidateCursor() const
188 return GB(this->referenced_id, 8, 8) < SPBC_END;
191 /** Verity that the data stored a valid VehicleType value */
192 bool StoryPageButtonData::ValidateVehicleType() const
194 byte vehtype = GB(this->referenced_id, 16, 8);
195 return vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END;
199 * Create a new story page.
200 * @param flags type of operation
201 * @param tile unused.
202 * @param p1 various bitstuffed elements
203 * - p1 = (bit 0 - 7) - Company for which this story page belongs to.
204 * @param p2 unused.
205 * @param text Title of the story page. Null is allowed in which case a generic page title is provided by OpenTTD.
206 * @return the cost of this operation or an error
208 CommandCost CmdCreateStoryPage(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
210 if (!StoryPage::CanAllocateItem()) return CMD_ERROR;
212 CompanyID company = (CompanyID)GB(p1, 0, 8);
214 if (_current_company != OWNER_DEITY) return CMD_ERROR;
215 if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
217 if (flags & DC_EXEC) {
218 if (_story_page_pool.items == 0) {
219 /* Initialize the next sort value variable. */
220 _story_page_next_sort_value = 0;
223 StoryPage *s = new StoryPage();
224 s->sort_value = _story_page_next_sort_value;
225 s->date = _date;
226 s->company = company;
227 if (text.empty()) {
228 s->title = nullptr;
229 } else {
230 s->title = stredup(text.c_str());
233 InvalidateWindowClassesData(WC_STORY_BOOK, -1);
234 if (StoryPage::GetNumItems() == 1) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
236 _new_story_page_id = s->index;
237 _story_page_next_sort_value++;
240 return CommandCost();
244 * Create a new story page element.
245 * @param flags type of operation
246 * @param tile Tile location if it is a location page element, otherwise unused.
247 * @param p1 various bitstuffed elements
248 * - p1 = (bit 0 - 15) - The page which the element belongs to.
249 * (bit 16 - 23) - Page element type
250 * @param p2 Id of referenced object
251 * @param text Text content in case it is a text or location page element
252 * @return the cost of this operation or an error
254 CommandCost CmdCreateStoryPageElement(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
256 if (!StoryPageElement::CanAllocateItem()) return CMD_ERROR;
258 StoryPageID page_id = (StoryPageID)GB(p1, 0, 16);
259 StoryPageElementType type = Extract<StoryPageElementType, 16, 8>(p1);
261 /* Allow at most 128 elements per page. */
262 uint16 element_count = 0;
263 for (StoryPageElement *iter : StoryPageElement::Iterate()) {
264 if (iter->page == page_id) element_count++;
266 if (element_count >= 128) return CMD_ERROR;
268 if (_current_company != OWNER_DEITY) return CMD_ERROR;
269 if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
270 if (!VerifyElementContentParameters(page_id, type, tile, p2, text.c_str())) return CMD_ERROR;
272 if (flags & DC_EXEC) {
273 if (_story_page_element_pool.items == 0) {
274 /* Initialize the next sort value variable. */
275 _story_page_element_next_sort_value = 0;
278 StoryPageElement *pe = new StoryPageElement();
279 pe->sort_value = _story_page_element_next_sort_value;
280 pe->type = type;
281 pe->page = page_id;
282 UpdateElement(*pe, tile, p2, text.c_str());
284 InvalidateWindowClassesData(WC_STORY_BOOK, page_id);
286 _new_story_page_element_id = pe->index;
287 _story_page_element_next_sort_value++;
290 return CommandCost();
294 * Update a new story page element.
295 * @param flags type of operation
296 * @param tile Tile location if it is a location page element, otherwise unused.
297 * @param p1 various bitstuffed elements
298 * - p1 = (bit 0 - 15) - The page element to update.
299 * (bit 16 - 31) - unused
300 * @param p2 Id of referenced object
301 * @param text Text content in case it is a text or location page element
302 * @return the cost of this operation or an error
304 CommandCost CmdUpdateStoryPageElement(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
306 StoryPageElementID page_element_id = (StoryPageElementID)GB(p1, 0, 16);
308 if (_current_company != OWNER_DEITY) return CMD_ERROR;
309 if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
311 StoryPageElement *pe = StoryPageElement::Get(page_element_id);
312 StoryPageID page_id = pe->page;
313 StoryPageElementType type = pe->type;
315 if (!VerifyElementContentParameters(page_id, type, tile, p2, text.c_str())) return CMD_ERROR;
317 if (flags & DC_EXEC) {
318 UpdateElement(*pe, tile, p2, text.c_str());
319 InvalidateWindowClassesData(WC_STORY_BOOK, pe->page);
322 return CommandCost();
326 * Update title of a story page.
327 * @param flags type of operation
328 * @param tile unused.
329 * @param p1 = (bit 0 - 15) - StoryPageID to update.
330 * @param p2 unused
331 * @param text title text of the story page.
332 * @return the cost of this operation or an error
334 CommandCost CmdSetStoryPageTitle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
336 if (_current_company != OWNER_DEITY) return CMD_ERROR;
337 StoryPageID page_id = (StoryPageID)GB(p1, 0, 16);
338 if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
340 if (flags & DC_EXEC) {
341 StoryPage *p = StoryPage::Get(page_id);
342 free(p->title);
343 if (text.empty()) {
344 p->title = nullptr;
345 } else {
346 p->title = stredup(text.c_str());
349 InvalidateWindowClassesData(WC_STORY_BOOK, page_id);
352 return CommandCost();
356 * Update date of a story page.
357 * @param flags type of operation
358 * @param tile unused.
359 * @param p1 = (bit 0 - 15) - StoryPageID to update.
360 * @param p2 = (bit 0 - 31) - date
361 * @param text unused
362 * @return the cost of this operation or an error
364 CommandCost CmdSetStoryPageDate(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
366 if (_current_company != OWNER_DEITY) return CMD_ERROR;
367 StoryPageID page_id = (StoryPageID)GB(p1, 0, 16);
368 if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
369 Date date = (Date)p2;
371 if (flags & DC_EXEC) {
372 StoryPage *p = StoryPage::Get(page_id);
373 p->date = date;
375 InvalidateWindowClassesData(WC_STORY_BOOK, page_id);
378 return CommandCost();
382 * Display a story page for all clients that are allowed to
383 * view the story page.
384 * @param flags type of operation
385 * @param tile unused.
386 * @param p1 = (bit 0 - 15) - StoryPageID to show.
387 * @param p2 unused
388 * @param text unused
389 * @return the cost of this operation or an error
391 CommandCost CmdShowStoryPage(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
393 if (_current_company != OWNER_DEITY) return CMD_ERROR;
394 StoryPageID page_id = (StoryPageID)GB(p1, 0, 16);
395 if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
397 if (flags & DC_EXEC) {
398 StoryPage *g = StoryPage::Get(page_id);
399 if ((g->company != INVALID_COMPANY && g->company == _local_company) || (g->company == INVALID_COMPANY && Company::IsValidID(_local_company))) ShowStoryBook(_local_company, page_id);
402 return CommandCost();
405 * Remove a story page and associated story page elements.
406 * @param flags type of operation
407 * @param tile unused.
408 * @param p1 = (bit 0 - 15) - StoryPageID to remove.
409 * @param p2 unused.
410 * @param text unused.
411 * @return the cost of this operation or an error
413 CommandCost CmdRemoveStoryPage(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
415 if (_current_company != OWNER_DEITY) return CMD_ERROR;
416 StoryPageID page_id = (StoryPageID)p1;
417 if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
419 if (flags & DC_EXEC) {
420 StoryPage *p = StoryPage::Get(page_id);
422 for (StoryPageElement *pe : StoryPageElement::Iterate()) {
423 if (pe->page == p->index) {
424 delete pe;
428 delete p;
430 InvalidateWindowClassesData(WC_STORY_BOOK, -1);
431 if (StoryPage::GetNumItems() == 0) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
434 return CommandCost();
438 * Remove a story page element
439 * @param flags type of operation
440 * @param tile unused.
441 * @param p1 = (bit 0 - 15) - StoryPageElementID to remove.
442 * @param p2 unused.
443 * @param text unused.
444 * @return the cost of this operation or an error
446 CommandCost CmdRemoveStoryPageElement(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
448 if (_current_company != OWNER_DEITY) return CMD_ERROR;
449 StoryPageElementID page_element_id = (StoryPageElementID)p1;
450 if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
452 if (flags & DC_EXEC) {
453 StoryPageElement *pe = StoryPageElement::Get(page_element_id);
454 StoryPageID page_id = pe->page;
456 delete pe;
458 InvalidateWindowClassesData(WC_STORY_BOOK, page_id);
461 return CommandCost();
465 * Clicked/used a button on a story page.
466 * @param flags Type of operation.
467 * @param tile Tile selected, for tile selection buttons, otherwise unused.
468 * @param p1 Bit 0..15 = story page element id of button.
469 * @param p2 ID of selected item for buttons that select an item (e.g. vehicle), otherwise unused.
470 * @param text Unused.
471 * @return The cost of the operation, or an error.
473 CommandCost CmdStoryPageButton(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
475 StoryPageElementID page_element_id = (StoryPageElementID)GB(p1, 0, 16);
477 if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
478 const StoryPageElement *const pe = StoryPageElement::Get(page_element_id);
480 /* Check the player belongs to the company that owns the page. */
481 const StoryPage *const sp = StoryPage::Get(pe->page);
482 if (sp->company != INVALID_COMPANY && sp->company != _current_company) return CMD_ERROR;
484 switch (pe->type) {
485 case SPET_BUTTON_PUSH:
486 /* No validation required */
487 if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageButtonClick(_current_company, pe->page, page_element_id));
488 break;
489 case SPET_BUTTON_TILE:
490 if (!IsValidTile(tile)) return CMD_ERROR;
491 if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageTileSelect(_current_company, pe->page, page_element_id, tile));
492 break;
493 case SPET_BUTTON_VEHICLE:
494 if (!Vehicle::IsValidID(p2)) return CMD_ERROR;
495 if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageVehicleSelect(_current_company, pe->page, page_element_id, (VehicleID)p2));
496 break;
497 default:
498 /* Invalid page element type, not a button. */
499 return CMD_ERROR;
502 return CommandCost();