Codechange: add GoalQuestionButton scoped enum and EnumBitSet (#15723)

This commit is contained in:
Peter Nelson
2026-06-14 17:32:27 +01:00
committed by GitHub
parent ff055aa1ce
commit fcd33b25a7
7 changed files with 79 additions and 72 deletions
+8 -10
View File
@@ -235,21 +235,18 @@ CommandCost CmdSetGoalCompleted(DoCommandFlags flags, GoalID goal, bool complete
* @param uniqueid Unique ID to use for this question.
* @param target Company or client for which this question is.
* @param is_client Question target: false - company, true - client.
* @param button_mask Buttons of the question.
* @param buttons Buttons of the question.
* @param type Question type.
* @param text Text of the question.
* @return the cost of this operation or an error
*/
CommandCost CmdGoalQuestion(DoCommandFlags flags, uint16_t uniqueid, uint32_t target, bool is_client, uint32_t button_mask, GoalQuestionType type, const EncodedString &text)
CommandCost CmdGoalQuestion(DoCommandFlags flags, uint16_t uniqueid, uint32_t target, bool is_client, GoalQuestionButtons buttons, GoalQuestionType type, const EncodedString &text)
{
static_assert(sizeof(uint32_t) >= sizeof(CompanyID));
CompanyID company = (CompanyID)target;
static_assert(sizeof(uint32_t) >= sizeof(ClientID));
ClientID client = (ClientID)target;
static_assert(GOAL_QUESTION_BUTTON_COUNT < 29);
button_mask &= (1U << GOAL_QUESTION_BUTTON_COUNT) - 1;
if (_current_company != OWNER_DEITY) return CMD_ERROR;
if (text.empty()) return CMD_ERROR;
if (is_client) {
@@ -261,7 +258,8 @@ CommandCost CmdGoalQuestion(DoCommandFlags flags, uint16_t uniqueid, uint32_t ta
if (company != CompanyID::Invalid() && !Company::IsValidID(company)) return CMD_ERROR;
}
uint min_buttons = (type == GoalQuestionType::Question ? 1 : 0);
if (CountBits(button_mask) < min_buttons || CountBits(button_mask) > 3) return CMD_ERROR;
if (!buttons.IsValid()) return CMD_ERROR;
if (buttons.Count() < min_buttons || buttons.Count() > 3) return CMD_ERROR;
if (type >= GoalQuestionType::End) return CMD_ERROR;
if (flags.Test(DoCommandFlag::Execute)) {
@@ -271,7 +269,7 @@ CommandCost CmdGoalQuestion(DoCommandFlags flags, uint16_t uniqueid, uint32_t ta
if (company == CompanyID::Invalid() && !Company::IsValidID(_local_company)) return CommandCost();
if (company != CompanyID::Invalid() && company != _local_company) return CommandCost();
}
ShowGoalQuestion(uniqueid, type, button_mask, text);
ShowGoalQuestion(uniqueid, type, buttons, text);
}
return CommandCost();
@@ -284,9 +282,9 @@ CommandCost CmdGoalQuestion(DoCommandFlags flags, uint16_t uniqueid, uint32_t ta
* @param button Button the company pressed
* @return the cost of this operation or an error
*/
CommandCost CmdGoalQuestionAnswer(DoCommandFlags flags, uint16_t uniqueid, uint8_t button)
CommandCost CmdGoalQuestionAnswer(DoCommandFlags flags, uint16_t uniqueid, GoalQuestionButton button)
{
if (button >= GOAL_QUESTION_BUTTON_COUNT) return CMD_ERROR;
if (button >= GoalQuestionButton::End) return CMD_ERROR;
if (_current_company == OWNER_DEITY) {
/* It has been requested to close this specific question on all clients */
@@ -301,7 +299,7 @@ CommandCost CmdGoalQuestionAnswer(DoCommandFlags flags, uint16_t uniqueid, uint8
}
if (flags.Test(DoCommandFlag::Execute)) {
Game::NewEvent(new ScriptEventGoalQuestionAnswer(uniqueid, _current_company, (ScriptGoal::QuestionButton)(1 << button)));
Game::NewEvent(new ScriptEventGoalQuestionAnswer(uniqueid, _current_company, static_cast<ScriptGoal::QuestionButton>(GoalQuestionButtons{button}.base())));
}
return CommandCost();
+2 -2
View File
@@ -19,8 +19,8 @@ CommandCost CmdSetGoalDestination(DoCommandFlags flags, GoalID goal, GoalType ty
CommandCost CmdSetGoalText(DoCommandFlags flags, GoalID goal, const EncodedString &text);
CommandCost CmdSetGoalProgress(DoCommandFlags flags, GoalID goal, const EncodedString &text);
CommandCost CmdSetGoalCompleted(DoCommandFlags flags, GoalID goal, bool completed);
CommandCost CmdGoalQuestion(DoCommandFlags flags, uint16_t uniqueid, uint32_t target, bool is_client, uint32_t button_mask, GoalQuestionType type, const EncodedString &text);
CommandCost CmdGoalQuestionAnswer(DoCommandFlags flags, uint16_t uniqueid, uint8_t button);
CommandCost CmdGoalQuestion(DoCommandFlags flags, uint16_t uniqueid, uint32_t target, bool is_client, GoalQuestionButtons buttons, GoalQuestionType type, const EncodedString &text);
CommandCost CmdGoalQuestionAnswer(DoCommandFlags flags, uint16_t uniqueid, GoalQuestionButton button);
DEF_CMD_TRAIT(Commands::CreateGoal, CmdCreateGoal, CommandFlags({CommandFlag::Deity, CommandFlag::StrCtrl}), CommandType::OtherManagement)
DEF_CMD_TRAIT(Commands::RemoveGoal, CmdRemoveGoal, CommandFlag::Deity, CommandType::OtherManagement)
+21 -36
View File
@@ -318,46 +318,39 @@ void ShowGoalsList(CompanyID company)
/** Ask a question about a goal. */
struct GoalQuestionWindow : public Window {
EncodedString question{}; ///< Question to ask (private copy).
int buttons = 0; ///< Number of valid buttons in #button.
std::array<int, 3> button{}; ///< Buttons to display.
TextColour colour{}; ///< Colour of the question text.
EncodedString question; ///< Question to ask (private copy).
GoalQuestionButtons buttons; ///< Buttons to display.
TextColour colour; ///< Colour of the question text.
GoalQuestionWindow(WindowDesc &desc, WindowNumber window_number, TextColour colour, uint32_t button_mask, const EncodedString &question) : Window(desc), colour(colour)
/**
* Construct a new Goal Question Window.
* @param desc Window description.
* @param window_number Number for the window.
* @param colour Colour of the question text.
* @param buttons Buttons to display.
* @param question Question to ask.
*/
GoalQuestionWindow(WindowDesc &desc, WindowNumber window_number, TextColour colour, GoalQuestionButtons buttons, const EncodedString &question)
: Window(desc), question(question), buttons(buttons), colour(colour)
{
this->question = question;
/* Figure out which buttons we have to enable. */
int n = 0;
for (uint bit : SetBitIterator(button_mask)) {
if (bit >= GOAL_QUESTION_BUTTON_COUNT) break;
this->button[n++] = bit;
if (n == 3) break;
}
this->buttons = n;
assert(this->buttons < 4);
assert(this->buttons.Count() < 4);
this->CreateNestedTree();
if (this->buttons == 0) {
if (this->buttons.None()) {
this->GetWidget<NWidgetStacked>(WID_GQ_BUTTONS)->SetDisplayedPlane(SZSP_HORIZONTAL);
} else {
this->GetWidget<NWidgetStacked>(WID_GQ_BUTTONS)->SetDisplayedPlane(this->buttons - 1);
this->GetWidget<NWidgetStacked>(WID_GQ_BUTTONS)->SetDisplayedPlane(this->buttons.Count() - 1);
}
this->FinishInitNested(window_number);
}
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
{
switch (widget) {
case WID_GQ_BUTTON_1:
return GetString(STR_GOAL_QUESTION_BUTTON_CANCEL + this->button[0]);
case WID_GQ_BUTTON_2:
return GetString(STR_GOAL_QUESTION_BUTTON_CANCEL + this->button[1]);
case WID_GQ_BUTTON_3:
return GetString(STR_GOAL_QUESTION_BUTTON_CANCEL + this->button[2]);
return GetString(STR_GOAL_QUESTION_BUTTON_CANCEL + to_underlying(this->buttons.GetNthSetBit(widget - WID_GQ_BUTTON_1).value_or(GoalQuestionButton::Cancel)));
default:
return this->Window::GetWidgetString(widget, stringid);
@@ -368,17 +361,9 @@ struct GoalQuestionWindow : public Window {
{
switch (widget) {
case WID_GQ_BUTTON_1:
Command<Commands::GoalQuestionAnswer>::Post(this->window_number, this->button[0]);
this->Close();
break;
case WID_GQ_BUTTON_2:
Command<Commands::GoalQuestionAnswer>::Post(this->window_number, this->button[1]);
this->Close();
break;
case WID_GQ_BUTTON_3:
Command<Commands::GoalQuestionAnswer>::Post(this->window_number, this->button[2]);
Command<Commands::GoalQuestionAnswer>::Post(this->window_number, this->buttons.GetNthSetBit(widget - WID_GQ_BUTTON_1).value_or(GoalQuestionButton::Cancel));
this->Close();
break;
}
@@ -471,11 +456,11 @@ static EnumIndexArray<WindowDesc, GoalQuestionType, GoalQuestionType::End> _goal
* Display a goal question.
* @param id Window number to use.
* @param type Type of question.
* @param button_mask Buttons to display.
* @param buttons Buttons to display.
* @param question Question to ask.
*/
void ShowGoalQuestion(uint16_t id, GoalQuestionType type, uint32_t button_mask, const EncodedString &question)
void ShowGoalQuestion(uint16_t id, GoalQuestionType type, GoalQuestionButtons buttons, const EncodedString &question)
{
assert(type < GoalQuestionType::End);
new GoalQuestionWindow(_goal_question_list_desc[type], id, type == GoalQuestionType::Error ? TextColour::White : TextColour::Black, button_mask, question);
new GoalQuestionWindow(_goal_question_list_desc[type], id, type == GoalQuestionType::Error ? TextColour::White : TextColour::Black, buttons, question);
}
+1 -1
View File
@@ -15,6 +15,6 @@
#include "strings_type.h"
void ShowGoalsList(CompanyID company);
void ShowGoalQuestion(uint16_t id, GoalQuestionType type, uint32_t button_mask, const EncodedString &question);
void ShowGoalQuestion(uint16_t id, GoalQuestionType type, GoalQuestionButtons buttons, const EncodedString &question);
#endif /* GOAL_TYPE_H */
+26 -2
View File
@@ -12,8 +12,6 @@
#include "core/pool_type.hpp"
static const uint32_t GOAL_QUESTION_BUTTON_COUNT = 18; ///< Amount of buttons available.
/** Types of goal questions. */
enum class GoalQuestionType : uint8_t {
Question = 0, ///< Asking a simple question; title: Question.
@@ -23,6 +21,32 @@ enum class GoalQuestionType : uint8_t {
End, ///< End marker.
};
/** Types of buttons that can be in the question window. */
enum class GoalQuestionButton : uint8_t {
Cancel, ///< Cancel button.
Ok, ///< OK button.
No, ///< No button.
Yes, ///< Yes button.
Decline, ///< Decline button.
Accept, ///< Accept button.
Ignore, ///< Ignore button.
Retry, ///< Retry button.
Previous, ///< Previous button.
Next, ///< Next button.
Stop, ///< Stop button.
Start, ///< Start button.
Go, ///< Go button.
Continue, ///< Continue button.
Restart, ///< Restart button.
Postpone, ///< Postpone button.
Surrender, ///< Surrender button.
Close, ///< Close button.
End, ///< End marker.
};
/** Bitset of \c GoalQuestionButton elements. */
using GoalQuestionButtons = EnumBitSet<GoalQuestionButton, uint32_t, GoalQuestionButton::End>;
/** Types of goal destinations */
enum class GoalType : uint8_t {
None, ///< Destination is not linked
+3 -3
View File
@@ -135,11 +135,11 @@
EnforcePreconditionEncodedText(false, text);
uint min_buttons = (type == QT_QUESTION ? 1 : 0);
EnforcePrecondition(false, CountBits<uint64_t>(buttons) >= min_buttons && CountBits<uint64_t>(buttons) <= 3);
EnforcePrecondition(false, buttons >= 0 && buttons < (1 << ::GOAL_QUESTION_BUTTON_COUNT));
EnforcePrecondition(false, buttons >= 0 && static_cast<::GoalQuestionButtons>(buttons).IsValid());
EnforcePrecondition(false, static_cast<::GoalQuestionType>(type) < ::GoalQuestionType::End);
EnforcePrecondition(false, uniqueid >= 0 && uniqueid <= UINT16_MAX);
return ScriptObject::Command<Commands::GoalQuestion>::Do(uniqueid, target, is_client, buttons, static_cast<::GoalQuestionType>(type), text);
return ScriptObject::Command<Commands::GoalQuestion>::Do(uniqueid, target, is_client, static_cast<::GoalQuestionButtons>(buttons), static_cast<::GoalQuestionType>(type), text);
}
/* static */ bool ScriptGoal::Question(SQInteger uniqueid, ScriptCompany::CompanyID company, Text *question, QuestionType type, SQInteger buttons)
@@ -160,5 +160,5 @@
EnforceDeityMode(false);
EnforcePrecondition(false, uniqueid >= 0 && uniqueid <= UINT16_MAX);
return ScriptObject::Command<Commands::GoalQuestionAnswer>::Do(uniqueid, 0);
return ScriptObject::Command<Commands::GoalQuestionAnswer>::Do(uniqueid, ::GoalQuestionButton::Cancel);
}
+18 -18
View File
@@ -56,24 +56,24 @@ public:
*/
enum QuestionButton {
/* Note: these values represent part of the string list starting with STR_GOAL_QUESTION_BUTTON_CANCEL */
BUTTON_CANCEL = (1 << 0), ///< Cancel button.
BUTTON_OK = (1 << 1), ///< OK button.
BUTTON_NO = (1 << 2), ///< No button.
BUTTON_YES = (1 << 3), ///< Yes button.
BUTTON_DECLINE = (1 << 4), ///< Decline button.
BUTTON_ACCEPT = (1 << 5), ///< Accept button.
BUTTON_IGNORE = (1 << 6), ///< Ignore button.
BUTTON_RETRY = (1 << 7), ///< Retry button.
BUTTON_PREVIOUS = (1 << 8), ///< Previous button.
BUTTON_NEXT = (1 << 9), ///< Next button.
BUTTON_STOP = (1 << 10), ///< Stop button.
BUTTON_START = (1 << 11), ///< Start button.
BUTTON_GO = (1 << 12), ///< Go button.
BUTTON_CONTINUE = (1 << 13), ///< Continue button.
BUTTON_RESTART = (1 << 14), ///< Restart button.
BUTTON_POSTPONE = (1 << 15), ///< Postpone button.
BUTTON_SURRENDER = (1 << 16), ///< Surrender button.
BUTTON_CLOSE = (1 << 17), ///< Close button.
BUTTON_CANCEL = ::GoalQuestionButtons{::GoalQuestionButton::Cancel}.base(), ///< Cancel button.
BUTTON_OK = ::GoalQuestionButtons{::GoalQuestionButton::Ok}.base(), ///< OK button.
BUTTON_NO = ::GoalQuestionButtons{::GoalQuestionButton::No}.base(), ///< No button.
BUTTON_YES = ::GoalQuestionButtons{::GoalQuestionButton::Yes}.base(), ///< Yes button.
BUTTON_DECLINE = ::GoalQuestionButtons{::GoalQuestionButton::Decline}.base(), ///< Decline button.
BUTTON_ACCEPT = ::GoalQuestionButtons{::GoalQuestionButton::Accept}.base(), ///< Accept button.
BUTTON_IGNORE = ::GoalQuestionButtons{::GoalQuestionButton::Ignore}.base(), ///< Ignore button.
BUTTON_RETRY = ::GoalQuestionButtons{::GoalQuestionButton::Retry}.base(), ///< Retry button.
BUTTON_PREVIOUS = ::GoalQuestionButtons{::GoalQuestionButton::Previous}.base(), ///< Previous button.
BUTTON_NEXT = ::GoalQuestionButtons{::GoalQuestionButton::Next}.base(), ///< Next button.
BUTTON_STOP = ::GoalQuestionButtons{::GoalQuestionButton::Stop}.base(), ///< Stop button.
BUTTON_START = ::GoalQuestionButtons{::GoalQuestionButton::Start}.base(), ///< Start button.
BUTTON_GO = ::GoalQuestionButtons{::GoalQuestionButton::Go}.base(), ///< Go button.
BUTTON_CONTINUE = ::GoalQuestionButtons{::GoalQuestionButton::Continue}.base(), ///< Continue button.
BUTTON_RESTART = ::GoalQuestionButtons{::GoalQuestionButton::Restart}.base(), ///< Restart button.
BUTTON_POSTPONE = ::GoalQuestionButtons{::GoalQuestionButton::Postpone}.base(), ///< Postpone button.
BUTTON_SURRENDER = ::GoalQuestionButtons{::GoalQuestionButton::Surrender}.base(), ///< Surrender button.
BUTTON_CLOSE = ::GoalQuestionButtons{::GoalQuestionButton::Close}.base(), ///< Close button.
};
/**