Codechange: Limit house default cargo label lists to original slot counts. (#13079)
[openttd-github.git] / src / goal.cpp
blobf33535fb743e9608a0e17668bc53aab098c4ea6f
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 goal.cpp Handling of goals. */
10 #include "stdafx.h"
11 #include "company_func.h"
12 #include "industry.h"
13 #include "town.h"
14 #include "window_func.h"
15 #include "goal_base.h"
16 #include "core/pool_func.hpp"
17 #include "game/game.hpp"
18 #include "command_func.h"
19 #include "company_base.h"
20 #include "story_base.h"
21 #include "string_func.h"
22 #include "gui.h"
23 #include "network/network.h"
24 #include "network/network_base.h"
25 #include "network/network_func.h"
26 #include "goal_cmd.h"
28 #include "safeguards.h"
31 GoalPool _goal_pool("Goal");
32 INSTANTIATE_POOL_METHODS(Goal)
34 /* static */ bool Goal::IsValidGoalDestination(CompanyID company, GoalType type, GoalTypeID dest)
36 switch (type) {
37 case GT_NONE:
38 if (dest != 0) return false;
39 break;
41 case GT_TILE:
42 if (!IsValidTile(dest)) return false;
43 break;
45 case GT_INDUSTRY:
46 if (!Industry::IsValidID(dest)) return false;
47 break;
49 case GT_TOWN:
50 if (!Town::IsValidID(dest)) return false;
51 break;
53 case GT_COMPANY:
54 if (!Company::IsValidID(dest)) return false;
55 break;
57 case GT_STORY_PAGE: {
58 if (!StoryPage::IsValidID(dest)) return false;
59 CompanyID story_company = StoryPage::Get(dest)->company;
60 if (company == INVALID_COMPANY ? story_company != INVALID_COMPANY : story_company != INVALID_COMPANY && story_company != company) return false;
61 break;
64 default: return false;
66 return true;
69 /**
70 * Create a new goal.
71 * @param flags type of operation
72 * @param company Company for which this goal is.
73 * @param type GoalType of destination.
74 * @param dest GoalTypeID of destination.
75 * @param text Text of the goal.
76 * @return the cost of this operation or an error
78 std::tuple<CommandCost, GoalID> CmdCreateGoal(DoCommandFlag flags, CompanyID company, GoalType type, GoalTypeID dest, const std::string &text)
80 if (!Goal::CanAllocateItem()) return { CMD_ERROR, INVALID_GOAL };
82 if (_current_company != OWNER_DEITY) return { CMD_ERROR, INVALID_GOAL };
83 if (text.empty()) return { CMD_ERROR, INVALID_GOAL };
84 if (company != INVALID_COMPANY && !Company::IsValidID(company)) return { CMD_ERROR, INVALID_GOAL };
85 if (!Goal::IsValidGoalDestination(company, type, dest)) return { CMD_ERROR, INVALID_GOAL };
87 if (flags & DC_EXEC) {
88 Goal *g = new Goal();
89 g->type = type;
90 g->dst = dest;
91 g->company = company;
92 g->text = text;
93 g->completed = false;
95 if (g->company == INVALID_COMPANY) {
96 InvalidateWindowClassesData(WC_GOALS_LIST);
97 } else {
98 InvalidateWindowData(WC_GOALS_LIST, g->company);
100 if (Goal::GetNumItems() == 1) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
102 return { CommandCost(), g->index };
105 return { CommandCost(), INVALID_GOAL };
109 * Remove a goal.
110 * @param flags type of operation
111 * @param goal GoalID to remove.
112 * @return the cost of this operation or an error
114 CommandCost CmdRemoveGoal(DoCommandFlag flags, GoalID goal)
116 if (_current_company != OWNER_DEITY) return CMD_ERROR;
117 if (!Goal::IsValidID(goal)) return CMD_ERROR;
119 if (flags & DC_EXEC) {
120 Goal *g = Goal::Get(goal);
121 CompanyID c = g->company;
122 delete g;
124 if (c == INVALID_COMPANY) {
125 InvalidateWindowClassesData(WC_GOALS_LIST);
126 } else {
127 InvalidateWindowData(WC_GOALS_LIST, c);
129 if (Goal::GetNumItems() == 0) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
132 return CommandCost();
136 * Update goal destination of a goal.
137 * @param flags type of operation
138 * @param goal GoalID to update.
139 * @param type GoalType of destination.
140 * @param dest GoalTypeID of destination.
141 * @return the cost of this operation or an error
143 CommandCost CmdSetGoalDestination(DoCommandFlag flags, GoalID goal, GoalType type, GoalTypeID dest)
145 if (_current_company != OWNER_DEITY) return CMD_ERROR;
146 if (!Goal::IsValidID(goal)) return CMD_ERROR;
147 Goal *g = Goal::Get(goal);
148 if (!Goal::IsValidGoalDestination(g->company, type, dest)) return CMD_ERROR;
150 if (flags & DC_EXEC) {
151 g->type = type;
152 g->dst = dest;
155 return CommandCost();
159 * Update goal text of a goal.
160 * @param flags type of operation
161 * @param goal GoalID to update.
162 * @param text Text of the goal.
163 * @return the cost of this operation or an error
165 CommandCost CmdSetGoalText(DoCommandFlag flags, GoalID goal, const std::string &text)
167 if (_current_company != OWNER_DEITY) return CMD_ERROR;
168 if (!Goal::IsValidID(goal)) return CMD_ERROR;
169 if (text.empty()) return CMD_ERROR;
171 if (flags & DC_EXEC) {
172 Goal *g = Goal::Get(goal);
173 g->text = text;
175 if (g->company == INVALID_COMPANY) {
176 InvalidateWindowClassesData(WC_GOALS_LIST);
177 } else {
178 InvalidateWindowData(WC_GOALS_LIST, g->company);
182 return CommandCost();
186 * Update progress text of a goal.
187 * @param flags type of operation
188 * @param goal GoalID to update.
189 * @param text Progress text of the goal.
190 * @return the cost of this operation or an error
192 CommandCost CmdSetGoalProgress(DoCommandFlag flags, GoalID goal, const std::string &text)
194 if (_current_company != OWNER_DEITY) return CMD_ERROR;
195 if (!Goal::IsValidID(goal)) return CMD_ERROR;
197 if (flags & DC_EXEC) {
198 Goal *g = Goal::Get(goal);
199 g->progress = text;
201 if (g->company == INVALID_COMPANY) {
202 InvalidateWindowClassesData(WC_GOALS_LIST);
203 } else {
204 InvalidateWindowData(WC_GOALS_LIST, g->company);
208 return CommandCost();
212 * Update completed state of a goal.
213 * @param flags type of operation
214 * @param goal GoalID to update.
215 * @param completed completed state of goal.
216 * @return the cost of this operation or an error
218 CommandCost CmdSetGoalCompleted(DoCommandFlag flags, GoalID goal, bool completed)
220 if (_current_company != OWNER_DEITY) return CMD_ERROR;
221 if (!Goal::IsValidID(goal)) return CMD_ERROR;
223 if (flags & DC_EXEC) {
224 Goal *g = Goal::Get(goal);
225 g->completed = completed;
227 if (g->company == INVALID_COMPANY) {
228 InvalidateWindowClassesData(WC_GOALS_LIST);
229 } else {
230 InvalidateWindowData(WC_GOALS_LIST, g->company);
234 return CommandCost();
238 * Ask a goal related question
239 * @param flags type of operation
240 * @param uniqueid Unique ID to use for this question.
241 * @param target Company or client for which this question is.
242 * @param is_client Question target: false - company, true - client.
243 * @param button_mask Buttons of the question.
244 * @param type Question type.
245 * @param text Text of the question.
246 * @return the cost of this operation or an error
248 CommandCost CmdGoalQuestion(DoCommandFlag flags, uint16_t uniqueid, uint32_t target, bool is_client, uint32_t button_mask, GoalQuestionType type, const std::string &text)
250 static_assert(sizeof(uint32_t) >= sizeof(CompanyID));
251 CompanyID company = (CompanyID)target;
252 static_assert(sizeof(uint32_t) >= sizeof(ClientID));
253 ClientID client = (ClientID)target;
255 static_assert(GOAL_QUESTION_BUTTON_COUNT < 29);
256 button_mask &= (1U << GOAL_QUESTION_BUTTON_COUNT) - 1;
258 if (_current_company != OWNER_DEITY) return CMD_ERROR;
259 if (text.empty()) return CMD_ERROR;
260 if (is_client) {
261 /* Only check during pre-flight; the client might have left between
262 * testing and executing. In that case it is fine to just ignore the
263 * fact the client is no longer here. */
264 if (!(flags & DC_EXEC) && _network_server && NetworkClientInfo::GetByClientID(client) == nullptr) return CMD_ERROR;
265 } else {
266 if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
268 uint min_buttons = (type == GQT_QUESTION ? 1 : 0);
269 if (CountBits(button_mask) < min_buttons || CountBits(button_mask) > 3) return CMD_ERROR;
270 if (type >= GQT_END) return CMD_ERROR;
272 if (flags & DC_EXEC) {
273 if (is_client) {
274 if (client != _network_own_client_id) return CommandCost();
275 } else {
276 if (company == INVALID_COMPANY && !Company::IsValidID(_local_company)) return CommandCost();
277 if (company != INVALID_COMPANY && company != _local_company) return CommandCost();
279 ShowGoalQuestion(uniqueid, type, button_mask, text);
282 return CommandCost();
286 * Reply to a goal question.
287 * @param flags type of operation
288 * @param uniqueid Unique ID to use for this question.
289 * @param button Button the company pressed
290 * @return the cost of this operation or an error
292 CommandCost CmdGoalQuestionAnswer(DoCommandFlag flags, uint16_t uniqueid, uint8_t button)
294 if (button >= GOAL_QUESTION_BUTTON_COUNT) return CMD_ERROR;
296 if (_current_company == OWNER_DEITY) {
297 /* It has been requested to close this specific question on all clients */
298 if (flags & DC_EXEC) CloseWindowById(WC_GOAL_QUESTION, uniqueid);
299 return CommandCost();
302 if (_networking && _local_company == _current_company) {
303 /* Somebody in the same company answered the question. Close the window */
304 if (flags & DC_EXEC) CloseWindowById(WC_GOAL_QUESTION, uniqueid);
305 if (!_network_server) return CommandCost();
308 if (flags & DC_EXEC) {
309 Game::NewEvent(new ScriptEventGoalQuestionAnswer(uniqueid, (ScriptCompany::CompanyID)(uint8_t)_current_company, (ScriptGoal::QuestionButton)(1 << button)));
312 return CommandCost();