
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
354 lines
9.8 KiB
C++
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
|