The Battle for Wesnoth  1.19.0-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/ranges.hpp"
42 
43 namespace wb
44 {
45 
47  : mouseover_hex_()
48  , exclusive_display_hexes_()
49  , owner_unit_()
50  , selection_candidate_()
51  , selected_action_()
52  , main_highlight_()
53  , secondary_highlights_()
54  , side_actions_(side_actions)
55 {
56 }
57 
59 {
60  try {
62  unhighlight();
63  }
64  } catch (...) {}
65 }
66 
68 {
69  clear();
70 
71  if(!hex.valid()) {
72  return;
73  }
74 
75  real_map ensure_real_map;
76  mouseover_hex_ = hex;
77  //if we're right over a unit, just highlight all of this unit's actions
79  if(it != get_unit_map().end()) {
81 
82  if(resources::gameboard->get_team(it->side()).get_side_actions()->unit_has_actions(*it)) {
84  }
85 
86  //commented code below is to also select the first action of this unit as
87  //the main highlight; it doesn't fit too well in the UI
88 // side_actions::iterator action_it = side_actions_->find_first_action_of(*it);
89 // if(action_it != side_actions_->end()) {
90 // main_highlight_ = *action_it;
91 // }
92  }
93 
94  //Set the execution/deletion/bump targets.
95  if(owner_unit_) {
96  side_actions::iterator itor = side_actions_->find_first_action_of(*owner_unit_);
97  if(itor != side_actions_->end()) {
98  selected_action_ = *itor;
99  }
100  }
101 
102  //Overwrite the above selected_action_ if we find a better one
103  if(side_actions_->empty()) {
104  return;
105  }
107  /**@todo "is_numbering_hex" is not the "correct" criterion by which to
108  * select the highlighted/selected action. It's just convenient for me
109  * to use at the moment since it happens to coincide with the "correct"
110  * criterion, which is to use find_main_highlight.*/
111  if(act->is_numbering_hex(hex)) {
112  selected_action_ = act;
113  break;
114  }
115  }
116 }
117 
119 {
120  unhighlight();
121  main_highlight_.reset();
122  owner_unit_.reset();
123  secondary_highlights_.clear();
124  selected_action_.reset();
125 }
126 
128 {
129  //Find main action to highlight if any, as well as owner unit
131 
132  if(action_ptr main = main_highlight_.lock()) {
133  //Highlight main highlight
134  highlight_main_visitor hm_visitor(*this);
135  main->accept(hm_visitor);
136  }
137 
138  if(owner_unit_) {
139  //Find secondary actions to highlight
141 
142  //Make sure owner unit is the only one displayed in its hex
144  exclusive_display_hexes_.insert(owner_unit_->get_location());
145 
146  if(!secondary_highlights_.empty()) {
147  //Highlight secondary highlights
148  highlight_secondary_visitor hs_visitor(*this);
150  if(action_ptr action = weak.lock()) {
151  action->accept(hs_visitor);
152  }
153  }
154  }
155  }
156 }
157 
159 {
160  unhighlight_visitor uh_visitor(*this);
161 
162  //unhighlight main highlight
163  if(action_ptr main = main_highlight_.lock()) {
164  main->accept(uh_visitor);
165  }
166 
167  //unhighlight secondary highlights
169  if(action_ptr action = weak.lock()) {
170  action->accept(uh_visitor);
171  }
172  }
173 
174  //unhide other units if needed
177  }
178  exclusive_display_hexes_.clear();
179 }
180 
182 {
183  //Last action with a fake unit always gets normal appearance
184  if(move->get_fake_unit()) {
185  side_actions& sa = *resources::gameboard->teams().at(move->team_index()).get_side_actions().get();
186 
188  side_actions::iterator second_to_last_action = last_action != sa.end() && last_action != sa.begin() ? last_action - 1 : sa.end();
189 
190  bool this_is_last_action = last_action != sa.end() && move == *last_action;
191  bool last_action_has_fake_unit = last_action != sa.end() && (*last_action)->get_fake_unit();
192  bool this_is_second_to_last_action = (second_to_last_action != sa.end() && move == *second_to_last_action);
193 
194  if(this_is_last_action || (this_is_second_to_last_action && !last_action_has_fake_unit)) {
195  move->get_fake_unit()->anim_comp().set_standing(true);
196  }
197  }
198 }
199 
201 {
202  // Even if we already found an owner_unit_ in the mouseover hex,
203  // action destination hexes usually take priority over that
204  assert(main_highlight_.expired());
205  //@todo re-enable the following assert once I find out what happends to
206  // viewing side assignments after victory
207  //assert(side_actions_->team_index() == display::get_singleton()->viewing_team());
208 
210  if(action_ptr main = main_highlight_.lock()) {
211  owner_unit_ = main->get_unit();
212  }
213 }
214 
216 {
217  assert(owner_unit_);
218  assert(secondary_highlights_.empty());
219 
220  if(owner_unit_ == nullptr) {
221  return;
222  }
223 
224  // List all the actions of owner_unit_
225  std::deque<action_ptr> actions = find_actions_of(*owner_unit_);
226 
227  // Remove main_highlight_ if present
228  actions.erase(std::remove(actions.begin(), actions.end(), main_highlight_.lock()), actions.end());
229 
230  // Copy in secondary_highlights_
231  std::copy(actions.begin(), actions.end(), std::back_inserter(secondary_highlights_));
232 }
233 
234 
236 {
237  if(action_ptr locked = selected_action_.lock()) {
238  return *side_actions_->find_first_action_of(locked->get_unit_id());
239  } else {
240  return action_ptr();
241  }
242 }
244 {
245  if(action_ptr locked = selected_action_.lock()) {
246  return *side_actions_->find_last_action_of(locked->get_unit_id());
247  } else {
248  return action_ptr();
249  }
250 }
251 
253 {
254  return selected_action_.lock();
255 }
256 
258 {
259  if(owner_unit_) {
260  return owner_unit_;
261  } else {
262  return selection_candidate_;
263  }
264 }
265 
267 {
268  if(move->get_arrow()) {
270  }
271  if(move->get_fake_unit()) {
272  // TODO: find some highlight animation
273  move->get_fake_unit()->anim_comp().set_ghosted(true);
274  //Make sure the fake unit is the only one displayed in its hex
276  highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
277 
279  }
280 }
281 
283 {
284  // TODO: highlight the attack indicator
285  visit(std::static_pointer_cast<move>(attack));
286 }
287 
289 {
290  if(recruit->get_fake_unit()) {
291  // TODO: find some suitable effect for mouseover on planned recruit.
292 
293  //Make sure the fake unit is the only one displayed in its hex
295  highlighter_.exclusive_display_hexes_.insert(recruit->get_fake_unit()->get_location());
296  }
297 }
298 
300 {
301  if(move->get_arrow()) {
303  }
304  if(move->get_fake_unit()) {
305  move->get_fake_unit()->anim_comp().set_ghosted(true);
306  //Make sure the fake unit is the only one displayed in its hex
308  highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
309 
310  highlighter_.last_action_redraw(move);
311  }
312 }
313 
315 {
316  visit(std::static_pointer_cast<move>(attack));
317 }
318 
320 {
321  if(move->get_arrow()) {
323  }
324  if(move->get_fake_unit()) {
325  move->get_fake_unit()->anim_comp().set_disabled_ghosted(false);
326 
327  highlighter_.last_action_redraw(move);
328  }
329 }
330 
332 {
333  visit(std::static_pointer_cast<move>(attack));
334 }
335 
337 {
338  if(recall->get_fake_unit()) {
339  //@todo: find some suitable effect for mouseover on planned recall.
340 
341  //Make sure the fake unit is the only one displayed in its hex
343  highlighter_.exclusive_display_hexes_.insert(recall->get_fake_unit()->get_location());
344  }
345 }
347 {
348  assert(resources::gameboard);
349  return resources::gameboard->units();
350 }
351 
352 } // end namespace wb
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:377
std::string remove_exclusive_draw(const map_location &loc)
Cancels an exclusive draw request.
Definition: display.cpp:387
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:102
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:79
virtual const unit_map & units() const override
Definition: game_board.hpp:106
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:58
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:67
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:46
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:109
game_board * gameboard
Definition: resources.cpp:20
auto reversed_view(T &container)
Definition: ranges.hpp:28
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:200
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:224
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:25
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:217
Ensures that the real unit map is active for the duration of the struct's life.
Definition: manager.hpp:284