The Battle for Wesnoth  1.19.5+dev
highlighter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2024
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 
24 
25 #include "whiteboard/action.hpp"
26 #include "whiteboard/attack.hpp"
27 #include "whiteboard/manager.hpp"
28 #include "whiteboard/move.hpp"
29 #include "whiteboard/recall.hpp"
30 #include "whiteboard/recruit.hpp"
32 #include "whiteboard/utility.hpp"
33 
34 #include "fake_unit_ptr.hpp"
35 #include "game_board.hpp"
36 #include "game_display.hpp"
37 #include "resources.hpp"
38 #include "units/unit.hpp"
40 #include "units/map.hpp"
41 #include "utils/general.hpp"
42 #include "utils/ranges.hpp"
43 
44 namespace wb
45 {
46 
48  : mouseover_hex_()
49  , exclusive_display_hexes_()
50  , owner_unit_()
51  , selection_candidate_()
52  , selected_action_()
53  , main_highlight_()
54  , secondary_highlights_()
55  , side_actions_(side_actions)
56 {
57 }
58 
60 {
61  try {
63  unhighlight();
64  }
65  } catch (...) {}
66 }
67 
69 {
70  clear();
71 
72  if(!hex.valid()) {
73  return;
74  }
75 
76  real_map ensure_real_map;
77  mouseover_hex_ = hex;
78  //if we're right over a unit, just highlight all of this unit's actions
80  if(it != get_unit_map().end()) {
82 
83  if(resources::gameboard->get_team(it->side()).get_side_actions()->unit_has_actions(*it)) {
85  }
86 
87  //commented code below is to also select the first action of this unit as
88  //the main highlight; it doesn't fit too well in the UI
89 // side_actions::iterator action_it = side_actions_->find_first_action_of(*it);
90 // if(action_it != side_actions_->end()) {
91 // main_highlight_ = *action_it;
92 // }
93  }
94 
95  //Set the execution/deletion/bump targets.
96  if(owner_unit_) {
97  side_actions::iterator itor = side_actions_->find_first_action_of(*owner_unit_);
98  if(itor != side_actions_->end()) {
99  selected_action_ = *itor;
100  }
101  }
102 
103  //Overwrite the above selected_action_ if we find a better one
104  if(side_actions_->empty()) {
105  return;
106  }
108  /**@todo "is_numbering_hex" is not the "correct" criterion by which to
109  * select the highlighted/selected action. It's just convenient for me
110  * to use at the moment since it happens to coincide with the "correct"
111  * criterion, which is to use find_main_highlight.*/
112  if(act->is_numbering_hex(hex)) {
113  selected_action_ = act;
114  break;
115  }
116  }
117 }
118 
120 {
121  unhighlight();
122  main_highlight_.reset();
123  owner_unit_.reset();
124  secondary_highlights_.clear();
125  selected_action_.reset();
126 }
127 
129 {
130  //Find main action to highlight if any, as well as owner unit
132 
133  if(action_ptr main = main_highlight_.lock()) {
134  //Highlight main highlight
135  highlight_main_visitor hm_visitor(*this);
136  main->accept(hm_visitor);
137  }
138 
139  if(owner_unit_) {
140  //Find secondary actions to highlight
142 
143  //Make sure owner unit is the only one displayed in its hex
145  exclusive_display_hexes_.insert(owner_unit_->get_location());
146 
147  if(!secondary_highlights_.empty()) {
148  //Highlight secondary highlights
149  highlight_secondary_visitor hs_visitor(*this);
151  if(action_ptr action = weak.lock()) {
152  action->accept(hs_visitor);
153  }
154  }
155  }
156  }
157 }
158 
160 {
161  unhighlight_visitor uh_visitor(*this);
162 
163  //unhighlight main highlight
164  if(action_ptr main = main_highlight_.lock()) {
165  main->accept(uh_visitor);
166  }
167 
168  //unhighlight secondary highlights
170  if(action_ptr action = weak.lock()) {
171  action->accept(uh_visitor);
172  }
173  }
174 
175  //unhide other units if needed
178  }
179  exclusive_display_hexes_.clear();
180 }
181 
183 {
184  //Last action with a fake unit always gets normal appearance
185  if(move->get_fake_unit()) {
186  side_actions& sa = *resources::gameboard->teams().at(move->team_index()).get_side_actions().get();
187 
189  side_actions::iterator second_to_last_action = last_action != sa.end() && last_action != sa.begin() ? last_action - 1 : sa.end();
190 
191  bool this_is_last_action = last_action != sa.end() && move == *last_action;
192  bool last_action_has_fake_unit = last_action != sa.end() && (*last_action)->get_fake_unit();
193  bool this_is_second_to_last_action = (second_to_last_action != sa.end() && move == *second_to_last_action);
194 
195  if(this_is_last_action || (this_is_second_to_last_action && !last_action_has_fake_unit)) {
196  move->get_fake_unit()->anim_comp().set_standing(true);
197  }
198  }
199 }
200 
202 {
203  // Even if we already found an owner_unit_ in the mouseover hex,
204  // action destination hexes usually take priority over that
205  assert(main_highlight_.expired());
206  //@todo re-enable the following assert once I find out what happends to
207  // viewing side assignments after victory
208  //assert(side_actions_->team_index() == display::get_singleton()->viewing_team());
209 
211  if(action_ptr main = main_highlight_.lock()) {
212  owner_unit_ = main->get_unit();
213  }
214 }
215 
217 {
218  assert(owner_unit_);
219  assert(secondary_highlights_.empty());
220 
221  if(owner_unit_ == nullptr) {
222  return;
223  }
224 
225  // List all the actions of owner_unit_
226  std::deque<action_ptr> actions = find_actions_of(*owner_unit_);
227 
228  // Remove main_highlight_ if present
230 
231  // Copy in secondary_highlights_
232  std::copy(actions.begin(), actions.end(), std::back_inserter(secondary_highlights_));
233 }
234 
235 
237 {
238  if(action_ptr locked = selected_action_.lock()) {
239  return *side_actions_->find_first_action_of(locked->get_unit_id());
240  } else {
241  return action_ptr();
242  }
243 }
245 {
246  if(action_ptr locked = selected_action_.lock()) {
247  return *side_actions_->find_last_action_of(locked->get_unit_id());
248  } else {
249  return action_ptr();
250  }
251 }
252 
254 {
255  return selected_action_.lock();
256 }
257 
259 {
260  if(owner_unit_) {
261  return owner_unit_;
262  } else {
263  return selection_candidate_;
264  }
265 }
266 
268 {
269  if(move->get_arrow()) {
271  }
272  if(move->get_fake_unit()) {
273  // TODO: find some highlight animation
274  move->get_fake_unit()->anim_comp().set_ghosted(true);
275  //Make sure the fake unit is the only one displayed in its hex
277  highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
278 
280  }
281 }
282 
284 {
285  // TODO: highlight the attack indicator
286  visit(std::static_pointer_cast<move>(attack));
287 }
288 
290 {
291  if(recruit->get_fake_unit()) {
292  // TODO: find some suitable effect for mouseover on planned recruit.
293 
294  //Make sure the fake unit is the only one displayed in its hex
296  highlighter_.exclusive_display_hexes_.insert(recruit->get_fake_unit()->get_location());
297  }
298 }
299 
301 {
302  if(move->get_arrow()) {
304  }
305  if(move->get_fake_unit()) {
306  move->get_fake_unit()->anim_comp().set_ghosted(true);
307  //Make sure the fake unit is the only one displayed in its hex
309  highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
310 
311  highlighter_.last_action_redraw(move);
312  }
313 }
314 
316 {
317  visit(std::static_pointer_cast<move>(attack));
318 }
319 
321 {
322  if(move->get_arrow()) {
324  }
325  if(move->get_fake_unit()) {
326  move->get_fake_unit()->anim_comp().set_disabled_ghosted(false);
327 
328  highlighter_.last_action_redraw(move);
329  }
330 }
331 
333 {
334  visit(std::static_pointer_cast<move>(attack));
335 }
336 
338 {
339  if(recall->get_fake_unit()) {
340  //@todo: find some suitable effect for mouseover on planned recall.
341 
342  //Make sure the fake unit is the only one displayed in its hex
344  highlighter_.exclusive_display_hexes_.insert(recall->get_fake_unit()->get_location());
345  }
346 }
348 {
349  assert(resources::gameboard);
350  return resources::gameboard->units();
351 }
352 
353 } // end namespace wb
bool add_exclusive_draw(const map_location &loc, const unit &unit)
Allows a unit to request to be the only one drawn in its hex.
Definition: display.cpp:371
std::string remove_exclusive_draw(const map_location &loc)
Cancels an exclusive draw request.
Definition: display.cpp:378
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:111
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
virtual const unit_map & units() const override
Definition: game_board.hpp:107
static game_display * get_singleton()
Container associating units to locations.
Definition: map.hpp:98
unit_iterator find(std::size_t id)
Definition: map.cpp:302
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:59
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:68
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:47
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.
game_board * gameboard
Definition: resources.cpp:20
auto reversed_view(T &container)
Definition: ranges.hpp:26
std::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
Definition: general.hpp:111
Definition: display.hpp:45
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:193
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:217
std::shared_ptr< recall > recall_ptr
Definition: typedefs.hpp:74
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
int main(int, char **)
Definition: sdl2.cpp:25
Encapsulates the map of the game.
Definition: location.hpp:45
bool valid() const
Definition: location.hpp:110
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:217
Ensures that the real unit map is active for the duration of the struct's life.
Definition: manager.hpp:284