From 74c0678015bd03ea7d7a79172392fa797fe1105b Mon Sep 17 00:00:00 2001 From: mrmbernardi Date: Thu, 8 Jun 2023 19:00:31 +0200 Subject: [PATCH] Feature: [GS] Goal destination can be updated (#10817) (cherry picked from commit 35ef6c1723556d9bba4b9effb63b29ee7a78f1be) --- src/command.cpp | 2 + src/command_type.h | 1 + src/goal.cpp | 98 +++++++++++++++++++++---------- src/goal_base.h | 2 + src/script/api/game_changelog.hpp | 2 + src/script/api/script_goal.cpp | 39 ++++++++---- src/script/api/script_goal.hpp | 21 +++++++ 7 files changed, 121 insertions(+), 44 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 60aee30349..7267c0ef76 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -191,6 +191,7 @@ CommandProc CmdCompanyCtrl; CommandProc CmdCustomNewsItem; CommandProc CmdCreateGoal; CommandProc CmdRemoveGoal; +CommandProcEx CmdSetGoalDestination; CommandProc CmdSetGoalText; CommandProc CmdSetGoalProgress; CommandProc CmdSetGoalCompleted; @@ -448,6 +449,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdCustomNewsItem, CMD_STR_CTRL | CMD_DEITY | CMD_LOG_AUX, CMDT_OTHER_MANAGEMENT ), // CMD_CUSTOM_NEWS_ITEM DEF_CMD(CmdCreateGoal, CMD_STR_CTRL | CMD_DEITY | CMD_LOG_AUX, CMDT_OTHER_MANAGEMENT ), // CMD_CREATE_GOAL DEF_CMD(CmdRemoveGoal, CMD_DEITY | CMD_LOG_AUX, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_GOAL + DEF_CMD(CmdSetGoalDestination, CMD_DEITY | CMD_LOG_AUX, CMDT_OTHER_MANAGEMENT ), // CMD_SET_GOAL_DESTINATION DEF_CMD(CmdSetGoalText, CMD_STR_CTRL | CMD_DEITY | CMD_LOG_AUX, CMDT_OTHER_MANAGEMENT ), // CMD_SET_GOAL_TEXT DEF_CMD(CmdSetGoalProgress, CMD_STR_CTRL | CMD_DEITY | CMD_LOG_AUX, CMDT_OTHER_MANAGEMENT ), // CMD_SET_GOAL_PROGRESS DEF_CMD(CmdSetGoalCompleted, CMD_STR_CTRL | CMD_DEITY | CMD_LOG_AUX, CMDT_OTHER_MANAGEMENT ), // CMD_SET_GOAL_COMPLETED diff --git a/src/command_type.h b/src/command_type.h index 9d9b6ac0df..8405500748 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -412,6 +412,7 @@ enum Commands { CMD_CUSTOM_NEWS_ITEM, ///< create a custom news message CMD_CREATE_GOAL, ///< create a new goal CMD_REMOVE_GOAL, ///< remove a goal + CMD_SET_GOAL_DESTINATION, ///< update goal destination of a goal CMD_SET_GOAL_TEXT, ///< update goal text of a goal CMD_SET_GOAL_PROGRESS, ///< update goal progress text of a goal CMD_SET_GOAL_COMPLETED, ///< update goal completed status of a goal diff --git a/src/goal.cpp b/src/goal.cpp index 0c44715194..4cde61eebb 100644 --- a/src/goal.cpp +++ b/src/goal.cpp @@ -32,63 +32,69 @@ GoalID _new_goal_id; GoalPool _goal_pool("Goal"); INSTANTIATE_POOL_METHODS(Goal) -/** - * Create a new goal. - * @param tile unused. - * @param flags type of operation - * @param p1 various bitstuffed elements - * - p1 = (bit 0 - 7) - GoalType of destination. - * - p1 = (bit 8 - 15) - Company for which this goal is. - * @param p2 GoalTypeID of destination. - * @param text Text of the goal. - * @return the cost of this operation or an error - */ -CommandCost CmdCreateGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +/* static */ bool Goal::IsValidGoalDestination(CompanyID company, GoalType type, GoalTypeID dest) { - if (!Goal::CanAllocateItem()) return CMD_ERROR; - - GoalType type = (GoalType)GB(p1, 0, 8); - CompanyID company = (CompanyID)GB(p1, 8, 8); - - if (_current_company != OWNER_DEITY) return CMD_ERROR; - if (StrEmpty(text)) return CMD_ERROR; - if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR; - switch (type) { case GT_NONE: - if (p2 != 0) return CMD_ERROR; + if (dest != 0) return false; break; case GT_TILE: - if (!IsValidTile(p2)) return CMD_ERROR; + if (!IsValidTile(dest)) return false; break; case GT_INDUSTRY: - if (!Industry::IsValidID(p2)) return CMD_ERROR; + if (!Industry::IsValidID(dest)) return false; break; case GT_TOWN: - if (!Town::IsValidID(p2)) return CMD_ERROR; + if (!Town::IsValidID(dest)) return false; break; case GT_COMPANY: - if (!Company::IsValidID(p2)) return CMD_ERROR; + if (!Company::IsValidID(dest)) return false; break; case GT_STORY_PAGE: { - if (!StoryPage::IsValidID(p2)) return CMD_ERROR; - CompanyID story_company = StoryPage::Get(p2)->company; - if (company == INVALID_COMPANY ? story_company != INVALID_COMPANY : story_company != INVALID_COMPANY && story_company != company) return CMD_ERROR; + if (!StoryPage::IsValidID(dest)) return false; + CompanyID story_company = StoryPage::Get(dest)->company; + if (company == INVALID_COMPANY ? story_company != INVALID_COMPANY : story_company != INVALID_COMPANY && story_company != company) return false; break; } - default: return CMD_ERROR; + default: return false; } + return true; +} + +/** + * Create a new goal. + * @param tile unused. + * @param flags type of operation + * @param p1 various bitstuffed elements + * - p1 = (bit 0 - 7) - GoalType of destination. + * - p1 = (bit 8 - 15) - Company for which this goal is. + * @param p2 GoalTypeID of destination. + * @param text Text of the goal. + * @return the cost of this operation or an error + */ +CommandCost CmdCreateGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + if (!Goal::CanAllocateItem()) return CMD_ERROR; + + GoalType type = (GoalType)GB(p1, 0, 8); + CompanyID company = (CompanyID)GB(p1, 8, 8); + GoalTypeID dest = p2; + + if (_current_company != OWNER_DEITY) return CMD_ERROR; + if (StrEmpty(text)) return CMD_ERROR; + if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR; + if (!Goal::IsValidGoalDestination(company, type, dest)) return CMD_ERROR; if (flags & DC_EXEC) { Goal *g = new Goal(); g->type = type; - g->dst = p2; + g->dst = dest; g->company = company; if (StrEmpty(text)) { g->text.clear(); @@ -140,6 +146,36 @@ CommandCost CmdRemoveGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 return CommandCost(); } +/** + * Update goal destination of a goal. + * @param tile unused. + * @param flags type of operation + * @param p1 GoalID to update. + * @param p2 GoalTypeID of destination. + * @param p3 various bitstuffed elements + * - p3 = (bit 0 - 7) - GoalType of destination. + * @param p2 GoalTypeID of destination. + * @param text unused. + */ +CommandCost CmdSetGoalDestination(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, const CommandAuxiliaryBase *aux_data) +{ + GoalID goal = p1; + GoalTypeID dest = p2; + GoalType type = (GoalType)GB(p3, 0, 8); + + if (_current_company != OWNER_DEITY) return CMD_ERROR; + if (!Goal::IsValidID(goal)) return CMD_ERROR; + Goal *g = Goal::Get(goal); + if (!Goal::IsValidGoalDestination(g->company, type, dest)) return CMD_ERROR; + + if (flags & DC_EXEC) { + g->type = type; + g->dst = dest; + } + + return CommandCost(); +} + /** * Update goal text of a goal. * @param tile unused. diff --git a/src/goal_base.h b/src/goal_base.h index c73699514e..bf5954ee18 100644 --- a/src/goal_base.h +++ b/src/goal_base.h @@ -35,6 +35,8 @@ struct Goal : GoalPool::PoolItem<&_goal_pool> { * (Empty) destructor has to be defined else operator delete might be called with nullptr parameter */ inline ~Goal() { } + + static bool IsValidGoalDestination(CompanyID company, GoalType type, GoalTypeID dest); }; #endif /* GOAL_BASE_H */ diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index 9253748794..ba9c99fb12 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -75,6 +75,8 @@ * \li GSGroupList * \li GSVehicleList_Group * \li GSVehicleList_DefaultGroup + * \li GSGoal::IsValidGoalDestination + * \li GSGoal::SetDestination * * API removals: * \li GSError::ERR_PRECONDITION_TOO_MANY_PARAMETERS, that error is never returned anymore. diff --git a/src/script/api/script_goal.cpp b/src/script/api/script_goal.cpp index 0c450d6925..ff66a39ba5 100644 --- a/src/script/api/script_goal.cpp +++ b/src/script/api/script_goal.cpp @@ -27,6 +27,20 @@ return ::Goal::IsValidID(goal_id); } +/* static */ bool ScriptGoal::IsValidGoalDestination(ScriptCompany::CompanyID company, GoalType type, SQInteger destination) +{ + CompanyID c = (::CompanyID)company; + if (company == ScriptCompany::COMPANY_INVALID) c = INVALID_COMPANY; + StoryPage *story_page = nullptr; + if (type == GT_STORY_PAGE && ScriptStoryPage::IsValidStoryPage((ScriptStoryPage::StoryPageID)destination)) story_page = ::StoryPage::Get((ScriptStoryPage::StoryPageID)destination); + return (type == GT_NONE && destination == 0) || + (type == GT_TILE && ScriptMap::IsValidTile(destination)) || + (type == GT_INDUSTRY && ScriptIndustry::IsValidIndustry(destination)) || + (type == GT_TOWN && ScriptTown::IsValidTown(destination)) || + (type == GT_COMPANY && ScriptCompany::ResolveCompanyID((ScriptCompany::CompanyID)destination) != ScriptCompany::COMPANY_INVALID) || + (type == GT_STORY_PAGE && story_page != nullptr && (c == INVALID_COMPANY ? story_page->company == INVALID_COMPANY : story_page->company == INVALID_COMPANY || story_page->company == c)); +} + /* static */ ScriptGoal::GoalID ScriptGoal::New(ScriptCompany::CompanyID company, Text *goal, GoalType type, SQInteger destination) { CCountedPtr counter(goal); @@ -36,20 +50,9 @@ const std::string &text = goal->GetEncodedText(); EnforcePreconditionEncodedText(GOAL_INVALID, text); EnforcePrecondition(GOAL_INVALID, company == ScriptCompany::COMPANY_INVALID || ScriptCompany::ResolveCompanyID(company) != ScriptCompany::COMPANY_INVALID); + EnforcePrecondition(GOAL_INVALID, IsValidGoalDestination(company, type, destination)); - uint8 c = company; - if (company == ScriptCompany::COMPANY_INVALID) c = INVALID_COMPANY; - StoryPage *story_page = nullptr; - if (type == GT_STORY_PAGE && ScriptStoryPage::IsValidStoryPage((ScriptStoryPage::StoryPageID)destination)) story_page = ::StoryPage::Get((ScriptStoryPage::StoryPageID)destination); - - EnforcePrecondition(GOAL_INVALID, (type == GT_NONE && destination == 0) || - (type == GT_TILE && ScriptMap::IsValidTile(destination)) || - (type == GT_INDUSTRY && ScriptIndustry::IsValidIndustry(destination)) || - (type == GT_TOWN && ScriptTown::IsValidTown(destination)) || - (type == GT_COMPANY && ScriptCompany::ResolveCompanyID((ScriptCompany::CompanyID)destination) != ScriptCompany::COMPANY_INVALID) || - (type == GT_STORY_PAGE && story_page != nullptr && (c == INVALID_COMPANY ? story_page->company == INVALID_COMPANY : story_page->company == INVALID_COMPANY || story_page->company == c))); - - if (!ScriptObject::DoCommand(0, type | (c << 8), destination, CMD_CREATE_GOAL, text, &ScriptInstance::DoCommandReturnGoalID)) return GOAL_INVALID; + if (!ScriptObject::DoCommand(0, type | (company << 8), destination, CMD_CREATE_GOAL, text, &ScriptInstance::DoCommandReturnGoalID)) return GOAL_INVALID; /* In case of test-mode, we return GoalID 0 */ return (ScriptGoal::GoalID)0; @@ -63,6 +66,16 @@ return ScriptObject::DoCommand(0, goal_id, 0, CMD_REMOVE_GOAL); } +/* static */ bool ScriptGoal::SetDestination(GoalID goal_id, GoalType type, SQInteger destination) +{ + EnforceDeityMode(false); + EnforcePrecondition(false, IsValidGoal(goal_id)); + Goal *g = Goal::Get(goal_id); + EnforcePrecondition(false, IsValidGoalDestination((ScriptCompany::CompanyID)g->company, type, destination)); + + return ScriptObject::DoCommandEx(0, goal_id, destination, type, CMD_SET_GOAL_DESTINATION); +} + /* static */ bool ScriptGoal::SetText(GoalID goal_id, Text *goal) { CCountedPtr counter(goal); diff --git a/src/script/api/script_goal.hpp b/src/script/api/script_goal.hpp index c3b1ad2b29..72d61ccba2 100644 --- a/src/script/api/script_goal.hpp +++ b/src/script/api/script_goal.hpp @@ -89,6 +89,15 @@ public: */ static bool IsValidGoal(GoalID goal_id); + /** + * Check whether this is a valid goal destination. + * @param company The relevant company if a story page is the destination. + * @param type The type of the goal. + * @param destination The destination of the \a type type. + * @return True if and only if this goal destination is valid. + */ + static bool IsValidGoalDestination(ScriptCompany::CompanyID company, GoalType type, SQInteger destination); + /** * Create a new goal. * @param company The company to create the goal for, or ScriptCompany::COMPANY_INVALID for all. @@ -114,6 +123,18 @@ public: */ static bool Remove(GoalID goal_id); + /** + * Update goal destination of a goal. + * @param goal_id The goal to update. + * @param type The type of the goal. + * @param destination The destination of the \a type type. + * @return True if the action succeeded. + * @pre ScriptCompanyMode::IsDeity(). + * @pre IsValidGoal(goal_id). + * @pre IsValidGoalDestination(g->company, type, destination). + */ + static bool SetDestination(GoalID goal_id, GoalType type, SQInteger destination); + /** * Update goal text of a goal. * @param goal_id The goal to update.