Feature: Push-buttons on storybook pages (#7896)

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

Additionally this reworks how the storybook pages are layouted and rendered, to allow for slightly more complex layouts, and maybe speeding drawing up a bit.
This commit is contained in:
Niels Martin Hansen
2020-05-22 22:22:55 +02:00
committed by GitHub
parent c972a63c8c
commit 800ade7702
17 changed files with 1159 additions and 82 deletions

View File

@@ -21,6 +21,10 @@
#include "goal_base.h"
#include "window_func.h"
#include "gui.h"
#include "vehicle_base.h"
#include "game/game.hpp"
#include "script/api/script_story_page.hpp"
#include "script/api/script_event_types.hpp"
#include "safeguards.h"
@@ -47,6 +51,8 @@ INSTANTIATE_POOL_METHODS(StoryPage)
*/
static bool VerifyElementContentParameters(StoryPageID page_id, StoryPageElementType type, TileIndex tile, uint32 reference, const char *text)
{
StoryPageButtonData button_data{ reference };
switch (type) {
case SPET_TEXT:
if (StrEmpty(text)) return false;
@@ -60,6 +66,18 @@ static bool VerifyElementContentParameters(StoryPageID page_id, StoryPageElement
/* Reject company specific goals on global pages */
if (StoryPage::Get(page_id)->company == INVALID_COMPANY && Goal::Get((GoalID)reference)->company != INVALID_COMPANY) return false;
break;
case SPET_BUTTON_PUSH:
if (!button_data.ValidateColour()) return false;
return true;
case SPET_BUTTON_TILE:
if (!button_data.ValidateColour()) return false;
if (!button_data.ValidateCursor()) return false;
return true;
case SPET_BUTTON_VEHICLE:
if (!button_data.ValidateColour()) return false;
if (!button_data.ValidateCursor()) return false;
if (!button_data.ValidateVehicleType()) return false;
return true;
default:
return false;
}
@@ -88,10 +106,94 @@ static void UpdateElement(StoryPageElement &pe, TileIndex tile, uint32 reference
case SPET_GOAL:
pe.referenced_id = (GoalID)reference;
break;
case SPET_BUTTON_PUSH:
case SPET_BUTTON_TILE:
case SPET_BUTTON_VEHICLE:
pe.text = stredup(text);
pe.referenced_id = reference;
break;
default: NOT_REACHED();
}
}
/** Set the button background colour. */
void StoryPageButtonData::SetColour(Colours button_colour)
{
assert(button_colour < COLOUR_END);
SB(this->referenced_id, 0, 8, button_colour);
}
void StoryPageButtonData::SetFlags(StoryPageButtonFlags flags)
{
SB(this->referenced_id, 24, 8, flags);
}
/** Set the mouse cursor used while waiting for input for the button. */
void StoryPageButtonData::SetCursor(StoryPageButtonCursor cursor)
{
assert(cursor < SPBC_END);
SB(this->referenced_id, 8, 8, cursor);
}
/** Set the type of vehicles that are accepted by the button */
void StoryPageButtonData::SetVehicleType(VehicleType vehtype)
{
assert(vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END);
SB(this->referenced_id, 16, 8, vehtype);
}
/** Get the button background colour. */
Colours StoryPageButtonData::GetColour() const
{
return Extract<Colours, 0, 8>(this->referenced_id);
}
StoryPageButtonFlags StoryPageButtonData::GetFlags() const
{
return (StoryPageButtonFlags)GB(this->referenced_id, 24, 8);
}
/** Get the mouse cursor used while waiting for input for the button. */
StoryPageButtonCursor StoryPageButtonData::GetCursor() const
{
return Extract<StoryPageButtonCursor, 8, 8>(this->referenced_id);
}
/** Get the type of vehicles that are accepted by the button */
VehicleType StoryPageButtonData::GetVehicleType() const
{
return (VehicleType)GB(this->referenced_id, 16, 8);
}
/** Verify that the data stored a valid Colour value */
bool StoryPageButtonData::ValidateColour() const
{
return GB(this->referenced_id, 0, 8) < COLOUR_END;
}
bool StoryPageButtonData::ValidateFlags() const
{
byte flags = GB(this->referenced_id, 24, 8);
/* Don't allow float left and right together */
if ((flags & SPBF_FLOAT_LEFT) && (flags & SPBF_FLOAT_RIGHT)) return false;
/* Don't allow undefined flags */
if (flags & ~(SPBF_FLOAT_LEFT | SPBF_FLOAT_RIGHT)) return false;
return true;
}
/** Verify that the data stores a valid StoryPageButtonCursor value */
bool StoryPageButtonData::ValidateCursor() const
{
return GB(this->referenced_id, 8, 8) < SPBC_END;
}
/** Verity that the data stored a valid VehicleType value */
bool StoryPageButtonData::ValidateVehicleType() const
{
byte vehtype = GB(this->referenced_id, 16, 8);
return vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END;
}
/**
* Create a new story page.
* @param tile unused.
@@ -358,3 +460,44 @@ CommandCost CmdRemoveStoryPageElement(TileIndex tile, DoCommandFlag flags, uint3
return CommandCost();
}
/**
* Clicked/used a button on a story page.
* @param tile Tile selected, for tile selection buttons, otherwise unused.
* @param flags Type of operation.
* @param p1 Bit 0..15 = story page element id of button.
* @param p2 ID of selected item for buttons that select an item (e.g. vehicle), otherwise unused.
* @param text Unused.
* @return The cost of the operation, or an error.
*/
CommandCost CmdStoryPageButton(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
StoryPageElementID page_element_id = (StoryPageElementID)GB(p1, 0, 16);
if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
const StoryPageElement *const pe = StoryPageElement::Get(page_element_id);
/* Check the player belongs to the company that owns the page. */
const StoryPage *const sp = StoryPage::Get(pe->page);
if (sp->company != INVALID_COMPANY && sp->company != _current_company) return CMD_ERROR;
switch (pe->type) {
case SPET_BUTTON_PUSH:
/* No validation required */
if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageButtonClick(_current_company, pe->page, page_element_id));
break;
case SPET_BUTTON_TILE:
if (!IsValidTile(tile)) return CMD_ERROR;
if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageTileSelect(_current_company, pe->page, page_element_id, tile));
break;
case SPET_BUTTON_VEHICLE:
if (!Vehicle::IsValidID(p2)) return CMD_ERROR;
if (flags & DC_EXEC) Game::NewEvent(new ScriptEventStoryPageVehicleSelect(_current_company, pe->page, page_element_id, (VehicleID)p2));
break;
default:
/* Invalid page element type, not a button. */
return CMD_ERROR;
}
return CommandCost();
}