Fix #8316: Make sort industries by production and transported with a cargo filter...
[openttd-github.git] / src / goal.cpp
blobc611650597d59912715064c3b5e568caed3e25f5
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"
27 #include "safeguards.h"
30 GoalID _new_goal_id;
32 GoalPool _goal_pool("Goal");
33 INSTANTIATE_POOL_METHODS(Goal)
35 /**
36 * Create a new goal.
37 * @param tile unused.
38 * @param flags type of operation
39 * @param p1 various bitstuffed elements
40 * - p1 = (bit 0 - 7) - GoalType of destination.
41 * - p1 = (bit 8 - 15) - Company for which this goal is.
42 * @param p2 GoalTypeID of destination.
43 * @param text Text of the goal.
44 * @return the cost of this operation or an error
46 CommandCost CmdCreateGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
48 if (!Goal::CanAllocateItem()) return CMD_ERROR;
50 GoalType type = (GoalType)GB(p1, 0, 8);
51 CompanyID company = (CompanyID)GB(p1, 8, 8);
53 if (_current_company != OWNER_DEITY) return CMD_ERROR;
54 if (text.empty()) return CMD_ERROR;
55 if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
57 switch (type) {
58 case GT_NONE:
59 if (p2 != 0) return CMD_ERROR;
60 break;
62 case GT_TILE:
63 if (!IsValidTile(p2)) return CMD_ERROR;
64 break;
66 case GT_INDUSTRY:
67 if (!Industry::IsValidID(p2)) return CMD_ERROR;
68 break;
70 case GT_TOWN:
71 if (!Town::IsValidID(p2)) return CMD_ERROR;
72 break;
74 case GT_COMPANY:
75 if (!Company::IsValidID(p2)) return CMD_ERROR;
76 break;
78 case GT_STORY_PAGE: {
79 if (!StoryPage::IsValidID(p2)) return CMD_ERROR;
80 CompanyID story_company = StoryPage::Get(p2)->company;
81 if (company == INVALID_COMPANY ? story_company != INVALID_COMPANY : story_company != INVALID_COMPANY && story_company != company) return CMD_ERROR;
82 break;
85 default: return CMD_ERROR;
88 if (flags & DC_EXEC) {
89 Goal *g = new Goal();
90 g->type = type;
91 g->dst = p2;
92 g->company = company;
93 g->text = stredup(text.c_str());
94 g->progress = nullptr;
95 g->completed = false;
97 if (g->company == INVALID_COMPANY) {
98 InvalidateWindowClassesData(WC_GOALS_LIST);
99 } else {
100 InvalidateWindowData(WC_GOALS_LIST, g->company);
102 if (Goal::GetNumItems() == 1) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
104 _new_goal_id = g->index;
107 return CommandCost();
111 * Remove a goal.
112 * @param tile unused.
113 * @param flags type of operation
114 * @param p1 GoalID to remove.
115 * @param p2 unused.
116 * @param text unused.
117 * @return the cost of this operation or an error
119 CommandCost CmdRemoveGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
121 if (_current_company != OWNER_DEITY) return CMD_ERROR;
122 if (!Goal::IsValidID(p1)) return CMD_ERROR;
124 if (flags & DC_EXEC) {
125 Goal *g = Goal::Get(p1);
126 CompanyID c = g->company;
127 delete g;
129 if (c == INVALID_COMPANY) {
130 InvalidateWindowClassesData(WC_GOALS_LIST);
131 } else {
132 InvalidateWindowData(WC_GOALS_LIST, c);
134 if (Goal::GetNumItems() == 0) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
137 return CommandCost();
141 * Update goal text of a goal.
142 * @param tile unused.
143 * @param flags type of operation
144 * @param p1 GoalID to update.
145 * @param p2 unused
146 * @param text Text of the goal.
147 * @return the cost of this operation or an error
149 CommandCost CmdSetGoalText(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
151 if (_current_company != OWNER_DEITY) return CMD_ERROR;
152 if (!Goal::IsValidID(p1)) return CMD_ERROR;
153 if (text.empty()) return CMD_ERROR;
155 if (flags & DC_EXEC) {
156 Goal *g = Goal::Get(p1);
157 free(g->text);
158 g->text = stredup(text.c_str());
160 if (g->company == INVALID_COMPANY) {
161 InvalidateWindowClassesData(WC_GOALS_LIST);
162 } else {
163 InvalidateWindowData(WC_GOALS_LIST, g->company);
167 return CommandCost();
171 * Update progress text of a goal.
172 * @param tile unused.
173 * @param flags type of operation
174 * @param p1 GoalID to update.
175 * @param p2 unused
176 * @param text Progress text of the goal.
177 * @return the cost of this operation or an error
179 CommandCost CmdSetGoalProgress(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
181 if (_current_company != OWNER_DEITY) return CMD_ERROR;
182 if (!Goal::IsValidID(p1)) return CMD_ERROR;
184 if (flags & DC_EXEC) {
185 Goal *g = Goal::Get(p1);
186 free(g->progress);
187 if (text.empty()) {
188 g->progress = nullptr;
189 } else {
190 g->progress = stredup(text.c_str());
193 if (g->company == INVALID_COMPANY) {
194 InvalidateWindowClassesData(WC_GOALS_LIST);
195 } else {
196 InvalidateWindowData(WC_GOALS_LIST, g->company);
200 return CommandCost();
204 * Update completed state of a goal.
205 * @param tile unused.
206 * @param flags type of operation
207 * @param p1 GoalID to update.
208 * @param p2 completed state. If goal is completed, set to 1, otherwise 0.
209 * @param text unused
210 * @return the cost of this operation or an error
212 CommandCost CmdSetGoalCompleted(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
214 if (_current_company != OWNER_DEITY) return CMD_ERROR;
215 if (!Goal::IsValidID(p1)) return CMD_ERROR;
217 if (flags & DC_EXEC) {
218 Goal *g = Goal::Get(p1);
219 g->completed = p2 == 1;
221 if (g->company == INVALID_COMPANY) {
222 InvalidateWindowClassesData(WC_GOALS_LIST);
223 } else {
224 InvalidateWindowData(WC_GOALS_LIST, g->company);
228 return CommandCost();
232 * Ask a goal related question
233 * @param tile unused.
234 * @param flags type of operation
235 * @param p1 various bitstuffed elements
236 * - p1 = (bit 0 - 15) - Unique ID to use for this question.
237 * - p1 = (bit 16 - 31) - Company or client for which this question is.
238 * @param p2 various bitstuffed elements
239 * - p2 = (bit 0 - 17) - Buttons of the question.
240 * - p2 = (bit 29 - 30) - Question type.
241 * - p2 = (bit 31) - Question target: 0 - company, 1 - client.
242 * @param text Text of the question.
243 * @return the cost of this operation or an error
245 CommandCost CmdGoalQuestion(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
247 uint16 uniqueid = (uint16)GB(p1, 0, 16);
248 CompanyID company = (CompanyID)GB(p1, 16, 8);
249 ClientID client = (ClientID)GB(p1, 16, 16);
251 static_assert(GOAL_QUESTION_BUTTON_COUNT < 29);
252 uint32 button_mask = GB(p2, 0, GOAL_QUESTION_BUTTON_COUNT);
253 byte type = GB(p2, 29, 2);
254 bool is_client = HasBit(p2, 31);
256 if (_current_company != OWNER_DEITY) return CMD_ERROR;
257 if (text.empty()) return CMD_ERROR;
258 if (is_client) {
259 /* Only check during pre-flight; the client might have left between
260 * testing and executing. In that case it is fine to just ignore the
261 * fact the client is no longer here. */
262 if (!(flags & DC_EXEC) && _network_server && NetworkClientInfo::GetByClientID(client) == nullptr) return CMD_ERROR;
263 } else {
264 if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
266 uint min_buttons = (type == GQT_QUESTION ? 1 : 0);
267 if (CountBits(button_mask) < min_buttons || CountBits(button_mask) > 3) return CMD_ERROR;
268 if (type >= GQT_END) return CMD_ERROR;
270 if (flags & DC_EXEC) {
271 if (is_client) {
272 if (client != _network_own_client_id) return CommandCost();
273 } else {
274 if (company == INVALID_COMPANY && !Company::IsValidID(_local_company)) return CommandCost();
275 if (company != INVALID_COMPANY && company != _local_company) return CommandCost();
277 ShowGoalQuestion(uniqueid, type, button_mask, text.c_str());
280 return CommandCost();
284 * Reply to a goal question.
285 * @param tile unused.
286 * @param flags type of operation
287 * @param p1 Unique ID to use for this question.
288 * @param p2 Button the company pressed
289 * @param text Text of the question.
290 * @return the cost of this operation or an error
292 CommandCost CmdGoalQuestionAnswer(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
294 if (p1 > UINT16_MAX) return CMD_ERROR;
295 if (p2 >= GOAL_QUESTION_BUTTON_COUNT) return CMD_ERROR;
297 if (_current_company == OWNER_DEITY) {
298 /* It has been requested to close this specific question on all clients */
299 if (flags & DC_EXEC) CloseWindowById(WC_GOAL_QUESTION, p1);
300 return CommandCost();
303 if (_networking && _local_company == _current_company) {
304 /* Somebody in the same company answered the question. Close the window */
305 if (flags & DC_EXEC) CloseWindowById(WC_GOAL_QUESTION, p1);
306 if (!_network_server) return CommandCost();
309 if (flags & DC_EXEC) {
310 Game::NewEvent(new ScriptEventGoalQuestionAnswer(p1, (ScriptCompany::CompanyID)(byte)_current_company, (ScriptGoal::QuestionButton)(1 << p2)));
313 return CommandCost();