diff --git a/src/command_type.h b/src/command_type.h index 174120fb91..4373a92e17 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -295,6 +295,7 @@ enum Commands : uint16 { 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 d6866ade02..716b36d477 100644 --- a/src/goal.cpp +++ b/src/goal.cpp @@ -31,6 +31,41 @@ GoalPool _goal_pool("Goal"); INSTANTIATE_POOL_METHODS(Goal) +/* static */ bool Goal::IsValidGoalDestination(CompanyID company, GoalType type, GoalTypeID dest) +{ + switch (type) { + case GT_NONE: + if (dest != 0) return false; + break; + + case GT_TILE: + if (!IsValidTile(dest)) return false; + break; + + case GT_INDUSTRY: + if (!Industry::IsValidID(dest)) return false; + break; + + case GT_TOWN: + if (!Town::IsValidID(dest)) return false; + break; + + case GT_COMPANY: + if (!Company::IsValidID(dest)) return false; + break; + + case GT_STORY_PAGE: { + 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 false; + } + return true; +} + /** * Create a new goal. * @param flags type of operation @@ -47,37 +82,7 @@ std::tuple CmdCreateGoal(DoCommandFlag flags, CompanyID com if (_current_company != OWNER_DEITY) return { CMD_ERROR, INVALID_GOAL }; if (text.empty()) return { CMD_ERROR, INVALID_GOAL }; if (company != INVALID_COMPANY && !Company::IsValidID(company)) return { CMD_ERROR, INVALID_GOAL }; - - switch (type) { - case GT_NONE: - if (dest != 0) return { CMD_ERROR, INVALID_GOAL }; - break; - - case GT_TILE: - if (!IsValidTile(dest)) return { CMD_ERROR, INVALID_GOAL }; - break; - - case GT_INDUSTRY: - if (!Industry::IsValidID(dest)) return { CMD_ERROR, INVALID_GOAL }; - break; - - case GT_TOWN: - if (!Town::IsValidID(dest)) return { CMD_ERROR, INVALID_GOAL }; - break; - - case GT_COMPANY: - if (!Company::IsValidID(dest)) return { CMD_ERROR, INVALID_GOAL }; - break; - - case GT_STORY_PAGE: { - if (!StoryPage::IsValidID(dest)) return { CMD_ERROR, INVALID_GOAL }; - CompanyID story_company = StoryPage::Get(dest)->company; - if (company == INVALID_COMPANY ? story_company != INVALID_COMPANY : story_company != INVALID_COMPANY && story_company != company) return { CMD_ERROR, INVALID_GOAL }; - break; - } - - default: return { CMD_ERROR, INVALID_GOAL }; - } + if (!Goal::IsValidGoalDestination(company, type, dest)) return { CMD_ERROR, INVALID_GOAL }; if (flags & DC_EXEC) { Goal *g = new Goal(); @@ -127,6 +132,29 @@ CommandCost CmdRemoveGoal(DoCommandFlag flags, GoalID goal) return CommandCost(); } +/** + * Update goal destination of a goal. + * @param flags type of operation + * @param goal GoalID to update. + * @param type GoalType of destination. + * @param dest GoalTypeID of destination. + * @return the cost of this operation or an error + */ +CommandCost CmdSetGoalDestination(DoCommandFlag flags, GoalID goal, GoalType type, GoalTypeID dest) +{ + 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 flags type of operation 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/goal_cmd.h b/src/goal_cmd.h index 3f5eac7d8c..c7e0ff1e24 100644 --- a/src/goal_cmd.h +++ b/src/goal_cmd.h @@ -15,6 +15,7 @@ std::tuple CmdCreateGoal(DoCommandFlag flags, CompanyID company, GoalType type, GoalTypeID dest, const std::string &text); CommandCost CmdRemoveGoal(DoCommandFlag flags, GoalID goal); +CommandCost CmdSetGoalDestination(DoCommandFlag flags, GoalID goal, GoalType type, GoalTypeID dest); CommandCost CmdSetGoalText(DoCommandFlag flags, GoalID goal, const std::string &text); CommandCost CmdSetGoalProgress(DoCommandFlag flags, GoalID goal, const std::string &text); CommandCost CmdSetGoalCompleted(DoCommandFlag flags, GoalID goal, bool completed); @@ -23,6 +24,7 @@ CommandCost CmdGoalQuestionAnswer(DoCommandFlag flags, uint16 uniqueid, uint8 bu DEF_CMD_TRAIT(CMD_CREATE_GOAL, CmdCreateGoal, CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT) DEF_CMD_TRAIT(CMD_REMOVE_GOAL, CmdRemoveGoal, CMD_DEITY, CMDT_OTHER_MANAGEMENT) +DEF_CMD_TRAIT(CMD_SET_GOAL_DESTINATION, CmdSetGoalDestination, CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT) DEF_CMD_TRAIT(CMD_SET_GOAL_TEXT, CmdSetGoalText, CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT) DEF_CMD_TRAIT(CMD_SET_GOAL_PROGRESS, CmdSetGoalProgress, CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT) DEF_CMD_TRAIT(CMD_SET_GOAL_COMPLETED, CmdSetGoalCompleted, CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT) 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 ad36c728b9..4430326101 100644 --- a/src/script/api/script_goal.cpp +++ b/src/script/api/script_goal.cpp @@ -28,6 +28,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); @@ -37,20 +51,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)); - 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); - - 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::Command::Do(&ScriptInstance::DoCommandReturnGoalID, c, (::GoalType)type, destination, text)) return GOAL_INVALID; + if (!ScriptObject::Command::Do(&ScriptInstance::DoCommandReturnGoalID, (::CompanyID)company, (::GoalType)type, destination, text)) return GOAL_INVALID; /* In case of test-mode, we return GoalID 0 */ return (ScriptGoal::GoalID)0; @@ -64,6 +67,16 @@ return ScriptObject::Command::Do(goal_id); } +/* 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::Command::Do(goal_id, (::GoalType)type, 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.