wesnoth/src/whiteboard/manager.cpp
2010-07-22 05:19:25 +00:00

550 lines
13 KiB
C++

/* $Id$ */
/*
Copyright (C) 2010 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 version 2
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 manager.cpp
*/
#include "manager.hpp"
#include "action.hpp"
#include "highlight_visitor.hpp"
#include "mapbuilder_visitor.hpp"
#include "move.hpp"
#include "side_actions.hpp"
#include "arrow.hpp"
#include "foreach.hpp"
#include "pathfind/pathfind.hpp"
#include "play_controller.hpp"
#include "resources.hpp"
#include "team.hpp"
#include "unit_display.hpp"
namespace wb {
manager::manager():
active_(false),
inverted_behavior_(false),
wait_for_side_init_(true),
mapbuilder_(),
highlighter_(),
route_(),
move_arrow_(),
fake_unit_(),
planned_unit_map_active_(false),
executing_actions_(false)
{
LOG_WB << "Manager initialized.\n";
highlighter_.reset(new highlight_visitor(*resources::units, viewer_actions()));
}
manager::~manager()
{
}
void manager::set_active(bool active)
{
if(is_observer())
{
active_ = false;
LOG_WB << "Whiteboard can't be activated by observers.\n";
}
else if (active != active_)
{
active_ = active;
erase_temp_move();
if (active_)
{
validate_viewer_actions();
LOG_WB << "Whiteboard activated! " << *viewer_actions() << "\n";
create_temp_move();
} else {
LOG_WB << "Whiteboard deactivated!\n";
}
}
}
void manager::set_invert_behavior(bool invert)
{
if(wait_for_side_init_
|| executing_actions_
|| is_observer())
return;
log_scope("set_invert_behavior");
if (invert)
{
if (!inverted_behavior_)
{
inverted_behavior_ = true;
if (active_)
{
DBG_WB << "Whiteboard deactivated temporarily.\n";
set_active(false);
}
else // active_ == false
{
DBG_WB << "Whiteboard activated temporarily.\n";
set_active(true);
}
}
}
else
{
if (inverted_behavior_)
{
inverted_behavior_ = false;
if (active_)
{
DBG_WB << "Whiteboard set back to deactivated status.\n";
set_active(false);
}
else // active_ == false
{
DBG_WB << "Whiteboard set back to activated status.\n";
set_active(true);
}
}
}
}
bool manager::can_execute_hotkey() const
{
return !viewer_actions()->empty();
}
void manager::on_init_side()
{
validate_viewer_actions();
highlighter_.reset(new highlight_visitor(*resources::units, viewer_actions()));
wait_for_side_init_ = false;
LOG_WB << "on_init_side()\n";
}
void manager::on_finish_side_turn()
{
wait_for_side_init_ = true;
highlighter_.reset();
erase_temp_move();
LOG_WB << "on_finish_side_turn()\n";
}
side_actions_ptr manager::viewer_actions() const
{
side_actions_ptr side_actions =
(*resources::teams)[resources::screen->viewing_team()].get_side_actions();
return side_actions;
}
side_actions_ptr manager::current_side_actions() const
{
side_actions_ptr side_actions =
(*resources::teams)[resources::controller->current_side() - 1].get_side_actions();
return side_actions;
}
bool manager::current_side_has_actions() const
{
return !current_side_actions()->empty();
}
void manager::validate_viewer_actions()
{
assert(!executing_actions_);
viewer_actions()->validate_actions();
}
void manager::set_planned_unit_map()
{
if (!executing_actions_ && !wait_for_side_init_)
{
if (!planned_unit_map_active_)
{
DBG_WB << "Building planned unit map.\n";
mapbuilder_.reset(new mapbuilder_visitor(*resources::units, viewer_actions()));
mapbuilder_->build_map();
planned_unit_map_active_ = true;
}
else
{
WRN_WB << "Attempt to set planned unit map when it was already set.\n";
}
}
else if (executing_actions_)
{
LOG_WB << "Attempt to set planned_unit_map during action execution.\n";
}
}
void manager::set_real_unit_map()
{
if (!executing_actions_)
{
if (planned_unit_map_active_)
{
DBG_WB << "Restoring regular unit map.\n";
mapbuilder_.reset();
planned_unit_map_active_ = false;
}
else
{
DBG_WB << "Attempt to disable the planned unit map, when it was already disabled.\n";
}
}
else //executing_actions_
{
LOG_WB << "Attempt to set planned_unit_map during action execution.\n";
}
}
unit* manager::find_unit_future(map_location hex)
{
scoped_planned_unit_map planned_unit_map;
assert(has_planned_unit_map());
unit_map::iterator it;
if ((it = resources::units->find(hex)) != resources::units->end())
return &*it;
else
return NULL;
}
unit* manager::find_selected_future()
{
return find_unit_future(resources::screen->selected_hex());
}
unit* manager::find_selected_actor_future()
{
unit* unit = find_selected_future();
if (unit && unit->side() == resources::screen->viewing_side())
return unit;
else
return NULL;
}
void manager::draw_hex(const map_location& hex)
{
if (!wait_for_side_init_)
viewer_actions()->draw_hex(hex);
}
void manager::on_mouseover_change(const map_location& hex)
{
//FIXME: Detect if a WML event is executing, and if so, avoid modifying the unit map during that time.
// Acting otherwise causes a crash.
if (!resources::screen->selected_hex().valid() && highlighter_)
{
highlighter_->set_mouseover_hex(hex);
highlighter_->highlight();
}
}
void manager::create_temp_move()
{
route_.reset();
if (!active_) return;
/*
* CHECK PRE-CONDITIONS
* (This section has multiple return paths.)
*/
pathfind::marked_route const& route =
resources::controller->get_mouse_handler_base().get_current_route();
if (route.steps.empty() || route.steps.size() < 2) return;
unit const* selected_unit = this->find_selected_actor_future();
if (!selected_unit) return;
if (selected_unit->side() != resources::screen->viewing_side()) return;
//FIXME: Temporary: Don't draw move arrow if move goes beyond range.
bool cancel = false;
foreach (const map_location& hex, route.steps)
{
if (cancel)
{
erase_temp_move();
return;
}
pathfind::marked_route::mark_map::const_iterator w =
route.marks.find(hex);
//We only accept an end-of-first-turn or a capture mark if this is the move's last hex.
if (w != route.marks.end() && (w->second.turns == 1
|| w->second.capture))
{
cancel = true;
}
}
/*
* DONE CHECKING PRE-CONDITIONS, CREATE THE TEMP MOVE
* (This section has only one return path.)
*/
//TODO: May be appropriate to replace these separate components by a temporary
// wb::move object
route_.reset(new pathfind::marked_route(route));
//NOTE: route_.steps.back() = dst, and route_.steps.front() = src
if (!move_arrow_)
{
// Create temp arrow
move_arrow_.reset(new arrow());
move_arrow_->set_color(team::get_side_color_index(
resources::screen->viewing_side()));
move_arrow_->set_alpha(move::ALPHA_HIGHLIGHT);
resources::screen->add_arrow(*move_arrow_);
}
if (!fake_unit_)
{
// Create temp ghost unit
fake_unit_.reset(new unit(*selected_unit),
wb::manager::fake_unit_deleter());
resources::screen->place_temporary_unit(fake_unit_.get());
fake_unit_->set_ghosted(false);
}
move_arrow_->set_path(route_->steps);
unit_display::move_unit(route_->steps, *fake_unit_, *resources::teams,
false); //get facing right
fake_unit_->set_location(route_->steps.back());
fake_unit_->set_ghosted(false);
}
void manager::erase_temp_move()
{
if (move_arrow_)
{
move_arrow_.reset(); //auto-removes itself from display
}
if (fake_unit_)
{
fake_unit_.reset(); //auto-removes itself from display thanks to custom deleter in the shared_ptr
}
if (route_)
{
route_.reset();
}
}
void manager::save_temp_move()
{
if (has_temp_move() && !executing_actions_)
{
scoped_planned_unit_map planned_unit_map;
arrow_ptr move_arrow;
fake_unit_ptr fake_unit;
move_arrow = arrow_ptr(move_arrow_);
fake_unit = fake_unit_ptr(fake_unit_);
fake_unit->set_disabled_ghosted(false);
viewer_actions()->queue_move(*route_, move_arrow, fake_unit);
erase_temp_move();
LOG_WB << *viewer_actions() << "\n";
}
}
void manager::save_temp_attack(const map_location& attack_from, const map_location& target_hex)
{
if (active_ && !executing_actions_)
{
arrow_ptr move_arrow;
fake_unit_ptr fake_unit;
map_location source_hex;
if (route_ && !route_->steps.empty())
{
move_arrow = arrow_ptr(move_arrow_);
fake_unit = fake_unit_ptr(fake_unit_);
assert(route_->steps.back() == attack_from);
source_hex = route_->steps.front();
fake_unit->set_disabled_ghosted(false);
}
else
{
move_arrow.reset(new arrow);
source_hex = attack_from;
route_.reset(new pathfind::marked_route);
// We'll pass as parameter a one-hex route with no marks.
route_->steps.push_back(attack_from);
}
unit* attacking_unit = find_unit_future(source_hex);
assert(attacking_unit);
int weapon_choice = resources::controller->get_mouse_handler_base().show_attack_dialog(
attacking_unit->get_location(), target_hex);
if (weapon_choice >= 0)
{
viewer_actions()->queue_attack(target_hex, weapon_choice, *route_, move_arrow, fake_unit);
}
resources::screen->invalidate(target_hex);
erase_temp_move();
LOG_WB << *viewer_actions() << "\n";
}
}
void manager::contextual_execute()
{
if (!(executing_actions_ || viewer_actions()->empty())
&& resources::controller->current_side() == resources::screen->viewing_side())
{
erase_temp_move();
validate_viewer_actions();
action_ptr action;
side_actions::iterator it;
unit* selected_unit = this->find_selected_actor_future();
if (selected_unit &&
(it = viewer_actions()->find_first_action_of(*selected_unit)) != viewer_actions()->end())
{
executing_actions_ = true;
viewer_actions()->execute(it);
executing_actions_ = false;
}
else if (highlighter_ && (action = highlighter_->get_execute_target()) &&
(it = viewer_actions()->get_position_of(action)) != viewer_actions()->end())
{
executing_actions_ = true;
viewer_actions()->execute(it);
executing_actions_ = false;
}
else //we already check above for viewer_actions()->empty()
{
executing_actions_ = true;
viewer_actions()->execute_next();
executing_actions_ = false;
}
}
}
void manager::contextual_delete()
{
if (!(executing_actions_ || viewer_actions()->empty()))
{
erase_temp_move();
validate_viewer_actions();
action_ptr action;
side_actions::iterator it;
unit* selected_unit = this->find_selected_actor_future();
if (selected_unit &&
(it = viewer_actions()->find_first_action_of(*selected_unit)) != viewer_actions()->end())
{
viewer_actions()->remove_action(it);
}
else if (highlighter_ && (action = highlighter_->get_delete_target()) &&
(it = viewer_actions()->get_position_of(action)) != viewer_actions()->end())
{
viewer_actions()->remove_action(it);
}
else //we already check above for viewer_actions()->empty()
{
viewer_actions()->remove_action(viewer_actions()->end() - 1);
}
}
}
void manager::contextual_bump_up_action()
{
if (!(executing_actions_ || viewer_actions()->empty()) && highlighter_)
{
validate_viewer_actions();
action_ptr action = highlighter_->get_bump_target();
if (action)
{
viewer_actions()->bump_earlier(viewer_actions()->get_position_of(action));
}
}
}
void manager::contextual_bump_down_action()
{
if (!(executing_actions_ || viewer_actions()->empty()) && highlighter_)
{
validate_viewer_actions();
action_ptr action = highlighter_->get_bump_target();
if (action)
{
viewer_actions()->bump_later(viewer_actions()->get_position_of(action));
}
}
}
bool manager::unit_has_actions(const unit& unit) const
{
return viewer_actions()->find_first_action_of(unit)
!= viewer_actions()->end();
}
void manager::fake_unit_deleter::operator() (unit*& fake_unit)
{
if (fake_unit)
{
if(resources::screen)
{
resources::screen->remove_temporary_unit(fake_unit);
}
DBG_WB << "Erasing temporary unit " << fake_unit->name() << " [ " << fake_unit->underlying_id() << "]\n";
delete fake_unit;
}
}
scoped_planned_unit_map::scoped_planned_unit_map()
:has_planned_unit_map_(resources::whiteboard->has_planned_unit_map())
{
if (!has_planned_unit_map_)
resources::whiteboard->set_planned_unit_map();
}
scoped_planned_unit_map::~scoped_planned_unit_map()
{
if (!has_planned_unit_map_)
resources::whiteboard->set_real_unit_map();
}
scoped_real_unit_map::scoped_real_unit_map()
:has_planned_unit_map_(resources::whiteboard->has_planned_unit_map())
{
if (has_planned_unit_map_)
resources::whiteboard->set_real_unit_map();
}
scoped_real_unit_map::~scoped_real_unit_map()
{
if (has_planned_unit_map_ && !resources::whiteboard->has_planned_unit_map())
resources::whiteboard->set_planned_unit_map();
}
} // end namespace wb