The Battle for Wesnoth  1.19.7+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 #include <utility>
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"
33 #include "whiteboard/utility.hpp"
34 
35 #include "fake_unit_ptr.hpp"
36 #include "game_board.hpp"
37 #include "game_display.hpp"
38 #include "resources.hpp"
39 #include "units/unit.hpp"
41 #include "units/map.hpp"
42 #include "utils/general.hpp"
43 #include "utils/ranges.hpp"
44 
45 namespace wb
46 {
47 
49  : mouseover_hex_()
50  , exclusive_display_hexes_()
51  , owner_unit_()
52  , selection_candidate_()
53  , selected_action_()
54  , main_highlight_()
55  , secondary_highlights_()
56  , side_actions_(std::move(side_actions))
57 {
58 }
59 
61 {
62  try {
64  unhighlight();
65  }
66  } catch (...) {}
67 }
68 
70 {
71  clear();
72 
73  if(!hex.valid()) {
74  return;
75  }
76 
77  real_map ensure_real_map;
78  mouseover_hex_ = hex;
79  //if we're right over a unit, just highlight all of this unit's actions
81  if(it != get_unit_map().end()) {
83 
84  if(resources::gameboard->get_team(it->side()).get_side_actions()->unit_has_actions(*it)) {
86  }
87 
88  //commented code below is to also select the first action of this unit as
89  //the main highlight; it doesn't fit too well in the UI
90 // side_actions::iterator action_it = side_actions_->find_first_action_of(*it);
91 // if(action_it != side_actions_->end()) {
92 // main_highlight_ = *action_it;
93 // }
94  }
95 
96  //Set the execution/deletion/bump targets.
97  if(owner_unit_) {
98  side_actions::iterator itor = side_actions_->find_first_action_of(*owner_unit_);
99  if(itor != side_actions_->end()) {
100  selected_action_ = *itor;
101  }
102  }
103 
104  //Overwrite the above selected_action_ if we find a better one
105  if(side_actions_->empty()) {
106  return;
107  }
109  /**@todo "is_numbering_hex" is not the "correct" criterion by which to
110  * select the highlighted/selected action. It's just convenient for me
111  * to use at the moment since it happens to coincide with the "correct"
112  * criterion, which is to use find_main_highlight.*/
113  if(act->is_numbering_hex(hex)) {
114  selected_action_ = act;
115  break;
116  }
117  }
118 }
119 
121 {
122  unhighlight();
123  main_highlight_.reset();
124  owner_unit_.reset();
125  secondary_highlights_.clear();
126  selected_action_.reset();
127 }
128 
130 {
131  //Find main action to highlight if any, as well as owner unit
133 
134  if(action_ptr main = main_highlight_.lock()) {
135  //Highlight main highlight
136  highlight_main_visitor hm_visitor(*this);
137  main->accept(hm_visitor);
138  }
139 
140  if(owner_unit_) {
141  //Find secondary actions to highlight
143 
144  //Make sure owner unit is the only one displayed in its hex
146  exclusive_display_hexes_.insert(owner_unit_->get_location());
147 
148  if(!secondary_highlights_.empty()) {
149  //Highlight secondary highlights
150  highlight_secondary_visitor hs_visitor(*this);
152  if(action_ptr action = weak.lock()) {
153  action->accept(hs_visitor);
154  }
155  }
156  }
157  }
158 }
159 
161 {
162  unhighlight_visitor uh_visitor(*this);
163 
164  //unhighlight main highlight
165  if(action_ptr main = main_highlight_.lock()) {
166  main->accept(uh_visitor);
167  }
168 
169  //unhighlight secondary highlights
171  if(action_ptr action = weak.lock()) {
172  action->accept(uh_visitor);
173  }
174  }
175 
176  //unhide other units if needed
179  }
180  exclusive_display_hexes_.clear();
181 }
182 
184 {
185  //Last action with a fake unit always gets normal appearance
186  if(move->get_fake_unit()) {
187  side_actions& sa = *resources::gameboard->teams().at(move->team_index()).get_side_actions().get();
188 
190  side_actions::iterator second_to_last_action = last_action != sa.end() && last_action != sa.begin() ? last_action - 1 : sa.end();
191 
192  bool this_is_last_action = last_action != sa.end() && move == *last_action;
193  bool last_action_has_fake_unit = last_action != sa.end() && (*last_action)->get_fake_unit();
194  bool this_is_second_to_last_action = (second_to_last_action != sa.end() && move == *second_to_last_action);
195 
196  if(this_is_last_action || (this_is_second_to_last_action && !last_action_has_fake_unit)) {
197  move->get_fake_unit()->anim_comp().set_standing(true);
198  }
199  }
200 }
201 
203 {
204  // Even if we already found an owner_unit_ in the mouseover hex,
205  // action destination hexes usually take priority over that
206  assert(main_highlight_.expired());
207  //@todo re-enable the following assert once I find out what happends to
208  // viewing side assignments after victory
209  //assert(side_actions_->team_index() == display::get_singleton()->viewing_team());
210 
212  if(action_ptr main = main_highlight_.lock()) {
213  owner_unit_ = main->get_unit();
214  }
215 }
216 
218 {
219  assert(owner_unit_);
220  assert(secondary_highlights_.empty());
221 
222  if(owner_unit_ == nullptr) {
223  return;
224  }
225 
226  // List all the actions of owner_unit_
227  std::deque<action_ptr> actions = find_actions_of(*owner_unit_);
228 
229  // Remove main_highlight_ if present
231 
232  // Copy in secondary_highlights_
233  std::copy(actions.begin(), actions.end(), std::back_inserter(secondary_highlights_));
234 }
235 
236 
238 {
239  if(action_ptr locked = selected_action_.lock()) {
240  return *side_actions_->find_first_action_of(locked->get_unit_id());
241  } else {
242  return action_ptr();
243  }
244 }
246 {
247  if(action_ptr locked = selected_action_.lock()) {
248  return *side_actions_->find_last_action_of(locked->get_unit_id());
249  } else {
250  return action_ptr();
251  }
252 }
253 
255 {
256  return selected_action_.lock();
257 }
258 
260 {
261  if(owner_unit_) {
262  return owner_unit_;
263  } else {
264  return selection_candidate_;
265  }
266 }
267 
269 {
270  if(move->get_arrow()) {
272  }
273  if(move->get_fake_unit()) {
274  // TODO: find some highlight animation
275  move->get_fake_unit()->anim_comp().set_ghosted(true);
276  //Make sure the fake unit is the only one displayed in its hex
278  highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
279 
281  }
282 }
283 
285 {
286  // TODO: highlight the attack indicator
287  visit(std::static_pointer_cast<move>(attack));
288 }
289 
291 {
292  if(recruit->get_fake_unit()) {
293  // TODO: find some suitable effect for mouseover on planned recruit.
294 
295  //Make sure the fake unit is the only one displayed in its hex
297  highlighter_.exclusive_display_hexes_.insert(recruit->get_fake_unit()->get_location());
298  }
299 }
300 
302 {
303  if(move->get_arrow()) {
305  }
306  if(move->get_fake_unit()) {
307  move->get_fake_unit()->anim_comp().set_ghosted(true);
308  //Make sure the fake unit is the only one displayed in its hex
310  highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
311 
312  highlighter_.last_action_redraw(move);
313  }
314 }
315 
317 {
318  visit(std::static_pointer_cast<move>(attack));
319 }
320 
322 {
323  if(move->get_arrow()) {
325  }
326  if(move->get_fake_unit()) {
327  move->get_fake_unit()->anim_comp().set_disabled_ghosted(false);
328 
329  highlighter_.last_action_redraw(move);
330  }
331 }
332 
334 {
335  visit(std::static_pointer_cast<move>(attack));
336 }
337 
339 {
340  if(recall->get_fake_unit()) {
341  //@todo: find some suitable effect for mouseover on planned recall.
342 
343  //Make sure the fake unit is the only one displayed in its hex
345  highlighter_.exclusive_display_hexes_.insert(recall->get_fake_unit()->get_location());
346  }
347 }
349 {
350  assert(resources::gameboard);
351  return resources::gameboard->units();
352 }
353 
354 } // 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:60
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()
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:69
map_location mouseover_hex_
Definition: highlighter.hpp:82
void last_action_redraw(const move_ptr &)
Redraw the given move action when needed.
unit_ptr owner_unit_
Definition: highlighter.hpp:84
highlighter(side_actions_ptr side_actions)
Definition: highlighter.cpp:48
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:117
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, const 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