whiteboard/manager.cpp

Go to the documentation of this file.
00001 /* $Id: manager.cpp 53982 2012-04-23 05:19:10Z gabba $ */
00002 /*
00003  Copyright (C) 2010 - 2012 by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
00004  Part of the Battle for Wesnoth Project http://www.wesnoth.org
00005 
00006  This program is free software; you can redistribute it and/or modify
00007  it under the terms of the GNU General Public License as published by
00008  the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010  This program is distributed in the hope that it will be useful,
00011  but WITHOUT ANY WARRANTY.
00012 
00013  See the COPYING file for more details.
00014  */
00015 
00016 /**
00017  * @file
00018  */
00019 
00020 #include "manager.hpp"
00021 
00022 #include "action.hpp"
00023 #include "highlight_visitor.hpp"
00024 #include "mapbuilder.hpp"
00025 #include "move.hpp"
00026 #include "attack.hpp"
00027 #include "recall.hpp"
00028 #include "recruit.hpp"
00029 #include "side_actions.hpp"
00030 #include "utility.hpp"
00031 
00032 #include "actions.hpp"
00033 #include "arrow.hpp"
00034 #include "chat_events.hpp"
00035 #include "foreach.hpp"
00036 #include "formula_string_utils.hpp"
00037 #include "game_preferences.hpp"
00038 #include "gettext.hpp"
00039 #include "gui/dialogs/simple_item_selector.hpp"
00040 #include "key.hpp"
00041 #include "network.hpp"
00042 #include "pathfind/pathfind.hpp"
00043 #include "play_controller.hpp"
00044 #include "resources.hpp"
00045 #include "rng.hpp"
00046 #include "team.hpp"
00047 #include "unit_display.hpp"
00048 
00049 #include <boost/lexical_cast.hpp>
00050 #include <sstream>
00051 
00052 namespace wb {
00053 
00054 manager::manager():
00055         active_(false),
00056         inverted_behavior_(false),
00057         self_activate_once_(true),
00058         print_help_once_(true),
00059         wait_for_side_init_(true),
00060         planned_unit_map_active_(false),
00061         executing_actions_(false),
00062         executing_all_actions_(false),
00063         preparing_to_end_turn_(false),
00064         gamestate_mutated_(false),
00065         activation_state_lock_(new bool),
00066         unit_map_lock_(new bool),
00067         mapbuilder_(),
00068         highlighter_(),
00069         route_(),
00070         move_arrows_(),
00071         fake_units_(),
00072         temp_move_unit_underlying_id_(0),
00073         key_poller_(new CKey),
00074         hidden_unit_hexes_(),
00075         net_buffer_(resources::teams->size()),
00076         team_plans_hidden_(resources::teams->size(),preferences::hide_whiteboard()),
00077         units_owning_moves_()
00078 {
00079     LOG_WB << "Manager initialized.\n";
00080 }
00081 
00082 manager::~manager()
00083 {
00084     LOG_WB << "Manager destroyed.\n";
00085 }
00086 
00087 //Used for chat-spamming debug info
00088 #if 0
00089 static void print_to_chat(const std::string& title, const std::string& message)
00090 {
00091     resources::screen->add_chat_message(time(NULL), title, 0, message,
00092             events::chat_handler::MESSAGE_PRIVATE, false);
00093 }
00094 #endif
00095 
00096 void manager::print_help_once()
00097 {
00098 #if 0
00099     if (!print_help_once_)
00100         return;
00101     else
00102         print_help_once_ = false;
00103 
00104     print_to_chat("whiteboard", std::string("Type :wb to activate/deactivate planning mode.")
00105         + "  Hold TAB to temporarily deactivate/activate it.");
00106     std::stringstream hotkeys;
00107     const hotkey::hotkey_item& hk_execute = hotkey::get_hotkey(hotkey::HOTKEY_WB_EXECUTE_ACTION);
00108     if(!hk_execute.null()) {
00109         //print_to_chat("[execute action]", "'" + hk_execute.get_name() + "'");
00110         hotkeys << "Execute: " << hk_execute.get_name() << ", ";
00111     }
00112     const hotkey::hotkey_item& hk_execute_all = hotkey::get_hotkey(hotkey::HOTKEY_WB_EXECUTE_ALL_ACTIONS);
00113     if(!hk_execute_all.null()) {
00114         //print_to_chat("[execute action]", "'" + hk_execute_all.get_name() + "'");
00115         hotkeys << "Execute all: " << hk_execute_all.get_name() << ", ";
00116     }
00117     const hotkey::hotkey_item& hk_delete = hotkey::get_hotkey(hotkey::HOTKEY_WB_DELETE_ACTION);
00118     if(!hk_delete.null()) {
00119         //print_to_chat("[delete action]", "'" + hk_delete.get_name() + "'");
00120         hotkeys << "Delete: " << hk_delete.get_name() << ", ";
00121     }
00122     const hotkey::hotkey_item& hk_bump_up = hotkey::get_hotkey(hotkey::HOTKEY_WB_BUMP_UP_ACTION);
00123     if(!hk_bump_up.null()) {
00124         //print_to_chat("[move action earlier in queue]", "'" + hk_bump_up.get_name() + "'");
00125         hotkeys << "Move earlier: " << hk_bump_up.get_name() << ", ";
00126     }
00127     const hotkey::hotkey_item& hk_bump_down = hotkey::get_hotkey(hotkey::HOTKEY_WB_BUMP_DOWN_ACTION);
00128     if(!hk_bump_down.null()) {
00129         //print_to_chat("[move action later in queue]", "'" + hk_bump_down.get_name() + "'");
00130         hotkeys << "Move later: " << hk_bump_down.get_name() << ", ";
00131     }
00132     print_to_chat("HOTKEYS:", hotkeys.str() + "\n");
00133 #endif
00134 }
00135 
00136 bool manager::can_modify_game_state() const
00137 {
00138     if(wait_for_side_init_
00139                     || resources::teams == NULL
00140                     || executing_actions_
00141                     || is_observer()
00142                     || resources::controller->is_linger_mode())
00143     {
00144         return false;
00145     }
00146     else
00147     {
00148         return true;
00149     }
00150 }
00151 
00152 bool manager::can_activate() const
00153 {
00154     //any more than one reference means a lock on whiteboard state was requested
00155     if(!activation_state_lock_.unique())
00156         return false;
00157 
00158     return can_modify_game_state();
00159 }
00160 
00161 void manager::set_active(bool active)
00162 {
00163     if(!can_activate())
00164     {
00165         active_ = false;
00166         LOG_WB << "Whiteboard can't be activated now.\n";
00167     }
00168     else if (active != active_)
00169     {
00170         active_ = active;
00171         erase_temp_move();
00172 
00173         if (active_)
00174         {
00175             if(should_clear_undo())
00176                 clear_undo();
00177             validate_viewer_actions();
00178             LOG_WB << "Whiteboard activated! " << *viewer_actions() << "\n";
00179             create_temp_move();
00180         } else {
00181             LOG_WB << "Whiteboard deactivated!\n";
00182         }
00183     }
00184 }
00185 
00186 void manager::set_invert_behavior(bool invert)
00187 {
00188     //any more than one reference means a lock on whiteboard state was requested
00189     if(!activation_state_lock_.unique())
00190         return;
00191 
00192     bool block_whiteboard_activation = false;
00193     if(!can_activate())
00194     {
00195          block_whiteboard_activation = true;
00196     }
00197 
00198     if (invert)
00199     {
00200         if (!inverted_behavior_)
00201         {
00202             if (active_)
00203             {
00204                 DBG_WB << "Whiteboard deactivated temporarily.\n";
00205                 inverted_behavior_ = true;
00206                 set_active(false);
00207             }
00208             else if (!block_whiteboard_activation)
00209             {
00210                 DBG_WB << "Whiteboard activated temporarily.\n";
00211                 inverted_behavior_ = true;
00212                 set_active(true);
00213             }
00214         }
00215     }
00216     else
00217     {
00218         if (inverted_behavior_)
00219         {
00220             if (active_)
00221             {
00222                 DBG_WB << "Whiteboard set back to deactivated status.\n";
00223                 inverted_behavior_ = false;
00224                 set_active(false);
00225             }
00226             else if (!block_whiteboard_activation)
00227             {
00228                 DBG_WB << "Whiteboard set back to activated status.\n";
00229                 inverted_behavior_ = false;
00230                 set_active(true);
00231             }
00232         }
00233     }
00234 }
00235 
00236 bool manager::can_enable_execution_hotkeys() const
00237 {
00238     return can_enable_modifier_hotkeys() && viewer_side() == resources::controller->current_side()
00239             && viewer_actions()->turn_size(0) > 0;
00240 }
00241 
00242 bool manager::can_enable_modifier_hotkeys() const
00243 {
00244     return can_modify_game_state() && !viewer_actions()->empty();
00245 }
00246 
00247 bool manager::can_enable_reorder_hotkeys() const
00248 {
00249     return can_enable_modifier_hotkeys() && highlighter_ && highlighter_->get_bump_target();
00250 }
00251 
00252 bool manager::allow_leader_to_move(unit const& leader) const
00253 {
00254     if(!has_actions())
00255         return true;
00256 
00257     //Look for another leader on another keep in the same castle
00258     { wb::future_map future; // start planned unit map scope
00259         if(!has_planned_unit_map()) {
00260             WRN_WB << "Unable to build future map to determine whether leader's allowed to move.\n";
00261         }
00262         if(find_backup_leader(leader))
00263             return true;
00264     } // end planned unit map scope
00265 
00266     if(viewer_actions()->empty()) {
00267         return true;
00268     }
00269 
00270     //Look for planned recruits that depend on this leader
00271     foreach(action_const_ptr action, *viewer_actions())
00272     {
00273         recruit_const_ptr recruit = boost::dynamic_pointer_cast<class recruit const>(action);
00274         recall_const_ptr recall = boost::dynamic_pointer_cast<class recall const>(action);
00275         if(recruit || recall)
00276         {
00277             map_location const target_hex = recruit?recruit->get_recruit_hex():recall->get_recall_hex();
00278             if (can_recruit_on(*resources::game_map, leader.get_location(), target_hex))
00279                 return false;
00280         }
00281     }
00282     return true;
00283 }
00284 
00285 void manager::on_init_side()
00286 {
00287     //Turn should never start with action auto-execution already enabled!
00288     assert(!executing_all_actions_ && !executing_actions_);
00289 
00290     update_plan_hiding(); //< validates actions
00291     wait_for_side_init_ = false;
00292     LOG_WB << "on_init_side()\n";
00293 
00294     if (self_activate_once_ && preferences::enable_whiteboard_mode_on_start())
00295     {
00296         self_activate_once_ = false;
00297         set_active(true);
00298     }
00299 }
00300 
00301 void manager::on_finish_side_turn(int side)
00302 {
00303     preparing_to_end_turn_ = false;
00304     wait_for_side_init_ = true;
00305     if(side == viewer_side() && !viewer_actions()->empty()) {
00306         viewer_actions()->synced_turn_shift();
00307     }
00308     highlighter_.reset();
00309     erase_temp_move();
00310     LOG_WB << "on_finish_side_turn()\n";
00311 }
00312 
00313 void manager::pre_delete_action(action_ptr)
00314 {
00315 }
00316 
00317 void manager::post_delete_action(action_ptr action)
00318 {
00319     // The fake unit representing the destination of a chain of planned moves should have the regular animation.
00320     // If the last remaining action of the unit that owned this move is a move as well,
00321     // adjust its appearance accordingly.
00322 
00323     side_actions_ptr side_actions = resources::teams->at(action->team_index()).get_side_actions();
00324 
00325     side_actions::iterator action_it = side_actions->find_last_action_of(action->get_unit());
00326     if (action_it != side_actions->end())
00327     {
00328         if (move_ptr move = boost::dynamic_pointer_cast<class move>(*action_it))
00329         {
00330             if (move->get_fake_unit())
00331                 move->get_fake_unit()->set_standing(true);
00332         }
00333     }
00334 }
00335 
00336 static void hide_all_plans()
00337 {
00338     foreach(team& t, *resources::teams)
00339         t.get_side_actions()->hide();
00340 }
00341 
00342 /* private */
00343 void manager::update_plan_hiding(size_t team_index)
00344 {
00345     //We don't control the "viewing" side ... we're probably an observer
00346     if(!resources::teams->at(team_index).is_human())
00347         hide_all_plans();
00348     else //< normal circumstance
00349     {
00350         foreach(team& t, *resources::teams)
00351         {
00352             //make sure only appropriate teams are hidden
00353             if(!t.is_network_human())
00354                 team_plans_hidden_[t.side()-1] = false;
00355 
00356             if(t.is_enemy(team_index+1) || team_plans_hidden_[t.side()-1])
00357                 t.get_side_actions()->hide();
00358             else
00359                 t.get_side_actions()->show();
00360         }
00361     }
00362     resources::teams->at(team_index).get_side_actions()->validate_actions();
00363 }
00364 void manager::update_plan_hiding()
00365     {update_plan_hiding(viewer_team());}
00366 
00367 void manager::on_viewer_change(size_t team_index)
00368 {
00369     if(!wait_for_side_init_)
00370         update_plan_hiding(team_index);
00371 }
00372 
00373 void manager::on_change_controller(int side, team& t)
00374 {
00375     wb::side_actions& sa = *t.get_side_actions();
00376     if(t.is_human()) //< we own this side now
00377     {
00378         //tell everyone to clear this side's actions -- we're starting anew
00379         resources::whiteboard->queue_net_cmd(sa.team_index(),sa.make_net_cmd_clear());
00380         sa.clear();
00381         //refresh the hidden_ attribute of every team's side_actions
00382         update_plan_hiding();
00383     }
00384     else if(t.is_ai() || t.is_network_ai()) //< no one owns this side anymore
00385         sa.clear(); //< clear its plans away -- the ai doesn't plan ... yet
00386     else if(t.is_network()) //< Another client is taking control of the side
00387     {
00388         if(side==viewer_side()) //< They're taking OUR side away!
00389             hide_all_plans(); //< give up knowledge of everyone's plans, in case we became an observer
00390 
00391         //tell them our plans -- they may not have received them up to this point
00392         size_t num_teams = resources::teams->size();
00393         for(size_t i=0; i<num_teams; ++i)
00394         {
00395             team& local_team = resources::teams->at(i);
00396             if(local_team.is_human() && !local_team.is_enemy(side))
00397                 resources::whiteboard->queue_net_cmd(i,local_team.get_side_actions()->make_net_cmd_refresh());
00398         }
00399     }
00400 }
00401 
00402 bool manager::current_side_has_actions()
00403 {
00404     if(current_side_actions()->empty()) {
00405         return false;
00406     }
00407 
00408     side_actions::range_t range = current_side_actions()->iter_turn(0);
00409     return range.first != range.second; //non-empty range
00410 }
00411 
00412 void manager::validate_viewer_actions()
00413 {
00414     assert(!executing_actions_);
00415     LOG_WB << "'gamestate_mutated_' flag dirty, validating actions.\n";
00416     gamestate_mutated_ = false;
00417     if (viewer_actions()->empty()) return;
00418     viewer_actions()->validate_actions();
00419 }
00420 
00421 //helper fcn
00422 static void draw_numbers(map_location const& hex, side_actions::numbers_t numbers)
00423 {
00424     std::vector<int>& numbers_to_draw = numbers.numbers_to_draw;
00425     std::vector<size_t>& team_numbers = numbers.team_numbers;
00426     int& main_number = numbers.main_number;
00427     std::set<size_t>& secondary_numbers = numbers.secondary_numbers;
00428 
00429     const double x_offset_base = 0.0;
00430     const double y_offset_base = 0.2;
00431     //position 0,0 in the hex is the upper left corner
00432     //0.8 = horizontal coord., close to the right side of the hex
00433     const double x_origin = 0.8 - numbers_to_draw.size() * x_offset_base;
00434     //0.5 = halfway in the hex vertically
00435     const double y_origin = 0.5 - numbers_to_draw.size() * (y_offset_base / 2);
00436     double x_offset = 0, y_offset = 0;
00437 
00438     size_t size = numbers_to_draw.size();
00439     for(size_t i=0; i<size; ++i)
00440     {
00441         int number = numbers_to_draw[i];
00442 
00443         std::string number_text = boost::lexical_cast<std::string>(number);
00444         size_t font_size;
00445         if (int(i) == main_number) font_size = 19;
00446         else if (secondary_numbers.find(i)!=secondary_numbers.end()) font_size = 17;
00447         else font_size = 15;
00448 
00449         SDL_Color color = team::get_side_color(static_cast<int>(team_numbers[i]+1));
00450         const double x_in_hex = x_origin + x_offset;
00451         const double y_in_hex = y_origin + y_offset;
00452         resources::screen->draw_text_in_hex(hex, display::LAYER_ACTIONS_NUMBERING,
00453                 number_text, font_size, color, x_in_hex, y_in_hex);
00454         x_offset += x_offset_base;
00455         y_offset += y_offset_base;
00456     }
00457 }
00458 
00459 
00460 namespace
00461 {
00462     //Helper struct that finds all units teams whose planned actions are currently visible
00463     //Only used by manager::pre_draw() and post_draw()
00464     struct move_owners_finder
00465         : private enable_visit_all<move_owners_finder>, public visitor
00466     {
00467         friend class enable_visit_all<move_owners_finder>;
00468 
00469     public:
00470         move_owners_finder()
00471             : move_owners_()
00472         {
00473             //Thanks to the default pre_visit_team, will only visit visible side_actions
00474             visit_all_actions();
00475         }
00476 
00477         std::set<size_t> const& get_units_owning_moves() {
00478             return move_owners_;
00479         }
00480 
00481     private:
00482         virtual void visit(move_ptr move) {
00483             move_owners_.insert(move->get_unit()->underlying_id());
00484         }
00485         virtual void visit(attack_ptr attack) {
00486             //also add attacks if they have an associated move
00487             if (boost::static_pointer_cast<move>(attack)->get_route().steps.size() >= 2) {
00488                 move_owners_.insert(attack->get_unit()->underlying_id());
00489             }
00490         }
00491         virtual void visit(recruit_ptr){}
00492         virtual void visit(recall_ptr){}
00493         virtual void visit(suppose_dead_ptr){}
00494 
00495         std::set<size_t> move_owners_;
00496     };
00497 }
00498 
00499 void manager::pre_draw()
00500 {
00501     if (can_modify_game_state() && has_actions())
00502     {
00503         units_owning_moves_ = move_owners_finder().get_units_owning_moves();
00504         foreach(size_t unit_id, units_owning_moves_)
00505         {
00506             unit_map::iterator unit_iter = resources::units->find(unit_id);
00507             assert(unit_iter.valid());
00508             ghost_owner_unit(&*unit_iter);
00509         }
00510     }
00511 }
00512 
00513 void manager::post_draw()
00514 {
00515     foreach(size_t unit_id, units_owning_moves_)
00516     {
00517         unit_map::iterator unit_iter = resources::units->find(unit_id);
00518         if (unit_iter.valid()) { 
00519             unghost_owner_unit(&*unit_iter);
00520         }
00521     }
00522     units_owning_moves_.clear();
00523 }
00524 
00525 namespace
00526 {
00527     //Only used by manager::draw_hex()
00528     struct draw_visitor
00529         : private enable_visit_all<draw_visitor>
00530     {
00531         friend class enable_visit_all<draw_visitor>;
00532 
00533     public:
00534         draw_visitor(map_location const& hex): hex_(hex) {}
00535 
00536         using enable_visit_all<draw_visitor>::visit_all;
00537 
00538     private:
00539         //"Inherited" from enable_visit_all
00540         bool process(size_t /*team_index*/, team&, side_actions&, side_actions::iterator itor)
00541             { (*itor)->draw_hex(hex_);   return true; }
00542         //using default pre_visit_team()
00543         //using default post_visit_team()
00544 
00545         map_location const& hex_;
00546     };
00547 }
00548 
00549 void manager::draw_hex(const map_location& hex)
00550 {
00551     /**
00552      * IMPORTANT: none of the code in this method can call anything which would
00553      * cause a hex to be invalidated (i.e. by calling in turn any variant of display::invalidate()).
00554      * Doing so messes up the iterator currently going over the list of invalidated hexes to draw.
00555      */
00556 
00557     if (!wait_for_side_init_ && has_actions())
00558     {
00559         //call draw() for all actions
00560         draw_visitor(hex).visit_all();
00561 
00562         //Info about the action numbers to be displayed on screen.
00563         side_actions::numbers_t numbers;
00564         foreach(team& t, *resources::teams)
00565         {
00566             side_actions& sa = *t.get_side_actions();
00567             if(!sa.hidden())
00568                 sa.get_numbers(hex,numbers);
00569         }
00570         draw_numbers(hex,numbers); //< helper fcn
00571     }
00572 
00573 }
00574 
00575 void manager::on_mouseover_change(const map_location& hex)
00576 {
00577 
00578     map_location selected_hex = resources::controller->get_mouse_handler_base().get_selected_hex();
00579     bool hex_has_unit;
00580     { wb::future_map future; //< start planned unit map scope
00581         hex_has_unit = resources::units->find(selected_hex) != resources::units->end();
00582     } // end planned unit map scope
00583     if (!((selected_hex.valid() && hex_has_unit)
00584             || has_temp_move() || wait_for_side_init_ || executing_actions_))
00585     {
00586         if (!highlighter_)
00587         {
00588             highlighter_.reset(new highlight_visitor(*resources::units, viewer_actions()));
00589         }
00590         highlighter_->set_mouseover_hex(hex);
00591         highlighter_->highlight();
00592     }
00593 }
00594 
00595 void manager::on_gamestate_change()
00596 {
00597     DBG_WB << "Manager received gamestate change notification.\n";
00598     // if on_gamestate_change() is called while the future unit map is applied,
00599     // it means that the future unit map scope is used where it shouldn't be.
00600     assert(!planned_unit_map_active_);
00601     // Set mutated flag so action queue gets validated on next future map build
00602     gamestate_mutated_ = true;
00603     //Clear exclusive draws that might not get a chance to be cleared the normal way
00604     resources::screen->clear_exclusive_draws();
00605 }
00606 
00607 void manager::send_network_data()
00608 {
00609     size_t size = net_buffer_.size();
00610     for(size_t team_index=0; team_index<size; ++team_index)
00611     {
00612         config& buf_cfg = net_buffer_[team_index];
00613 
00614         if(buf_cfg.empty())
00615             continue;
00616 
00617         config packet;
00618         config& wb_cfg = packet.add_child("whiteboard",buf_cfg);
00619         wb_cfg["side"] = static_cast<int>(team_index+1);
00620         wb_cfg["team_name"] = resources::teams->at(team_index).team_name();
00621 
00622         buf_cfg = config();
00623 
00624         network::send_data(packet,0,"whiteboard");
00625 
00626         size_t count = wb_cfg.child_count("net_cmd");
00627         LOG_WB << "Side " << (team_index+1) << " sent wb data (" << count << " cmds).\n";
00628     }
00629 }
00630 
00631 void manager::process_network_data(config const& cfg)
00632 {
00633     if(config const& wb_cfg = cfg.child("whiteboard"))
00634     {
00635         size_t count = wb_cfg.child_count("net_cmd");
00636         LOG_WB << "Received wb data (" << count << ").\n";
00637 
00638         team& team_from = resources::teams->at(wb_cfg["side"]-1);
00639         foreach(side_actions::net_cmd const& cmd, wb_cfg.child_range("net_cmd"))
00640             team_from.get_side_actions()->execute_net_cmd(cmd);
00641     }
00642 }
00643 
00644 void manager::queue_net_cmd(size_t team_index, side_actions::net_cmd const& cmd)
00645 {
00646     net_buffer_[team_index].add_child("net_cmd",cmd);
00647 }
00648 
00649 void manager::create_temp_move()
00650 {
00651     route_.reset();
00652 
00653     /*
00654      * CHECK PRE-CONDITIONS
00655      * (This section has multiple return paths.)
00656      */
00657 
00658     if(!active_
00659             || wait_for_side_init_
00660             || executing_actions_
00661             || is_observer()
00662             || resources::controller->is_linger_mode())
00663         return;
00664 
00665     pathfind::marked_route const& route =
00666             resources::controller->get_mouse_handler_base().get_current_route();
00667 
00668     if (route.steps.empty() || route.steps.size() < 2) return;
00669 
00670     unit const* selected_unit =
00671             future_visible_unit(resources::controller->get_mouse_handler_base().get_selected_hex(), viewer_side());
00672     if (!selected_unit) return;
00673     if (selected_unit->side() != resources::screen->viewing_side()) return;
00674 
00675     /*
00676      * DONE CHECKING PRE-CONDITIONS, CREATE THE TEMP MOVE
00677      * (This section has only one return path.)
00678      */
00679 
00680     temp_move_unit_underlying_id_ = selected_unit->underlying_id();
00681 
00682     //@todo: May be appropriate to replace these separate components by a temporary
00683     //      wb::move object
00684 
00685     route_.reset(new pathfind::marked_route(route));
00686     //NOTE: route_->steps.back() = dst, and route_->steps.front() = src
00687 
00688     size_t turn = 0;
00689     std::vector<map_location>::iterator prev_itor = route.steps.begin();
00690     std::vector<map_location>::iterator curr_itor = prev_itor;
00691     std::vector<map_location>::iterator end_itor  = route.steps.end();
00692     for(; curr_itor!=end_itor; ++curr_itor)
00693     {
00694         const map_location& hex = *curr_itor;
00695 
00696         //search for end-of-turn marks
00697         pathfind::marked_route::mark_map::const_iterator w =
00698                 route.marks.find(hex);
00699         if(w != route.marks.end() && w->second.turns > 0)
00700         {
00701             turn = w->second.turns-1;
00702 
00703             if(turn >= move_arrows_.size())
00704                 move_arrows_.resize(turn+1);
00705             if(turn >= fake_units_.size())
00706                 fake_units_.resize(turn+1);
00707 
00708             arrow_ptr& move_arrow = move_arrows_[turn];
00709             fake_unit_ptr& fake_unit = fake_units_[turn];
00710 
00711             if(!move_arrow)
00712             {
00713                 // Create temp arrow
00714                 move_arrow.reset(new arrow());
00715                 move_arrow->set_color(team::get_side_color_index(
00716                         viewer_side()));
00717                 move_arrow->set_style(arrow::STYLE_HIGHLIGHTED);
00718             }
00719 
00720             arrow_path_t path(prev_itor,curr_itor+1);
00721             move_arrow->set_path(path);
00722 
00723             if(path.size() >= 2)
00724             {
00725                 if(!fake_unit)
00726                 {
00727                     // Create temp ghost unit
00728                     fake_unit.reset(new game_display::fake_unit(*selected_unit));
00729                     fake_unit->place_on_game_display( resources::screen);
00730                     fake_unit->set_ghosted(true);
00731                 }
00732 
00733                 unit_display::move_unit(path, *fake_unit, *resources::teams,
00734                         false); //get facing right
00735                 fake_unit->invalidate(fake_unit->get_location());
00736                 fake_unit->set_location(*curr_itor);
00737                 fake_unit->set_ghosted(true);
00738             }
00739             else //zero-hex path -- don't bother drawing a fake unit
00740                 fake_unit.reset();
00741 
00742             prev_itor = curr_itor;
00743         }
00744     }
00745     //in case path shortens on next step and one ghosted unit has to be removed
00746     int ind = fake_units_.size() - 1;
00747     fake_units_[ind]->invalidate(fake_units_[ind]->get_location());
00748     //toss out old arrows and fake units
00749     move_arrows_.resize(turn+1);
00750     fake_units_.resize(turn+1);
00751 }
00752 
00753 void manager::erase_temp_move()
00754 {
00755     move_arrows_.clear();
00756     foreach(fake_unit_ptr const& tmp, fake_units_) {
00757         if(tmp) {
00758             tmp->invalidate(tmp->get_location());
00759         }
00760     }
00761     fake_units_.clear();
00762     route_.reset();
00763     temp_move_unit_underlying_id_ = 0;
00764 }
00765 
00766 void manager::save_temp_move()
00767 {
00768     if (has_temp_move() && !executing_actions_ && !resources::controller->is_linger_mode())
00769     {
00770         side_actions& sa = *viewer_actions();
00771         unit* u = future_visible_unit(route_->steps.front());
00772         assert(u);
00773         size_t first_turn = sa.get_turn_num_of(*u);
00774 
00775         on_save_action(u);
00776 
00777         assert(move_arrows_.size() == fake_units_.size());
00778         size_t size = move_arrows_.size();
00779         for(size_t i=0; i<size; ++i)
00780         {
00781             arrow_ptr move_arrow = move_arrows_[i];
00782             if(!arrow::valid_path(move_arrow->get_path()))
00783                 continue;
00784 
00785             size_t turn = first_turn + i;
00786             fake_unit_ptr fake_unit = fake_units_[i];
00787 
00788             //@todo Using a marked_route here is wrong, since right now it's not marked
00789             //either switch over to a plain route for planned moves, or mark it correctly
00790             pathfind::marked_route route;
00791             route.steps = move_arrow->get_path();
00792             route.move_cost = path_cost(route.steps,*u);
00793 
00794             sa.queue_move(turn,*u,route,move_arrow,fake_unit);
00795         }
00796         erase_temp_move();
00797 
00798         LOG_WB << *viewer_actions() << "\n";
00799         print_help_once();
00800     }
00801 }
00802 
00803 unit_map::iterator manager::get_temp_move_unit() const
00804 {
00805     return resources::units->find(temp_move_unit_underlying_id_);
00806 }
00807 
00808 void manager::save_temp_attack(const map_location& attacker_loc, const map_location& defender_loc, int weapon_choice)
00809 {
00810     if (active_ && !executing_actions_ && !resources::controller->is_linger_mode())
00811     {
00812         assert(weapon_choice >= 0);
00813 
00814         arrow_ptr move_arrow;
00815         fake_unit_ptr fake_unit;
00816         map_location source_hex;
00817 
00818         if (route_ && !route_->steps.empty())
00819         {
00820             //attack-move
00821             assert(move_arrows_.size() == 1);
00822             assert(fake_units_.size() == 1);
00823             move_arrow = move_arrows_.front();
00824             fake_unit = fake_units_.front();
00825 
00826             assert(route_->steps.back() == attacker_loc);
00827             source_hex = route_->steps.front();
00828 
00829             fake_unit->set_disabled_ghosted(true);
00830         }
00831         else
00832         {
00833             //simple attack
00834             move_arrow.reset(new arrow);
00835             source_hex = attacker_loc;
00836             route_.reset(new pathfind::marked_route);
00837             // We'll pass as parameter a one-hex route with no marks.
00838             route_->steps.push_back(attacker_loc);
00839         }
00840 
00841         unit* attacking_unit = future_visible_unit(source_hex);
00842         assert(attacking_unit);
00843 
00844         on_save_action(attacking_unit);
00845 
00846         side_actions& sa = *viewer_actions();
00847         sa.queue_attack(sa.get_turn_num_of(*attacking_unit),*attacking_unit,defender_loc,weapon_choice,*route_,move_arrow,fake_unit);
00848 
00849         print_help_once();
00850 
00851         resources::screen->invalidate(defender_loc);
00852         resources::screen->invalidate(attacker_loc);
00853         erase_temp_move();
00854         LOG_WB << *viewer_actions() << "\n";
00855     }
00856 }
00857 
00858 bool manager::save_recruit(const std::string& name, int side_num, const map_location& recruit_hex)
00859 {
00860     bool created_planned_recruit = false;
00861 
00862     if (active_ && !executing_actions_ && !resources::controller->is_linger_mode()) {
00863         if (side_num != resources::screen->viewing_side())
00864         {
00865             LOG_WB <<"manager::save_recruit called for a different side than viewing side.\n";
00866             created_planned_recruit = false;
00867         }
00868         else
00869         {
00870             on_save_action(NULL);
00871 
00872             side_actions& sa = *viewer_actions();
00873             unit* recruiter;
00874             { wb::future_map raii;
00875                 recruiter = find_recruiter(side_num-1,recruit_hex);
00876             } // end planned unit map scope
00877             assert(recruiter);
00878             size_t turn = sa.get_turn_num_of(*recruiter);
00879             sa.queue_recruit(turn,name,recruit_hex);
00880             created_planned_recruit = true;
00881 
00882             print_help_once();
00883         }
00884     }
00885     return created_planned_recruit;
00886 }
00887 
00888 bool manager::save_recall(const unit& unit, int side_num, const map_location& recall_hex)
00889 {
00890     bool created_planned_recall = false;
00891 
00892     if (active_ && !executing_actions_ && !resources::controller->is_linger_mode())
00893     {
00894         if (side_num != resources::screen->viewing_side())
00895         {
00896             LOG_WB <<"manager::save_recall called for a different side than viewing side.\n";
00897             created_planned_recall = false;
00898         }
00899         else
00900         {
00901             on_save_action(NULL);
00902 
00903             side_actions& sa = *viewer_actions();
00904             size_t turn = sa.num_turns();
00905             if(turn > 0)
00906                 --turn;
00907             sa.queue_recall(turn,unit,recall_hex);
00908             created_planned_recall = true;
00909 
00910             print_help_once();
00911         }
00912     }
00913     return created_planned_recall;
00914 }
00915 
00916 void manager::save_suppose_dead(unit& curr_unit, map_location const& loc)
00917 {
00918     if(active_ && !executing_actions_ && !resources::controller->is_linger_mode())
00919     {
00920         on_save_action(&curr_unit);
00921         side_actions& sa = *viewer_actions();
00922         sa.queue_suppose_dead(sa.get_turn_num_of(curr_unit),curr_unit,loc);
00923     }
00924 }
00925 
00926 void manager::on_save_action(unit const* u) const
00927 {
00928     if(u)
00929         viewer_actions()->remove_invalid_of(u);
00930 }
00931 
00932 void manager::contextual_execute()
00933 {
00934     validate_viewer_actions();
00935     if (can_enable_execution_hotkeys())
00936     {
00937         erase_temp_move();
00938 
00939         //For exception-safety, this struct sets executing_actions_ to false on destruction.
00940         variable_finalizer<bool> finally(executing_actions_, false);
00941 
00942         action_ptr action;
00943         side_actions::iterator it;
00944         unit const* selected_unit = future_visible_unit(resources::controller->get_mouse_handler_base().get_selected_hex(), viewer_side());
00945         if (selected_unit &&
00946                 (it = viewer_actions()->find_first_action_of(selected_unit)) != viewer_actions()->end())
00947         {
00948             executing_actions_ = true;
00949             viewer_actions()->execute(it);
00950         }
00951         else if (highlighter_ && (action = highlighter_->get_execute_target()) &&
00952                  (it = viewer_actions()->get_position_of(action)) != viewer_actions()->end())
00953         {
00954             executing_actions_ = true;
00955             viewer_actions()->execute(it);
00956         }
00957         else //we already check above for viewer_actions()->empty()
00958         {
00959             executing_actions_ = true;
00960             viewer_actions()->execute_next();
00961         }
00962     } //Finalizer struct sets executing_actions_ to false
00963 }
00964 
00965 bool manager::allow_end_turn()
00966 {
00967     preparing_to_end_turn_ = true;
00968     return execute_all_actions();
00969 }
00970 
00971 bool manager::execute_all_actions()
00972 {
00973     //exception-safety: finalizers set variables to false on destruction
00974     //i.e. when method exits naturally or exception is thrown
00975     variable_finalizer<bool> finalize_executing_actions(executing_actions_, false);
00976     variable_finalizer<bool> finalize_executing_all_actions(executing_all_actions_, false);
00977 
00978     validate_viewer_actions();
00979     if(viewer_actions()->empty() || viewer_actions()->turn_size(0) == 0)
00980     {
00981         //No actions to execute, job done.
00982         return true;
00983     }
00984 
00985     assert(can_enable_execution_hotkeys());
00986 
00987     erase_temp_move();
00988 
00989     // Build unit map once to ensure spent gold and other calculations are refreshed
00990     set_planned_unit_map();
00991     assert(has_planned_unit_map());
00992     set_real_unit_map();
00993 
00994     executing_actions_ = true;
00995     executing_all_actions_ = true;
00996 
00997     side_actions_ptr sa = viewer_actions();
00998 
00999     if (resources::whiteboard->has_planned_unit_map())
01000     {
01001         ERR_WB << "Modifying action queue while temp modifiers are applied!!!\n";
01002     }
01003 
01004     //LOG_WB << "Before executing all actions, " << *sa << "\n";
01005 
01006     while (sa->turn_begin(0) != sa->turn_end(0))
01007     {
01008         bool action_successful = sa->execute(sa->begin());
01009 
01010         // Interrupt if an attack is waiting for a random seed from the server
01011         if ( rand_rng::has_new_seed_callback())
01012         {
01013             //leave executing_all_actions_ to true, we'll resume once attack completes
01014             finalize_executing_all_actions.clear();
01015 
01016             events::commands_disabled++; //to be decremented by continue_execute_all()
01017             return false;
01018         }
01019         // Interrupt on incomplete action
01020         if (!action_successful)
01021         {
01022             return false;
01023         }
01024     }
01025     return true;
01026 }
01027 
01028 void manager::continue_execute_all()
01029 {
01030     if (executing_all_actions_ && !rand_rng::has_new_seed_callback()) {
01031         events::commands_disabled--;
01032         if (execute_all_actions()) {
01033             resources::controller->force_end_turn();
01034         }
01035     }
01036 }
01037 
01038 void manager::contextual_delete()
01039 {
01040     validate_viewer_actions();
01041     if (can_enable_modifier_hotkeys())
01042     {
01043         erase_temp_move();
01044 
01045         action_ptr action;
01046         side_actions::iterator it;
01047         unit const* selected_unit = future_visible_unit(resources::controller->get_mouse_handler_base().get_selected_hex(), viewer_side());
01048         if (selected_unit &&
01049                 (it = viewer_actions()->find_first_action_of(selected_unit)) != viewer_actions()->end())
01050         {
01051             ///@todo Shouldn't it be "find_last_action_of" instead of "find_first_action_of" above?
01052             viewer_actions()->remove_action(it);
01053             ///@todo Shouldn't we probably deselect the unit at this point?
01054         }
01055         else if (highlighter_ && (action = highlighter_->get_delete_target()) &&
01056                 (it = viewer_actions()->get_position_of(action)) != viewer_actions()->end())
01057         {
01058             viewer_actions()->remove_action(it);
01059             viewer_actions()->remove_invalid_of(action->get_unit());
01060             highlighter_->set_mouseover_hex(highlighter_->get_mouseover_hex());
01061             highlighter_->highlight();
01062         }
01063         else //we already check above for viewer_actions()->empty()
01064         {
01065             it = (viewer_actions()->end() - 1);
01066             action = *it;
01067             viewer_actions()->remove_action(it);
01068             viewer_actions()->remove_invalid_of(action->get_unit());
01069         }
01070     }
01071 }
01072 
01073 void manager::contextual_bump_up_action()
01074 {
01075     validate_viewer_actions();
01076     if(can_enable_reorder_hotkeys())
01077     {
01078         action_ptr action = highlighter_->get_bump_target();
01079         if (action)
01080         {
01081             viewer_actions()->bump_earlier(viewer_actions()->get_position_of(action));
01082         }
01083     }
01084 }
01085 
01086 void manager::contextual_bump_down_action()
01087 {
01088     validate_viewer_actions();
01089     if(can_enable_reorder_hotkeys())
01090     {
01091         action_ptr action = highlighter_->get_bump_target();
01092         if (action)
01093         {
01094             viewer_actions()->bump_later(viewer_actions()->get_position_of(action));
01095         }
01096     }
01097 }
01098 
01099 bool manager::has_actions() const
01100 {
01101     assert(!wait_for_side_init_);
01102     return wb::has_actions();
01103 }
01104 
01105 bool manager::unit_has_actions(unit const* unit) const
01106 {
01107     assert(!wait_for_side_init_);
01108     return viewer_actions()->unit_has_actions(unit);
01109 }
01110 
01111 int manager::get_spent_gold_for(int side)
01112 {
01113     if(wait_for_side_init_)
01114         return 0;
01115 
01116     return resources::teams->at(side - 1).get_side_actions()->get_gold_spent();
01117 }
01118 
01119 void manager::clear_undo()
01120 {
01121     apply_shroud_changes(*resources::undo_stack, viewer_side());
01122     resources::undo_stack->clear();
01123     resources::redo_stack->clear();
01124 }
01125 
01126 void manager::options_dlg()
01127 {
01128     int v_side = viewer_side();
01129 
01130     int selection = 0;
01131 
01132     std::vector<team*> allies;
01133     std::vector<std::string> options;
01134     utils::string_map t_vars;
01135 
01136     options.push_back(_("SHOW ALL allies’ plans"));
01137     options.push_back(_("HIDE ALL allies’ plans"));
01138 
01139     //populate list of networked allies
01140     foreach(team &t, *resources::teams)
01141     {
01142         //Exclude enemies, AIs, and local players
01143         if(t.is_enemy(v_side) || !t.is_network())
01144             continue;
01145 
01146         allies.push_back(&t);
01147 
01148         t_vars["player"] = t.current_player();
01149         size_t t_index = t.side()-1;
01150         if(team_plans_hidden_[t_index])
01151             options.push_back(vgettext("Show plans for $player", t_vars));
01152         else
01153             options.push_back(vgettext("Hide plans for $player", t_vars));
01154     }
01155 
01156     gui2::tsimple_item_selector dlg("", _("Whiteboard Options"), options);
01157     dlg.show(resources::screen->video());
01158     selection = dlg.selected_index();
01159 
01160     if(selection == -1)
01161         return;
01162 
01163     switch(selection)
01164     {
01165     case 0:
01166         foreach(team* t, allies)
01167             team_plans_hidden_[t->side()-1]=false;
01168         break;
01169     case 1:
01170         foreach(team* t, allies)
01171             team_plans_hidden_[t->side()-1]=true;
01172         break;
01173     default:
01174         if(selection > 1)
01175         {
01176             size_t t_index = allies[selection-2]->side()-1;
01177             //toggle ...
01178             bool hidden = team_plans_hidden_[t_index];
01179             team_plans_hidden_[t_index] = !hidden;
01180         }
01181         break;
01182     }
01183     update_plan_hiding();
01184 }
01185 
01186 void manager::set_planned_unit_map()
01187 {
01188     if (!can_modify_game_state()) {
01189         LOG_WB << "Not building planned unit map: cannot modify game state now.\n";
01190         return;
01191     }
01192     //any more than one reference means a lock on unit map was requested
01193     if(!unit_map_lock_.unique()) {
01194         LOG_WB << "Not building planned unit map: unit map locked.\n";
01195         return;
01196     }
01197     if (planned_unit_map_active_) {
01198         WRN_WB << "Not building planned unit map: already set.\n";
01199         return;
01200     }
01201 
01202     validate_actions_if_needed();
01203     log_scope2("whiteboard", "Building planned unit map");
01204     mapbuilder_.reset(new mapbuilder(*resources::units));
01205     mapbuilder_->build_map();
01206 
01207     planned_unit_map_active_ = true;
01208 }
01209 
01210 void manager::set_real_unit_map()
01211 {
01212     if (planned_unit_map_active_)
01213     {
01214         assert(!executing_actions_);
01215         assert(!wait_for_side_init_);
01216         if(mapbuilder_)
01217         {
01218             log_scope2("whiteboard", "Restoring regular unit map.");
01219             mapbuilder_.reset();
01220         }
01221         planned_unit_map_active_ = false;
01222     }
01223     else
01224     {
01225         LOG_WB << "Not disabling planned unit map: already disabled.\n";
01226     }
01227 }
01228 
01229 void manager::validate_actions_if_needed()
01230 {
01231     if (gamestate_mutated_) {
01232         validate_viewer_actions();
01233     }
01234 }
01235 
01236 future_map::future_map():
01237         initial_planned_unit_map_(resources::whiteboard && resources::whiteboard->has_planned_unit_map())
01238 {
01239     if (!resources::whiteboard)
01240         return;
01241     if (!initial_planned_unit_map_)
01242         resources::whiteboard->set_planned_unit_map();
01243     // check if if unit map was successfully applied
01244     if (!resources::whiteboard->has_planned_unit_map()) {
01245         DBG_WB << "Scoped future unit map failed to apply.\n";
01246     }
01247 }
01248 
01249 future_map::~future_map()
01250 {
01251     if (!resources::whiteboard)
01252         return;
01253     if (!initial_planned_unit_map_ && resources::whiteboard->has_planned_unit_map())
01254         resources::whiteboard->set_real_unit_map();
01255 }
01256 
01257 future_map_if_active::future_map_if_active():
01258         initial_planned_unit_map_(resources::whiteboard && resources::whiteboard->has_planned_unit_map()),
01259         whiteboard_active_(resources::whiteboard && resources::whiteboard->is_active())
01260 {
01261     if (!resources::whiteboard)
01262         return;
01263     if (!whiteboard_active_)
01264         return;
01265     if (!initial_planned_unit_map_)
01266         resources::whiteboard->set_planned_unit_map();
01267     // check if if unit map was successfully applied
01268     if (!resources::whiteboard->has_planned_unit_map()) {
01269         DBG_WB << "Scoped future unit map failed to apply.\n";
01270     }
01271 }
01272 
01273 future_map_if_active::~future_map_if_active()
01274 {
01275     if (!resources::whiteboard)
01276         return;
01277     if (!initial_planned_unit_map_ && resources::whiteboard->has_planned_unit_map())
01278         resources::whiteboard->set_real_unit_map();
01279 }
01280 
01281 
01282 real_map::real_map():
01283         initial_planned_unit_map_(resources::whiteboard && resources::whiteboard->has_planned_unit_map()),
01284         unit_map_lock_(resources::whiteboard ? resources::whiteboard->unit_map_lock_ : boost::shared_ptr<bool>(new bool(false)))
01285 {
01286     if (!resources::whiteboard)
01287         return;
01288     if (initial_planned_unit_map_)
01289         resources::whiteboard->set_real_unit_map();
01290 }
01291 
01292 real_map::~real_map()
01293 {
01294     if (!resources::whiteboard)
01295         return;
01296     assert(!resources::whiteboard->has_planned_unit_map());
01297     if (initial_planned_unit_map_)
01298     {
01299         resources::whiteboard->set_planned_unit_map();
01300     }
01301 }
01302 
01303 } // end namespace wb
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:39 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs