1 /*
2  Copyright (C) 2010 - 2018 by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13  */
14 
15 /**
16  * @file
17  */
18 
19 #pragma once
20 
21 #include "side_actions.hpp"
22 
23 #include "units/map.hpp"
24 
25 #include <boost/dynamic_bitset.hpp>
26 
27 class CKey;
28 class team;
29 
30 namespace pathfind {
31 	struct marked_route;
32 }
33 
34 namespace wb {
35 
36 class mapbuilder;
37 class highlighter;
38 
39 /**
40  * This class is the frontend of the whiteboard framework for the rest of the Wesnoth code.
41  */
42 class manager
43 {
44 	friend struct future_map;
45 	friend struct future_map_if_active;
46 	friend struct real_map;
47 
48 public:
49 	manager(const manager&) = delete;
50 	manager& operator=(const manager&) = delete;
51 
52 	manager();
53 	~manager();
54 
55 	void print_help_once();
56 
57 	/** Determine whether the game is initialized and the current side has control of the game
58 	 *  i.e. the whiteboard can take over
59 	 */
60 	bool can_modify_game_state() const;
61 	/** Determine whether the whiteboard can be activated safely */
62 	bool can_activate() const;
63 	/** Determine whether the whiteboard is activated. */
is_active() const64 	bool is_active() const { return active_; }
65 	/** Activates/Deactivates the whiteboard*/
66 	void set_active(bool active);
67 	/** Called by the key that temporarily toggles the activated state when held */
68 	void set_invert_behavior(bool invert);
69 	/** Prevents the whiteboard from changing its activation state, as long as the returned reference is held */
get_activation_state_lock()70 	whiteboard_lock get_activation_state_lock() { return activation_state_lock_; }
71 
72 	/** Is the whiteboard in the process of executing an action? */
is_executing_actions() const73 	bool is_executing_actions() const { return executing_actions_; }
74 
75 	/** Used to ask the whiteboard if its action execution hotkeys should be available to the user */
76 	bool can_enable_execution_hotkeys() const;
77 	/** Used to ask the whiteboard if hotkeys affecting the action queue should be available to the user */
78 	bool can_enable_modifier_hotkeys() const;
79 	/** Used to ask the whiteboard if its action reordering hotkeys should be available to the user */
80 	bool can_enable_reorder_hotkeys() const;
81 	/** Used to ask permission to the wb to move a leader, to avoid invalidating planned recruits */
82 	bool allow_leader_to_move(const unit& leader) const;
83 	/** @ return true if the whiteboard is ready to end turn. Triggers the execution of remaining planned actions. */
84 	bool allow_end_turn();
85 	/**
86 	 * The on_* methods below inform the whiteboard of specific events
87 	 */
88 	void on_init_side();
89 	void on_finish_side_turn(int side);
90 	void on_mouseover_change(const map_location& hex);
on_deselect_hex()91 	void on_deselect_hex(){ erase_temp_move();}
92 	void on_gamestate_change();
93 	void on_viewer_change(size_t team_index);
94 	void on_change_controller(int side, const team& t);
95 	void on_kill_unit();
96 	/** Handles various cleanup right before removing an action from the queue */
97 	void pre_delete_action(action_ptr action);
98 	/** Handles various cleanup right after removing an action from the queue */
99 	void post_delete_action(action_ptr action);
100 
101 	/** Called by replay_network_sender to add whiteboard data to the outgoing network packets */
102 	void send_network_data();
103 	/** Called by turn_info::process_network_data() when network data needs to be processed */
104 	void process_network_data(const config&);
105 	/** Adds a side_actions::net_cmd to net_buffer_[team_index], whereupon it will (later) be sent to all allies */
106 	void queue_net_cmd(size_t team_index, const side_actions::net_cmd&);
107 
108 	/** Whether the current side has actions in the first turn of its planned actions queue */
109 	static bool current_side_has_actions();
110 
111 	/** Validates all actions of the current viewing side */
112 	void validate_viewer_actions();
113 
114 	/** Whether the planned unit map is currently applied */
has_planned_unit_map() const115 	bool has_planned_unit_map() const { return planned_unit_map_active_; }
116 
117 
118 	/**
119 	 * Called from the display before drawing.
120 	 */
121 	void pre_draw();
122 	/**
123 	 * Called from the display after drawing.
124 	 */
125 	void post_draw();
126 	/**
127 	 * Called from the display when drawing hexes, to allow the whiteboard to
128 	 * add visual elements. Some visual elements such as arrows and fake units
129 	 * are not handled through this function, but separately registered with the display.
130 	 */
131 	void draw_hex(const map_location& hex);
132 
133 	/** Creates a temporary visual arrow, that follows the cursor, for move creation purposes */
134 	void create_temp_move();
135 	/** Informs whether an arrow is being displayed for move creation purposes */
has_temp_move() const136 	bool has_temp_move() const { return route_ && !fake_units_.empty() && !move_arrows_.empty(); }
137 	/** Erase the temporary arrow */
138 	void erase_temp_move();
139 	/** Creates a move action for the current side, and erases the temp move.
140 	 *  The move action is inserted at the end of the queue, to be executed last. */
141 	void save_temp_move();
142 	/** @return an iterator to the unit that owns the temp move, resources::gameboard->units().end() if there's none. */
143 	unit_map::iterator get_temp_move_unit() const;
144 
145 	/** Creates an attack or attack-move action for the current side */
146 	void save_temp_attack(const map_location& attacker_loc, const map_location& defender_loc, int weapon_choice);
147 
148 	/** Creates a recruit action for the current side
149 	 *  @return true if manager has saved a planned recruit */
150 	bool save_recruit(const std::string& name, int side_num, const map_location& recruit_hex);
151 
152 	/** Creates a recall action for the current side
153 	 *  @return true if manager has saved a planned recall */
154 	bool save_recall(const unit& unit, int side_num, const map_location& recall_hex);
155 
156 	/** Creates a suppose-dead action for the current side */
157 	void save_suppose_dead(unit& curr_unit, const map_location& loc);
158 
159 	/** Executes first action in the queue for current side */
160 	void contextual_execute();
161 	/** Executes all actions for the current turn in sequence
162 	 *  @return true if the there are no more actions left for this turn when the method returns */
163 	bool execute_all_actions();
164 	/** Deletes last action in the queue for current side */
165 	void contextual_delete();
166 	/** Moves the action determined by the UI toward the beginning of the queue  */
167 	void contextual_bump_up_action();
168 	/** Moves the action determined by the UI toward the beginning of the queue  */
169 	void contextual_bump_down_action();
170 
171 	/** Get the highlight visitor instance in use by the manager */
get_highlighter()172 	std::weak_ptr<highlighter> get_highlighter() { return highlighter_; }
173 
174 	/** Checks whether the whiteboard has any planned action on any team */
175 	bool has_actions() const;
176 	/** Checks whether the specified unit has at least one planned action */
177 	bool unit_has_actions(unit const* unit) const;
178 
179 	/** Used to track gold spending per-side when building the planned unit map
180 	 *  Is referenced by the top bar gold display */
181 	int get_spent_gold_for(int side);
182 
183 	/** Determines whether or not the undo_stack should be cleared.
184 	 *  @todo When there are network allies, only clear the undo stack when we have set a preferences option */
185 	bool should_clear_undo() const;
186 
187 	/** Displays the whiteboard options dialog. */
188 	void options_dlg();
189 
190 private:
191 	/** Transforms the unit map so that it now reflects the future state of things,
192 	 *  i.e. when all planned actions will have been executed */
193 	void set_planned_unit_map();
194 	/** Restore the regular unit map */
195 	void set_real_unit_map();
196 
197 	void validate_actions_if_needed();
198 	/** Called by all of the save_***() methods after they have added their action to the queue */
199 	void update_plan_hiding(size_t viewing_team);
200 	void update_plan_hiding(); //same as above, but uses wb::viewer_team() as default argument
201 
202 	/** Tracks whether the whiteboard is active. */
203 	bool active_;
204 	bool inverted_behavior_;
205 	bool self_activate_once_;
206 #if 0
207 	bool print_help_once_;
208 #endif
209 	bool wait_for_side_init_;
210 	bool planned_unit_map_active_;
211 	/** Track whenever we're modifying actions, to avoid dual execution etc. */
212 	bool executing_actions_;
213 	/** Track whether we're in the process of executing all actions */
214 	bool executing_all_actions_;
215 	/** true if we're in the process of executing all action and should end turn once finished. */
216 	bool preparing_to_end_turn_;
217 	/** Track whether the gamestate changed and we need to validate actions. */
218 	bool gamestate_mutated_;
219 
220 	/** Reference counted "lock" to allow preventing whiteboard activation state changes */
221 	whiteboard_lock activation_state_lock_;
222 	/** Reference counted "lock" to prevent the building of the unit map at certain times */
223 	whiteboard_lock unit_map_lock_;
224 
225 
226 	std::unique_ptr<mapbuilder> mapbuilder_;
227 	std::shared_ptr<highlighter> highlighter_;
228 
229 	std::unique_ptr<pathfind::marked_route> route_;
230 
231 	std::vector<arrow_ptr> move_arrows_;
232 	std::vector<fake_unit_ptr> fake_units_;
233 	size_t temp_move_unit_underlying_id_;
234 
235 	const std::unique_ptr<CKey> key_poller_;
236 
237 	std::vector<map_location> hidden_unit_hexes_;
238 
239 	///net_buffer_[i] = whiteboard network data to be sent "from" teams[i].
240 	std::vector<config> net_buffer_;
241 
242 	///team_plans_hidden_[i] = whether or not to hide actions from teams[i].
243 	boost::dynamic_bitset<> team_plans_hidden_;
244 
245 	///used to keep track of units owning planned moves for visual ghosting/unghosting
246 	std::set<size_t> units_owning_moves_;
247 };
248 
249 /** Applies the planned unit map for the duration of the struct's life.
250  *  Reverts to real unit map on destruction, unless planned unit map was already applied when the struct was created. */
251 struct future_map
252 {
253 	future_map();
254 	~future_map();
255 	bool initial_planned_unit_map_;
256 };
257 
258 struct future_map_if
259 {
260 	const std::unique_ptr<future_map> future_map_;
261 
262 	/** @param cond If true, applies the planned unit map for the duration of the struct's life and reverts to real unit map on destruction.
263 			No effect if cond == false.
264 	*/
future_map_ifwb::future_map_if265 	future_map_if(bool cond)
266 		: future_map_(cond ? new future_map() : nullptr)
267 	{}
268 };
269 
270 /** ONLY IF whiteboard is currently active, applies the planned unit map for the duration of the struct's life.
271  *  Reverts to real unit map on destruction, unless planned unit map was already applied when the struct was created. */
272 struct future_map_if_active
273 {
274 	future_map_if_active();
275 	~future_map_if_active();
276 	bool initial_planned_unit_map_;
277 	bool whiteboard_active_;
278 };
279 
280 /** Ensures that the real unit map is active for the duration of the struct's life.
281  *  On destruction reverts to planned unit map if it was active when the struct was created. */
282 struct real_map
283 {
284 	real_map();
285 	~real_map();
286 	bool initial_planned_unit_map_;
287 	whiteboard_lock unit_map_lock_;
288 };
289 
290 } // end namespace wb
291