The Battle for Wesnoth  1.17.17+dev
highlighter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2023
3  by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  */
19 
20 #include <algorithm>
21 #include <iterator>
22 #include <functional>
23 
25 
26 #include "whiteboard/action.hpp"
27 #include "whiteboard/attack.hpp"
28 #include "whiteboard/manager.hpp"
29 #include "whiteboard/move.hpp"
30 #include "whiteboard/recall.hpp"
31 #include "whiteboard/recruit.hpp"
34 #include "whiteboard/utility.hpp"
35 
36 #include "arrow.hpp"
37 #include "config.hpp"
38 #include "fake_unit_ptr.hpp"
39 #include "game_board.hpp"
40 #include "game_display.hpp"
41 #include "game_errors.hpp"
42 #include "play_controller.hpp"
43 #include "resources.hpp"
44 #include "units/unit.hpp"
46 #include "units/map.hpp"
47 #include "utils/ranges.hpp"
48 
49 namespace wb
50 {
51 
53  : mouseover_hex_()
54  , exclusive_display_hexes_()
55  , owner_unit_()
56  , selection_candidate_()
57  , selected_action_()
58  , main_highlight_()
59  , secondary_highlights_()
60  , side_actions_(side_actions)
61 {
62 }
63 
65 {
66  try {
68  unhighlight();
69  }
70  } catch (...) {}
71 }
72 
74 {
75  clear();
76 
77  if(!hex.valid()) {
78  return;
79  }
80 
81  real_map ensure_real_map;
82  mouseover_hex_ = hex;
83  //if we're right over a unit, just highlight all of this unit's actions
85  if(it != get_unit_map().end()) {
87 
88  if(resources::gameboard->get_team(it->side()).get_side_actions()->unit_has_actions(*it)) {
90  }
91 
92  //commented code below is to also select the first action of this unit as
93  //the main highlight; it doesn't fit too well in the UI
94 // side_actions::iterator action_it = side_actions_->find_first_action_of(*it);
95 // if(action_it != side_actions_->end()) {
96 // main_highlight_ = *action_it;
97 // }
98  }
99 
100  //Set the execution/deletion/bump targets.
101  if(owner_unit_) {
102  side_actions::iterator itor = side_actions_->find_first_action_of(*owner_unit_);
103  if(itor != side_actions_->end()) {
104  selected_action_ = *itor;
105  }
106  }
107 
108  //Overwrite the above selected_action_ if we find a better one
109  if(side_actions_->empty()) {
110  return;
111  }
113  /**@todo "is_numbering_hex" is not the "correct" criterion by which to
114  * select the highlighted/selected action. It's just convenient for me
115  * to use at the moment since it happens to coincide with the "correct"
116  * criterion, which is to use find_main_highlight.*/
117  if(act->is_numbering_hex(hex)) {
118  selected_action_ = act;
119  break;
120  }
121  }
122 }
123 
125 {
126  unhighlight();
127  main_highlight_.reset();
128  owner_unit_.reset();
129  secondary_highlights_.clear();
130  selected_action_.reset();
131 }
132 
134 {
135  //Find main action to highlight if any, as well as owner unit
137 
138  if(action_ptr main = main_highlight_.lock()) {
139  //Highlight main highlight
140  highlight_main_visitor hm_visitor(*this);
141  main->accept(hm_visitor);
142  }
143 
144  if(owner_unit_) {
145  //Find secondary actions to highlight
147 
148  //Make sure owner unit is the only one displayed in its hex
150  exclusive_display_hexes_.insert(owner_unit_->get_location());
151 
152  if(!secondary_highlights_.empty()) {
153  //Highlight secondary highlights
154  highlight_secondary_visitor hs_visitor(*this);
156  if(action_ptr action = weak.lock()) {
157  action->accept(hs_visitor);
158  }
159  }
160  }
161  }
162 }
163 
165 {
166  unhighlight_visitor uh_visitor(*this);
167 
168  //unhighlight main highlight
169  if(action_ptr main = main_highlight_.lock()) {
170  main->accept(uh_visitor);
171  }
172 
173  //unhighlight secondary highlights
175  if(action_ptr action = weak.lock()) {
176  action->accept(uh_visitor);
177  }
178  }
179 
180  //unhide other units if needed
183  }
184  exclusive_display_hexes_.clear();
185 }
186 
188 {
189  //Last action with a fake unit always gets normal appearance
190  if(move->get_fake_unit()) {
191  side_actions& sa = *resources::gameboard->teams().at(move->team_index()).get_side_actions().get();
192 
194  side_actions::iterator second_to_last_action = last_action != sa.end() && last_action != sa.begin() ? last_action - 1 : sa.end();
195 
196  bool this_is_last_action = last_action != sa.end() && move == *last_action;
197  bool last_action_has_fake_unit = last_action != sa.end() && (*last_action)->get_fake_unit();
198  bool this_is_second_to_last_action = (second_to_last_action != sa.end() && move == *second_to_last_action);
199 
200  if(this_is_last_action || (this_is_second_to_last_action && !last_action_has_fake_unit)) {
201  move->get_fake_unit()->anim_comp().set_standing(true);
202  }
203  }
204 }
205 
207 {
208  // Even if we already found an owner_unit_ in the mouseover hex,
209  // action destination hexes usually take priority over that
210  assert(main_highlight_.expired());
211  //@todo re-enable the following assert once I find out what happends to
212  // viewing side assignments after victory
213  //assert(side_actions_->team_index() == display::get_singleton()->viewing_team());
214 
216  if(action_ptr main = main_highlight_.lock()) {
217  owner_unit_ = main->get_unit();
218  }
219 }
220 
222 {
223  assert(owner_unit_);
224  assert(secondary_highlights_.empty());
225 
226  if(owner_unit_ == nullptr) {
227  return;
228  }
229 
230  // List all the actions of owner_unit_
231  std::deque<action_ptr> actions = find_actions_of(*owner_unit_);
232 
233  // Remove main_highlight_ if present
234  actions.erase(std::remove(actions.begin(), actions.end(), main_highlight_.lock()), actions.end());
235 
236  // Copy in secondary_highlights_
237  std::copy(actions.begin(), actions.end(), std::back_inserter(secondary_highlights_));
238 }
239 
240 
242 {
243  if(action_ptr locked = selected_action_.lock()) {
244  return *side_actions_->find_first_action_of(locked->get_unit_id());
245  } else {
246  return action_ptr();
247  }
248 }
250 {
251  if(action_ptr locked = selected_action_.lock()) {
252  return *side_actions_->find_last_action_of(locked->get_unit_id());
253  } else {
254  return action_ptr();
255  }
256 }
257 
259 {
260  return selected_action_.lock();
261 }
262 
264 {
265  if(owner_unit_) {
266  return owner_unit_;
267  } else {
268  return selection_candidate_;
269  }
270 }
271 
273 {
274  if(move->get_arrow()) {
276  }
277  if(move->get_fake_unit()) {
278  // TODO: find some highlight animation
279  move->get_fake_unit()->anim_comp().set_ghosted(true);
280  //Make sure the fake unit is the only one displayed in its hex
282  highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
283 
285  }
286 }
287 
289 {
290  // TODO: highlight the attack indicator
291  visit(std::static_pointer_cast<move>(attack));
292 }
293 
295 {
296  if(recruit->get_fake_unit()) {
297  // TODO: find some suitable effect for mouseover on planned recruit.
298 
299  //Make sure the fake unit is the only one displayed in its hex
301  highlighter_.exclusive_display_hexes_.insert(recruit->get_fake_unit()->get_location());
302  }
303 }
304 
306 {
307  if(move->get_arrow()) {
309  }
310  if(move->get_fake_unit()) {
311  move->get_fake_unit()->anim_comp().set_ghosted(true);
312  //Make sure the fake unit is the only one displayed in its hex
314  highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
315 
316  highlighter_.last_action_redraw(move);
317  }
318 }
319 
321 {
322  visit(std::static_pointer_cast<move>(attack));
323 }
324 
326 {
327  if(move->get_arrow()) {
329  }
330  if(move->get_fake_unit()) {
331  move->get_fake_unit()->anim_comp().set_disabled_ghosted(false);
332 
333  highlighter_.last_action_redraw(move);
334  }
335 }
336 
338 {
339  visit(std::static_pointer_cast<move>(attack));
340 }
341 
343 {
344  if(recall->get_fake_unit()) {
345  //@todo: find some suitable effect for mouseover on planned recall.
346 
347  //Make sure the fake unit is the only one displayed in its hex
349  highlighter_.exclusive_display_hexes_.insert(recall->get_fake_unit()->get_location());
350  }
351 }
353 {
354  assert(resources::gameboard);
355  return resources::gameboard->units();
356 }
357 
358 } // end namespace wb
Arrows destined to be drawn on the map.
bool add_exclusive_draw(const map_location &loc, unit &unit)
Allows a unit to request to be the only one drawn in its hex.
Definition: display.cpp:382
std::string remove_exclusive_draw(const map_location &loc)
Cancels an exclusive draw request.
Definition: display.cpp:392
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:101
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:86
virtual const unit_map & units() const override
Definition: game_board.hpp:113
static game_display * get_singleton()
Container associating units to locations.
Definition: map.hpp:99
unit_iterator find(std::size_t id)
Definition: map.cpp:301
Abstract base class for all the whiteboard planned actions.
Definition: action.hpp:34
std::size_t team_index() const
Returns the index of the team that owns this action.
Definition: action.hpp:84
virtual void accept(visitor &v)=0
virtual ~highlighter()
Definition: highlighter.cpp:64
side_actions_ptr side_actions_
Definition: highlighter.hpp:91
void find_main_highlight()
std::set< map_location > exclusive_display_hexes_
Definition: highlighter.hpp:83
action_ptr get_bump_target()
void last_action_redraw(move_ptr)
Redraw the given move action when needed.
secondary_highlights_t secondary_highlights_
Definition: highlighter.hpp:89
unit_ptr selection_candidate_
Definition: highlighter.hpp:85
action_ptr get_execute_target()
void set_mouseover_hex(const map_location &hex)
Definition: highlighter.cpp:73
map_location mouseover_hex_
Definition: highlighter.hpp:82
unit_ptr owner_unit_
Definition: highlighter.hpp:84
highlighter(side_actions_ptr side_actions)
Definition: highlighter.cpp:52
unit_map & get_unit_map()
action_ptr get_delete_target()
weak_action_ptr selected_action_
Definition: highlighter.hpp:87
unit_ptr get_selection_target()
void find_secondary_highlights()
weak_action_ptr main_highlight_
Definition: highlighter.hpp:88
A planned move, represented on the map by an arrow and a ghosted unit in the destination hex.
Definition: move.hpp:36
virtual size_t get_unit_id() const
Returns the id of the unit targeted by this action.
Definition: move.hpp:59
void set_arrow_brightness(ARROW_BRIGHTNESS x) const
Definition: move.hpp:98
virtual fake_unit_ptr get_fake_unit()
Definition: move.hpp:61
virtual arrow_ptr get_arrow()
Definition: move.hpp:77
@ ARROW_BRIGHTNESS_FOCUS
Definition: move.hpp:97
@ ARROW_BRIGHTNESS_STANDARD
Definition: move.hpp:97
@ ARROW_BRIGHTNESS_HIGHLIGHTED
Definition: move.hpp:97
virtual fake_unit_ptr get_fake_unit()
Definition: recall.hpp:68
virtual fake_unit_ptr get_fake_unit()
Definition: recruit.hpp:73
This internal whiteboard class holds the planned action queues for a team, and offers many utility me...
iterator find_last_action_of(const unit &unit, iterator start_position)
Finds the last action that belongs to this unit, starting the search backwards from the specified pos...
iterator begin()
Returns the iterator for the first (executed earlier) action within the actions queue.
container::iterator iterator
iterator end()
Returns the iterator for the position after the last executed action within the actions queue.
void remove()
Removes a tip.
Definition: tooltip.cpp:111
game_board * gameboard
Definition: resources.cpp:21
auto reversed_view(T &container)
Definition: ranges.hpp:28
Definition: display.hpp:49
std::shared_ptr< recruit > recruit_ptr
Definition: typedefs.hpp:72
std::weak_ptr< action > weak_action_ptr
Definition: typedefs.hpp:64
std::shared_ptr< move > move_ptr
Definition: typedefs.hpp:68
std::shared_ptr< action > action_ptr
Definition: typedefs.hpp:62
std::shared_ptr< attack > attack_ptr
Definition: typedefs.hpp:70
action_ptr find_action_at(map_location hex, team_filter team_filter)
Find the first action occurring on a given hex.
Definition: utility.cpp:203
std::shared_ptr< side_actions > side_actions_ptr
Definition: typedefs.hpp:66
std::deque< action_ptr > find_actions_of(const unit &target)
Find the actions of an unit.
Definition: utility.cpp:227
std::shared_ptr< recall > recall_ptr
Definition: typedefs.hpp:74
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
int main(int, char **argv)
Definition: sdl2.cpp:19
Encapsulates the map of the game.
Definition: location.hpp:38
bool valid() const
Definition: location.hpp:89
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:218
Ensures that the real unit map is active for the duration of the struct's life.
Definition: manager.hpp:284