wesnoth/src/whiteboard/highlighter.cpp
Chris Beck 504e4932b5 split off animation fcs from unit to unit_animation_component
This is a strict refactor, all we do is move the functions and
variables used just for animations to "unit_animation_component",
and include the necessary headers appropriate.

With a bit more work we can probably remove the graphics related
headers from unit.hpp
2014-06-17 02:18:46 -04:00

354 lines
9.8 KiB
C++

/*
Copyright (C) 2010 - 2014 by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
*/
#include <algorithm>
#include <iterator>
#include <boost/bind.hpp>
#include "highlighter.hpp"
#include "action.hpp"
#include "attack.hpp"
#include "manager.hpp"
#include "move.hpp"
#include "recall.hpp"
#include "recruit.hpp"
#include "side_actions.hpp"
#include "suppose_dead.hpp"
#include "utility.hpp"
#include "arrow.hpp"
#include "config.hpp"
#include "fake_unit.hpp"
#include "game_board.hpp"
#include "game_display.hpp"
#include "game_errors.hpp"
#include "play_controller.hpp"
#include "resources.hpp"
#include "unit.hpp"
#include "unit_animation_component.hpp"
#include "unit_map.hpp"
#include <boost/foreach.hpp>
namespace wb
{
highlighter::highlighter(unit_map& unit_map, side_actions_ptr side_actions)
: unit_map_(unit_map)
, mouseover_hex_()
, exclusive_display_hexes_()
, owner_unit_()
, selection_candidate_()
, selected_action_()
, main_highlight_()
, secondary_highlights_()
, side_actions_(side_actions)
{
}
highlighter::~highlighter()
{
try {
if(resources::screen && owner_unit_) {
unhighlight();
}
} catch (...) {}
}
void highlighter::set_mouseover_hex(const map_location& hex)
{
clear();
if(!hex.valid()) {
return;
}
real_map ensure_real_map;
mouseover_hex_ = hex;
//if we're right over a unit, just highlight all of this unit's actions
unit_map::iterator it = unit_map_.find(hex);
if(it != unit_map_.end()) {
selection_candidate_ = it.get_shared_ptr();
if(resources::teams->at(it->side()-1).get_side_actions()->unit_has_actions(*it)) {
owner_unit_ = it.get_shared_ptr();
}
//commented code below is to also select the first action of this unit as
//the main highlight; it doesn't fit too well in the UI
// side_actions::iterator action_it = side_actions_->find_first_action_of(*it);
// if(action_it != side_actions_->end()) {
// main_highlight_ = *action_it;
// }
}
//Set the execution/deletion/bump targets.
if(owner_unit_) {
side_actions::iterator itor = side_actions_->find_first_action_of(*owner_unit_);
if(itor != side_actions_->end()) {
selected_action_ = *itor;
}
}
//Overwrite the above selected_action_ if we find a better one
if(side_actions_->empty()) {
return;
}
BOOST_REVERSE_FOREACH(action_ptr act, *side_actions_) {
/**@todo "is_numbering_hex" is not the "correct" criterion by which to
* select the hightlighted/selected action. It's just convenient for me
* to use at the moment since it happens to coincide with the "correct"
* criterion, which is to use find_main_highlight.*/
if(act->is_numbering_hex(hex)) {
selected_action_ = act;
break;
}
}
}
void highlighter::clear()
{
unhighlight();
main_highlight_.reset();
owner_unit_.reset();
secondary_highlights_.clear();
selected_action_.reset();
}
void highlighter::highlight()
{
//Find main action to highlight if any, as well as owner unit
find_main_highlight();
if(action_ptr main = main_highlight_.lock()) {
//Highlight main highlight
highlight_main_visitor hm_visitor(*this);
main->accept(hm_visitor);
}
if(owner_unit_) {
//Find secondary actions to highlight
find_secondary_highlights();
//Make sure owner unit is the only one displayed in its hex
resources::screen->add_exclusive_draw(owner_unit_->get_location(), *owner_unit_);
exclusive_display_hexes_.insert(owner_unit_->get_location());
if(!secondary_highlights_.empty()) {
//Highlight secondary highlights
highlight_secondary_visitor hs_visitor(*this);
BOOST_FOREACH(weak_action_ptr weak, secondary_highlights_) {
if(action_ptr action = weak.lock()) {
action->accept(hs_visitor);
}
}
}
}
}
void highlighter::unhighlight()
{
unhighlight_visitor uh_visitor(*this);
//unhighlight main highlight
if(action_ptr main = main_highlight_.lock()) {
main->accept(uh_visitor);
}
//unhighlight secondary highlights
BOOST_FOREACH(weak_action_ptr weak, secondary_highlights_) {
if(action_ptr action = weak.lock()) {
action->accept(uh_visitor);
}
}
//unhide other units if needed
BOOST_FOREACH(map_location hex, exclusive_display_hexes_) {
resources::screen->remove_exclusive_draw(hex);
}
exclusive_display_hexes_.clear();
}
void highlighter::last_action_redraw(move_ptr move)
{
//Last action with a fake unit always gets normal appearance
if(move->get_fake_unit()) {
side_actions& sa = *resources::teams->at(move->team_index()).get_side_actions();
side_actions::iterator last_action = sa.find_last_action_of(*(move->get_unit()));
side_actions::iterator second_to_last_action = last_action != sa.end() && last_action != sa.begin() ? last_action - 1 : sa.end();
bool this_is_last_action = last_action != sa.end() && move == *last_action;
bool last_action_has_fake_unit = last_action != sa.end() && (*last_action)->get_fake_unit();
bool this_is_second_to_last_action = (second_to_last_action != sa.end() && move == *second_to_last_action);
if(this_is_last_action || (this_is_second_to_last_action && !last_action_has_fake_unit)) {
move->get_fake_unit()->anim_comp().set_standing(true);
}
}
}
void highlighter::find_main_highlight()
{
// Even if we already found an owner_unit_ in the mouseover hex,
// action destination hexes usually take priority over that
assert(main_highlight_.expired());
//@todo re-enable the following assert once I find out what happends to
// viewing side assignments after victory
//assert(side_actions_->team_index() == resources::screen->viewing_team());
main_highlight_ = find_action_at(mouseover_hex_);
if(action_ptr main = main_highlight_.lock()) {
owner_unit_ = main->get_unit();
}
}
void highlighter::find_secondary_highlights()
{
assert(owner_unit_);
assert(secondary_highlights_.empty());
if(owner_unit_ == NULL) {
return;
}
// List all the actions of owner_unit_
std::deque<action_ptr> actions = find_actions_of(*owner_unit_);
// Remove main_highlight_ if present
actions.erase(std::remove(actions.begin(), actions.end(), main_highlight_.lock()), actions.end());
// Copy in secondary_highlights_
std::copy(actions.begin(), actions.end(), std::back_inserter(secondary_highlights_));
}
action_ptr highlighter::get_execute_target()
{
if(action_ptr locked = selected_action_.lock()) {
return *side_actions_->find_first_action_of(*(locked->get_unit()));
} else {
return action_ptr();
}
}
action_ptr highlighter::get_delete_target()
{
if(action_ptr locked = selected_action_.lock()) {
return *side_actions_->find_last_action_of(*(locked->get_unit()));
} else {
return action_ptr();
}
}
action_ptr highlighter::get_bump_target()
{
return selected_action_.lock();
}
UnitPtr highlighter::get_selection_target()
{
if(owner_unit_) {
return owner_unit_;
} else {
return selection_candidate_;
}
}
void highlighter::highlight_main_visitor::visit(move_ptr move)
{
if(move->get_arrow()) {
move->set_arrow_brightness(move::ARROW_BRIGHTNESS_FOCUS);
}
if(move->get_fake_unit()) {
///@todo find some highlight animation
move->get_fake_unit()->anim_comp().set_ghosted(true);
//Make sure the fake unit is the only one displayed in its hex
resources::screen->add_exclusive_draw(move->get_fake_unit()->get_location(), *move->get_fake_unit());
highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
highlighter_.last_action_redraw(move);
}
}
void highlighter::highlight_main_visitor::visit(attack_ptr attack)
{
///@todo: highlight the attack indicator
visit(boost::static_pointer_cast<move>(attack));
}
void highlighter::highlight_main_visitor::visit(recruit_ptr recruit)
{
if(recruit->get_fake_unit()) {
///@todo: find some suitable effect for mouseover on planned recruit.
//Make sure the fake unit is the only one displayed in its hex
resources::screen->add_exclusive_draw(recruit->get_fake_unit()->get_location(), *recruit->get_fake_unit());
highlighter_.exclusive_display_hexes_.insert(recruit->get_fake_unit()->get_location());
}
}
void highlighter::highlight_secondary_visitor::visit(move_ptr move)
{
if(move->get_arrow()) {
move->set_arrow_brightness(move::ARROW_BRIGHTNESS_HIGHLIGHTED);
}
if(move->get_fake_unit()) {
move->get_fake_unit()->anim_comp().set_ghosted(true);
//Make sure the fake unit is the only one displayed in its hex
resources::screen->add_exclusive_draw(move->get_fake_unit()->get_location(), *move->get_fake_unit());
highlighter_.exclusive_display_hexes_.insert(move->get_fake_unit()->get_location());
highlighter_.last_action_redraw(move);
}
}
void highlighter::highlight_secondary_visitor::visit(attack_ptr attack)
{
visit(boost::static_pointer_cast<move>(attack));
}
void highlighter::unhighlight_visitor::visit(move_ptr move)
{
if(move->get_arrow()) {
move->set_arrow_brightness(move::ARROW_BRIGHTNESS_STANDARD);
}
if(move->get_fake_unit()) {
move->get_fake_unit()->anim_comp().set_disabled_ghosted(false);
highlighter_.last_action_redraw(move);
}
}
void highlighter::unhighlight_visitor::visit(attack_ptr attack)
{
visit(boost::static_pointer_cast<move>(attack));
}
void highlighter::unhighlight_visitor::visit(recall_ptr recall)
{
if(recall->get_fake_unit()) {
//@todo: find some suitable effect for mouseover on planned recall.
//Make sure the fake unit is the only one displayed in its hex
resources::screen->add_exclusive_draw(recall->get_fake_unit()->get_location(), *recall->get_fake_unit());
highlighter_.exclusive_display_hexes_.insert(recall->get_fake_unit()->get_location());
}
}
} // end namespace wb