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/>.
8 /** @file goal.cpp Handling of goals. */
11 #include "company_func.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"
23 #include "network/network.h"
24 #include "network/network_base.h"
25 #include "network/network_func.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
)
38 if (dest
!= 0) return false;
42 if (!IsValidTile(dest
)) return false;
46 if (!Industry::IsValidID(dest
)) return false;
50 if (!Town::IsValidID(dest
)) return false;
54 if (!Company::IsValidID(dest
)) return false;
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;
64 default: return false;
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
) {
95 if (g
->company
== INVALID_COMPANY
) {
96 InvalidateWindowClassesData(WC_GOALS_LIST
);
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
};
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
;
124 if (c
== INVALID_COMPANY
) {
125 InvalidateWindowClassesData(WC_GOALS_LIST
);
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
) {
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
);
175 if (g
->company
== INVALID_COMPANY
) {
176 InvalidateWindowClassesData(WC_GOALS_LIST
);
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
);
201 if (g
->company
== INVALID_COMPANY
) {
202 InvalidateWindowClassesData(WC_GOALS_LIST
);
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
);
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
;
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
;
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
) {
274 if (client
!= _network_own_client_id
) return CommandCost();
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();