game_events.cpp

Go to the documentation of this file.
00001 /* $Id: game_events.cpp 54154 2012-05-11 22:31:47Z jamit $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
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  * Processing of WML-events.
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 #include "actions.hpp"
00024 #include "ai/manager.hpp"
00025 #include "dialogs.hpp"
00026 #include "foreach.hpp"
00027 #include "game_display.hpp"
00028 #include "game_events.hpp"
00029 #include "game_preferences.hpp"
00030 #include "gettext.hpp"
00031 #include "gui/dialogs/gamestate_inspector.hpp"
00032 #include "gui/dialogs/transient_message.hpp"
00033 #include "gui/dialogs/wml_message.hpp"
00034 #include "gui/widgets/window.hpp"
00035 #include "help.hpp"
00036 #include "log.hpp"
00037 #include "map.hpp"
00038 #include "map_label.hpp"
00039 #include "map_exception.hpp"
00040 #include "pathfind/teleport.hpp"
00041 #include "replay.hpp"
00042 #include "reports.hpp"
00043 #include "resources.hpp"
00044 #include "scripting/lua.hpp"
00045 #include "side_filter.hpp"
00046 #include "sound.hpp"
00047 #include "soundsource.hpp"
00048 #include "terrain_filter.hpp"
00049 #include "unit_display.hpp"
00050 #include "unit_helper.hpp"
00051 #include "wml_exception.hpp"
00052 #include "play_controller.hpp"
00053 #include "persist_var.hpp"
00054 #include "whiteboard/manager.hpp"
00055 
00056 #include <boost/scoped_array.hpp>
00057 #include <boost/scoped_ptr.hpp>
00058 #include <boost/lexical_cast.hpp>
00059 #include <algorithm>
00060 #include <iomanip>
00061 #include <iostream>
00062 
00063 static lg::log_domain log_engine("engine");
00064 #define DBG_NG LOG_STREAM(debug, log_engine)
00065 #define LOG_NG LOG_STREAM(info, log_engine)
00066 #define WRN_NG LOG_STREAM(warn, log_engine)
00067 #define ERR_NG LOG_STREAM(err, log_engine)
00068 
00069 static lg::log_domain log_display("display");
00070 #define DBG_DP LOG_STREAM(debug, log_display)
00071 #define LOG_DP LOG_STREAM(info, log_display)
00072 
00073 static lg::log_domain log_wml("wml");
00074 #define DBG_WML LOG_STREAM(debug, log_wml)
00075 #define LOG_WML LOG_STREAM(info, log_wml)
00076 #define WRN_WML LOG_STREAM(warn, log_wml)
00077 #define ERR_WML LOG_STREAM(err, log_wml)
00078 
00079 static lg::log_domain log_config("config");
00080 #define ERR_CF LOG_STREAM(err, log_config)
00081 
00082 static lg::log_domain log_event_handler("event_handler");
00083 #define DBG_EH LOG_STREAM(debug, log_event_handler)
00084 
00085 /**
00086  * State when processing a flight of events or commands.
00087  */
00088 struct event_context
00089 {
00090     bool mutated;
00091     bool skip_messages;
00092     event_context(bool s): mutated(true), skip_messages(s) {}
00093 };
00094 
00095 static event_context default_context(false);
00096 static event_context *current_context = &default_context;
00097 
00098 /**
00099  * Context state with automatic lifetime handling.
00100  */
00101 struct scoped_context
00102 {
00103     event_context *old_context;
00104     event_context new_context;
00105 
00106     scoped_context()
00107         : old_context(current_context)
00108         , new_context(old_context != &default_context && old_context->skip_messages)
00109     {
00110         current_context = &new_context;
00111     }
00112 
00113     ~scoped_context()
00114     {
00115         old_context->mutated |= new_context.mutated;
00116         current_context = old_context;
00117     }
00118 };
00119 
00120 static bool screen_needs_rebuild;
00121 
00122 namespace {
00123 
00124     std::stringstream wml_messages_stream;
00125 
00126     bool manager_running = false;
00127     int floating_label = 0;
00128 
00129     typedef std::pair< std::string, config* > wmi_command_change;
00130     std::vector< wmi_command_change > wmi_command_changes;
00131 
00132     const gui::msecs prevent_misclick_duration = 10;
00133     const gui::msecs average_frame_time = 30;
00134 
00135     class pump_manager {
00136         public:
00137         pump_manager() :
00138             x1_(resources::state_of_game->get_variable("x1")),
00139             x2_(resources::state_of_game->get_variable("x2")),
00140             y1_(resources::state_of_game->get_variable("y1")),
00141             y2_(resources::state_of_game->get_variable("y2"))
00142         {
00143             ++instance_count;
00144         }
00145         ~pump_manager() {
00146             resources::state_of_game->get_variable("x1") = x1_;
00147             resources::state_of_game->get_variable("x2") = x2_;
00148             resources::state_of_game->get_variable("y1") = y1_;
00149             resources::state_of_game->get_variable("y2") = y2_;
00150             --instance_count;
00151         }
00152         static unsigned count() {
00153             return instance_count;
00154         }
00155         private:
00156         static unsigned instance_count;
00157         int x1_, x2_, y1_, y2_;
00158     };
00159     unsigned pump_manager::instance_count=0;
00160 
00161 } // end anonymous namespace (1)
00162 
00163 #ifdef _MSC_VER
00164 // std::getline might be broken in Visual Studio so show a warning
00165 #if _MSC_VER < 1300
00166 #ifndef GETLINE_PATCHED
00167 #pragma message("warning: the std::getline implementation in your compiler might be broken.")
00168 #pragma message(" http://support.microsoft.com/default.aspx?scid=kb;EN-US;q240015")
00169 #endif
00170 #endif
00171 #endif
00172 
00173 /**
00174  * Helper function which determines whether a wml_message text can
00175  * really be pushed into the wml_messages_stream, and does it.
00176  */
00177 static void put_wml_message(const std::string& logger, const std::string& message)
00178 {
00179     if (logger == "err" || logger == "error") {
00180         ERR_WML << message << "\n";
00181         wml_messages_stream << _("Error: ") << message << "\n";
00182     } else if (logger == "warn" || logger == "wrn" || logger == "warning") {
00183         WRN_WML << message << "\n";
00184         wml_messages_stream << _("Warning: ") << message << "\n";
00185     } else if ((logger == "debug" || logger == "dbg") && !lg::debug.dont_log(log_wml)) {
00186         DBG_WML << message << "\n";
00187         wml_messages_stream << _("Debug: ") << message << "\n";
00188     } else if (!lg::info.dont_log(log_wml)) {
00189         LOG_WML << message << "\n";
00190         wml_messages_stream << _("Info: ") << message << "\n";
00191     }
00192 }
00193 
00194 /**
00195  * Helper function for show_wml_errors(), which gathers
00196  * the messages from a stringstream.
00197  */
00198 static void fill_wml_messages_map(std::map<std::string, int>& msg_map, std::stringstream& source)
00199 {
00200     while(true) {
00201         std::string msg;
00202         std::getline(source, msg);
00203 
00204         if(source.eof()) {
00205             break;
00206         }
00207 
00208         if(msg == "") {
00209             continue;
00210         }
00211 
00212         if(msg_map.find(msg) == msg_map.end()) {
00213             msg_map[msg] = 1;
00214         } else {
00215             msg_map[msg]++;
00216         }
00217     }
00218     // Make sure the eof flag is cleared otherwise no new messages are shown
00219     source.clear();
00220 }
00221 
00222 /**
00223  * Shows a summary of the errors encountered in WML thusfar,
00224  * to avoid a lot of the same messages to be shown.
00225  * Identical messages are shown once, with (between braces)
00226  * the number of times that message was encountered.
00227  * The order in which the messages are shown does not need
00228  * to be the order in which these messages are encountered.
00229  * Messages are always written to std::cerr.
00230  */
00231 static void show_wml_errors()
00232 {
00233     // Get all unique messages in messages,
00234     // with the number of encounters for these messages
00235     std::map<std::string, int> messages;
00236     fill_wml_messages_map(messages, lg::wml_error);
00237 
00238     // Show the messages collected
00239     const std::string caption = "Invalid WML found";
00240     for(std::map<std::string, int>::const_iterator itor = messages.begin();
00241             itor != messages.end(); ++itor) {
00242 
00243         std::stringstream msg;
00244         msg << itor->first;
00245         if(itor->second > 1) {
00246             msg << " (" << itor->second << ")";
00247         }
00248 
00249         resources::screen->add_chat_message(time(NULL), caption, 0, msg.str(),
00250                 events::chat_handler::MESSAGE_PUBLIC, false);
00251         std::cerr << caption << ": " << msg.str() << '\n';
00252     }
00253 }
00254 
00255 static void show_wml_messages()
00256 {
00257     // Get all unique messages in messages,
00258     // with the number of encounters for these messages
00259     std::map<std::string, int> messages;
00260     fill_wml_messages_map(messages, wml_messages_stream);
00261 
00262     // Show the messages collected
00263     const std::string caption = "WML";
00264     for(std::map<std::string, int>::const_iterator itor = messages.begin();
00265             itor != messages.end(); ++itor) {
00266 
00267         std::stringstream msg;
00268         msg << itor->first;
00269         if(itor->second > 1) {
00270             msg << " (" << itor->second << ")";
00271         }
00272 
00273         resources::screen->add_chat_message(time(NULL), caption, 0, msg.str(),
00274                 events::chat_handler::MESSAGE_PUBLIC, false);
00275     }
00276 }
00277 
00278 typedef void (*wml_handler_function)(
00279     const game_events::queued_event &event_info, const vconfig &cfg);
00280 
00281 typedef std::map<std::string, wml_handler_function> static_wml_action_map;
00282 /** Map of the default action handlers known of the engine. */
00283 static static_wml_action_map static_wml_actions;
00284 
00285 /**
00286  * WML_HANDLER_FUNCTION macro handles auto registeration for wml handlers
00287  *
00288  * @param pname wml tag name
00289  * @param pei the variable name of game_events::queued_event object inside function
00290  * @param pcfg the variable name of config object inside function
00291  *
00292  * You are warned! This is evil macro magic!
00293  *
00294  * The following code registers a [foo] tag:
00295  * \code
00296  * // comment out unused parameters to prevent compiler warnings
00297  * WML_HANDLER_FUNCTION(foo, event_info, cfg)
00298  * {
00299  *    // code for foo
00300  * }
00301  * \endcode
00302  *
00303  * Generated code looks like this:
00304  * \code
00305  * void wml_action_foo(...);
00306  * struct wml_func_register_foo {
00307  *   wml_func_register_foo() {
00308  *     static_wml_actions["foo"] = &wml_func_foo;
00309  *   } wml_func_register_foo;
00310  * void wml_func_foo(...)
00311  * {
00312  *    // code for foo
00313  * }
00314  * \endcode
00315  */
00316 #define WML_HANDLER_FUNCTION(pname, pei, pcfg) \
00317     static void wml_func_##pname(const game_events::queued_event &pei, const vconfig &pcfg); \
00318     struct wml_func_register_##pname \
00319     { \
00320         wml_func_register_##pname() \
00321         { static_wml_actions[#pname] = &wml_func_##pname; } \
00322     }; \
00323     static wml_func_register_##pname wml_func_register_##pname##_aux;  \
00324     static void wml_func_##pname(const game_events::queued_event& pei, const vconfig& pcfg)
00325 
00326 namespace game_events {
00327 
00328     static bool matches_special_filter(const config &cfg, const vconfig& filter);
00329 
00330     static bool internal_conditional_passed(const vconfig& cond, bool& backwards_compat)
00331     {
00332         static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-99999");
00333 
00334         // If the if statement requires we have a certain unit,
00335         // then check for that.
00336         const vconfig::child_list& have_unit = cond.get_children("have_unit");
00337         backwards_compat = backwards_compat && have_unit.empty();
00338         for(vconfig::child_list::const_iterator u = have_unit.begin(); u != have_unit.end(); ++u) {
00339             if(resources::units == NULL)
00340                 return false;
00341             std::vector<std::pair<int,int> > counts = (*u).has_attribute("count")
00342                 ? utils::parse_ranges((*u)["count"]) : default_counts;
00343             int match_count = 0;
00344             foreach (const unit &i, *resources::units)
00345             {
00346                 if(i.hitpoints() > 0 && unit_matches_filter(i, *u)) {
00347                     ++match_count;
00348                     if(counts == default_counts) {
00349                         // by default a single match is enough, so avoid extra work
00350                         break;
00351                     }
00352                 }
00353             }
00354             if ((*u)["search_recall_list"].to_bool())
00355             {
00356                 for(std::vector<team>::iterator team = resources::teams->begin();
00357                         team!=resources::teams->end(); ++team)
00358                 {
00359                     if(counts == default_counts && match_count) {
00360                         break;
00361                     }
00362                     const std::vector<unit>& avail_units = team->recall_list();
00363                     for(std::vector<unit>::const_iterator unit = avail_units.begin(); unit!=avail_units.end(); ++unit) {
00364                         if(counts == default_counts && match_count) {
00365                             break;
00366                         }
00367                         scoped_recall_unit auto_store("this_unit", team->save_id(), unit - avail_units.begin());
00368                         if (unit_matches_filter(*unit, *u)) {
00369                             ++match_count;
00370                         }
00371                     }
00372                 }
00373             }
00374             if(!in_ranges(match_count, counts)) {
00375                 return false;
00376             }
00377         }
00378 
00379         // If the if statement requires we have a certain location,
00380         // then check for that.
00381         const vconfig::child_list& have_location = cond.get_children("have_location");
00382         backwards_compat = backwards_compat && have_location.empty();
00383         for(vconfig::child_list::const_iterator v = have_location.begin(); v != have_location.end(); ++v) {
00384             std::set<map_location> res;
00385             terrain_filter(*v, *resources::units).get_locations(res);
00386 
00387             std::vector<std::pair<int,int> > counts = (*v).has_attribute("count")
00388                 ? utils::parse_ranges((*v)["count"]) : default_counts;
00389             if(!in_ranges<int>(res.size(), counts)) {
00390                 return false;
00391             }
00392         }
00393 
00394         // Check against each variable statement,
00395         // to see if the variable matches the conditions or not.
00396         const vconfig::child_list& variables = cond.get_children("variable");
00397         backwards_compat = backwards_compat && variables.empty();
00398 
00399         foreach (const vconfig &values, variables)
00400         {
00401             const std::string name = values["name"];
00402             config::attribute_value value = resources::state_of_game->get_variable_const(name);
00403             std::string str_value = value.str();
00404             double num_value = value.to_double();
00405 
00406 #define TEST_ATTR(name, test) do { \
00407             if (values.has_attribute(name)) { \
00408                 config::attribute_value attr = values[name]; \
00409                 if (!(test)) return false; \
00410             } \
00411             } while (0)
00412 
00413 #define TEST_STR_ATTR(name, test) do { \
00414             if (values.has_attribute(name)) { \
00415                 std::string attr_str = values[name]; \
00416                 if (!(test)) return false; \
00417             } \
00418             } while (0)
00419 
00420 #define TEST_NUM_ATTR(name, test) do { \
00421             if (values.has_attribute(name)) { \
00422                 double attr_num = values[name].to_double(); \
00423                 if (!(test)) return false; \
00424             } \
00425             } while (0)
00426 
00427             TEST_STR_ATTR("equals",                str_value == attr_str);
00428             TEST_STR_ATTR("not_equals",            str_value != attr_str);
00429             TEST_NUM_ATTR("numerical_equals",      num_value == attr_num);
00430             TEST_NUM_ATTR("numerical_not_equals",  num_value != attr_num);
00431             TEST_NUM_ATTR("greater_than",          num_value >  attr_num);
00432             TEST_NUM_ATTR("less_than",             num_value <  attr_num);
00433             TEST_NUM_ATTR("greater_than_equal_to", num_value >= attr_num);
00434             TEST_NUM_ATTR("less_than_equal_to",    num_value <= attr_num);
00435             TEST_ATTR("boolean_equals",     value.to_bool() == attr.to_bool());
00436             TEST_ATTR("boolean_not_equals", value.to_bool() != attr.to_bool());
00437             TEST_STR_ATTR("contains", value.str().find(attr_str) != std::string::npos);
00438 
00439 #undef TEST_ATTR
00440 #undef TEST_STR_ATTR
00441 #undef TEST_NUM_ATTR
00442         }
00443         return true;
00444     }
00445 
00446     bool conditional_passed(const vconfig& cond, bool backwards_compat)
00447     {
00448         bool allow_backwards_compat = backwards_compat = backwards_compat &&
00449             cond["backwards_compat"].to_bool(true);
00450         bool matches = internal_conditional_passed(cond, allow_backwards_compat);
00451 
00452         // Handle [and], [or], and [not] with in-order precedence
00453         int or_count = 0;
00454         vconfig::all_children_iterator cond_i = cond.ordered_begin();
00455         vconfig::all_children_iterator cond_end = cond.ordered_end();
00456         while(cond_i != cond_end)
00457         {
00458             const std::string& cond_name = cond_i.get_key();
00459             const vconfig& cond_filter = cond_i.get_child();
00460 
00461             // Handle [and]
00462             if(cond_name == "and")
00463             {
00464                 matches = matches && conditional_passed(cond_filter, backwards_compat);
00465                 backwards_compat = false;
00466             }
00467             // Handle [or]
00468             else if(cond_name == "or")
00469             {
00470                 matches = matches || conditional_passed(cond_filter, backwards_compat);
00471                 ++or_count;
00472             }
00473             // Handle [not]
00474             else if(cond_name == "not")
00475             {
00476                 matches = matches && !conditional_passed(cond_filter, backwards_compat);
00477                 backwards_compat = false;
00478             }
00479             ++cond_i;
00480         }
00481         // Check for deprecated [or] syntax
00482         if(matches && or_count > 1 && allow_backwards_compat)
00483         {
00484             ///@deprecated r18803 [or] syntax
00485             lg::wml_error << "possible deprecated [or] syntax: now forcing re-interpretation\n";
00486             /**
00487              * @todo For now we will re-interpret it according to the old
00488              * rules, but this should be later to prevent re-interpretation
00489              * errors.
00490              */
00491             const vconfig::child_list& orcfgs = cond.get_children("or");
00492             for(unsigned int i=0; i < orcfgs.size(); ++i) {
00493                 if(conditional_passed(orcfgs[i])) {
00494                     return true;
00495                 }
00496             }
00497             return false;
00498         }
00499         return matches;
00500     }
00501 
00502     void handle_wml_log_message(const config& cfg)
00503     {
00504         const std::string& logger = cfg["logger"];
00505         const std::string& msg = cfg["message"];
00506 
00507         put_wml_message(logger,msg);
00508     }
00509 
00510     void handle_deprecated_message(const config& cfg)
00511     {
00512         // Note: no need to translate the string, since only used for deprecated things.
00513         const std::string& message = cfg["message"];
00514         lg::wml_error << message << '\n';
00515     }
00516 
00517     std::vector<int> get_sides_vector(const vconfig& cfg)
00518     {
00519         const config::attribute_value sides = cfg["side"];
00520         const vconfig &ssf = cfg.child("filter_side");
00521 
00522         if (!ssf.null()) {
00523             if(!sides.empty()) { WRN_NG << "ignoring duplicate side filter information (inline side=)\n"; }
00524             side_filter filter(ssf);
00525             return filter.get_teams();
00526         }
00527 
00528         side_filter filter(sides.str());
00529         return filter.get_teams();
00530     }
00531 
00532 } // end namespace game_events (1)
00533 
00534 namespace {
00535 
00536     std::set<std::string> used_items;
00537 
00538 } // end anonymous namespace (2)
00539 
00540 static bool events_init() { return resources::screen != 0; }
00541 
00542 namespace {
00543 
00544     std::deque<game_events::queued_event> events_queue;
00545 
00546 
00547 } // end anonymous namespace (3)
00548 
00549 static map_location cfg_to_loc(const vconfig& cfg,int defaultx = 0, int defaulty = 0)
00550 {
00551     int x = cfg["x"].to_int(defaultx) - 1;
00552     int y = cfg["y"].to_int(defaulty) - 1;
00553 
00554     return map_location(x, y);
00555 }
00556 
00557 namespace {
00558 
00559     class t_event_handlers {
00560         typedef std::vector<game_events::event_handler> t_active;
00561     public:
00562         typedef t_active::iterator iterator;
00563         typedef t_active::const_iterator const_iterator;
00564     private:
00565 
00566         t_active active_; ///Active event handlers
00567         t_active insert_buffer_; ///Event handlers added while pumping events
00568         std::set<std::string> remove_buffer_; ///Event handlers removed while pumping events
00569         bool buffering_;
00570 
00571 
00572         void log_handler(std::stringstream& ss,
00573             const std::vector<game_events::event_handler>& handlers,
00574             const std::string& msg) {
00575 
00576             foreach(const game_events::event_handler& h, handlers){
00577                 const config& cfg = h.get_config();
00578                 ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
00579             }
00580             DBG_EH << msg << " handlers are now " << ss.str() << "\n";
00581             ss.str(std::string());
00582         }
00583 
00584         void log_handlers() {
00585             if(lg::debug.dont_log("event_handler")) return;
00586 
00587             std::stringstream ss;
00588             log_handler(ss, active_, "active");
00589             log_handler(ss, insert_buffer_, "insert buffered");
00590             foreach(const std::string& h, remove_buffer_){
00591                 ss << "id=" << h << "; ";
00592             }
00593             DBG_EH << "remove buffered handlers are now " << ss.str() << "\n";
00594         }
00595 
00596     public:
00597 
00598         t_event_handlers()
00599             : active_() , insert_buffer_() , remove_buffer_() , buffering_(false) { }
00600 
00601         /**
00602          * Adds an event handler.  An event with a nonempty ID will not
00603          * be added if an event with that ID already exists.  This method
00604          * respects this class's buffering functionality.
00605          */
00606         void add_event_handler(game_events::event_handler const & new_handler) {
00607             if(buffering_) {
00608                 DBG_EH << "buffering event handler for name=" << new_handler.get_config()["name"] <<
00609                 " with id " << new_handler.get_config()["id"] << "\n";
00610                 insert_buffer_.push_back(new_handler);
00611                 log_handlers();
00612             }
00613 
00614             else {
00615                 const config & cfg = new_handler.get_config();
00616                 std::string id = cfg["id"];
00617                 if(!id.empty()) {
00618                     foreach( game_events::event_handler const & eh, active_) {
00619                         config const & temp_config( eh.get_config());
00620                         if(id == temp_config["id"]) {
00621                             DBG_EH << "ignoring event handler for name=" << cfg["name"] <<
00622                                 " with id " << id << "\n";
00623                             return;
00624                         }
00625                     }
00626                 }
00627                 DBG_EH << "inserting event handler for name=" << cfg["name"] <<
00628                     " with id=" << id << "\n";
00629                 active_.push_back(new_handler);
00630                 log_handlers();
00631             }
00632         }
00633 
00634         /**
00635          * Removes an event handler, identified by its ID.  Events with
00636          * empty IDs cannot be removed.  This method respects this class's
00637          * buffering functionality.
00638          */
00639         void remove_event_handler(std::string const & id) {
00640             if(id == "") { return; }
00641 
00642             DBG_EH << "removing event handler with id " << id << "\n";
00643 
00644             if(buffering_) { remove_buffer_.insert(id); }
00645 
00646             t_active &temp = buffering_ ? insert_buffer_ : active_;
00647 
00648             t_active::iterator i = temp.begin();
00649             while(i < temp.end()) {
00650                 config const & temp_config = (*i).get_config();
00651                 std::string event_id = temp_config["id"];
00652                 if(event_id != "" && event_id == id) {
00653                     i = temp.erase(i); }
00654                 else {
00655                     ++i; }
00656             }
00657             log_handlers();
00658         }
00659 
00660         /**
00661          * Starts buffering.  While buffering, any calls to add_event_handler
00662          * and remove_event_handler will not take effect until commit_buffer
00663          * is called.  This function is idempotent - starting a buffer
00664          * when already buffering will not start a second buffer.
00665          */
00666         void start_buffering() {
00667             buffering_ = true;
00668             DBG_EH << "starting buffering...\n";
00669         }
00670 
00671         void stop_buffering() {
00672             DBG_EH << "stopping buffering...\n";
00673             buffering_ = false;
00674         }
00675 
00676         /**
00677          * Commits all buffered events
00678          */
00679         void commit_buffer() {
00680             DBG_EH << "committing buffered event handlers, buffering: " << buffering_ << "\n";
00681             if(buffering_)
00682                 return;
00683 
00684             // Commit any event removals
00685             foreach(std::string const & i ,  remove_buffer_ ){
00686                 remove_event_handler( i ); }
00687             remove_buffer_.clear();
00688 
00689             // Commit any spawned events-within-events
00690             foreach( game_events::event_handler const & i ,  insert_buffer_ ){
00691                 add_event_handler( i ); }
00692             insert_buffer_.clear();
00693 
00694             log_handlers();
00695         }
00696 
00697         void clear(){
00698             active_.clear();
00699             insert_buffer_.clear();
00700             remove_buffer_.clear();
00701             buffering_ = false;
00702         }
00703 
00704 
00705         iterator begin() { return active_.begin(); }
00706         const_iterator begin() const { return active_.begin(); }
00707 
00708         iterator end() { return active_.end(); }
00709         const_iterator end() const { return active_.end(); }
00710 
00711     };
00712 
00713     t_event_handlers event_handlers;
00714 
00715 } // end anonymous namespace (4)
00716 
00717 static void toggle_shroud(const bool remove, const vconfig& cfg)
00718 {
00719     // Filter the sides.
00720     std::vector<int> sides = game_events::get_sides_vector(cfg);
00721     size_t index;
00722 
00723     // Filter the locations.
00724     std::set<map_location> locs;
00725     const terrain_filter filter(cfg, *resources::units);
00726     filter.get_locations(locs, true);
00727 
00728     foreach (const int &side_num, sides)
00729     {
00730         index = side_num - 1;
00731         team &t = (*resources::teams)[index];
00732 
00733         foreach (map_location const &loc, locs)
00734         {
00735             if (remove) {
00736                 t.clear_shroud(loc);
00737             } else {
00738                 t.place_shroud(loc);
00739             }
00740         }
00741     }
00742 
00743     resources::screen->labels().recalculate_shroud();
00744     resources::screen->recalculate_minimap();
00745     resources::screen->invalidate_all();
00746 }
00747 
00748 WML_HANDLER_FUNCTION(remove_shroud, /*event_info*/, cfg)
00749 {
00750     toggle_shroud(true,cfg);
00751 }
00752 
00753 WML_HANDLER_FUNCTION(place_shroud, /*event_info*/,cfg)
00754 {
00755     toggle_shroud(false,cfg );
00756 }
00757 
00758 /* Implements the lifting and resetting of fog via WML.
00759  * Keeping affect_normal_fog as false causes only the fog override to be affected.
00760  * Otherwise, fog lifting will be implemented similar to normal sight (cannot be
00761  * individually reset and ends at the end of the turn), and fog resetting will, in
00762  * addition to removing overrides, extend the specified teams' normal fog to all
00763  * hexes.
00764  */
00765 static void toggle_fog(const bool clear, const vconfig& cfg, const bool affect_normal_fog=false)
00766 {
00767     // Filter the sides.
00768     const vconfig &ssf = cfg.child("filter_side");
00769     const side_filter s_filter(ssf.null() ? vconfig::empty_vconfig() : ssf);
00770     const std::vector<int> sides = s_filter.get_teams();
00771 
00772     // Filter the locations.
00773     std::set<map_location> locs;
00774     const terrain_filter t_filter(cfg, *resources::units);
00775     t_filter.get_locations(locs, true);
00776 
00777     // Loop through sides.
00778     foreach (const int &side_num, sides)
00779     {
00780         team &t = (*resources::teams)[side_num-1];
00781         if ( !clear )
00782         {
00783             // Extend fog.
00784             t.remove_fog_override(locs);
00785             if ( affect_normal_fog )
00786                 t.refog();
00787         }
00788         else if ( !affect_normal_fog )
00789             // Force the locations clear of fog.
00790             t.add_fog_override(locs);
00791         else
00792             // Simply clear fog from the locations.
00793             foreach (const map_location &hex, locs)
00794                 t.clear_fog(hex);
00795     }
00796 
00797     // Flag a screen update.
00798     resources::screen->recalculate_minimap();
00799     resources::screen->invalidate_all();
00800 }
00801 
00802 WML_HANDLER_FUNCTION(lift_fog, /*event_info*/, cfg)
00803 {
00804     toggle_fog(true, cfg, !cfg["multiturn"].to_bool(false));
00805 }
00806 
00807 WML_HANDLER_FUNCTION(reset_fog, /*event_info*/, cfg)
00808 {
00809     toggle_fog(false, cfg, cfg["reset_view"].to_bool(false));
00810 }
00811 
00812 WML_HANDLER_FUNCTION(tunnel, /*event_info*/, cfg)
00813 {
00814     const bool remove = cfg["remove"].to_bool(false);
00815     if (remove) {
00816         const std::vector<std::string> ids = utils::split(cfg["id"]);
00817         foreach(const std::string &id, ids) {
00818             resources::tunnels->remove(id);
00819         }
00820     } else if (cfg.get_children("source").empty() ||
00821         cfg.get_children("target").empty() ||
00822         cfg.get_children("filter").empty()) {
00823         ERR_WML << "[tunnel] is missing a mandatory tag:\n"
00824              << cfg.get_config().debug();
00825     } else {
00826         pathfind::teleport_group tunnel(cfg, false);
00827         resources::tunnels->add(tunnel);
00828 
00829         if(cfg["bidirectional"].to_bool(true)) {
00830             tunnel = pathfind::teleport_group(cfg, true);
00831             resources::tunnels->add(tunnel);
00832         }
00833     }
00834 }
00835 
00836 WML_HANDLER_FUNCTION(teleport, event_info, cfg)
00837 {
00838     unit_map::iterator u = resources::units->find(event_info.loc1);
00839 
00840     // Search for a valid unit filter, and if we have one, look for the matching unit
00841     const vconfig filter = cfg.child("filter");
00842     if(!filter.null()) {
00843         for (u = resources::units->begin(); u != resources::units->end(); ++u){
00844             if(game_events::unit_matches_filter(*u, filter))
00845                 break;
00846         }
00847     }
00848 
00849     if (u == resources::units->end()) return;
00850 
00851     // We have found a unit that matches the filter
00852     const map_location dst = cfg_to_loc(cfg);
00853     if (dst == u->get_location() || !resources::game_map->on_board(dst)) return;
00854 
00855     const unit* pass_check = NULL;
00856     //@deprecated ignore_passability 1.9.10
00857     const config::attribute_value ignore_passability = cfg["ignore_passability"];
00858     if (!ignore_passability.blank()) {
00859         WRN_NG << "[teleport]ignore_passability= is deprecated, use check_passability=\n";
00860             if (!ignore_passability.to_bool(false))
00861                 pass_check = &*u;
00862     }
00863     else if (cfg["check_passability"].to_bool(true))
00864         pass_check = &*u;
00865     const map_location vacant_dst = find_vacant_tile(*resources::game_map, *resources::units, dst, pathfind::VACANT_ANY, pass_check);
00866     if (!resources::game_map->on_board(vacant_dst)) return;
00867 
00868     int side = u->side();
00869     if (cfg["clear_shroud"].to_bool(true)) {
00870         clear_shroud(side);
00871     }
00872 
00873     map_location src_loc = u->get_location();
00874 
00875     std::vector<map_location> teleport_path;
00876     teleport_path.push_back(src_loc);
00877     teleport_path.push_back(vacant_dst);
00878     bool animate = cfg["animate"].to_bool();
00879     unit_display::move_unit(teleport_path, *u, *resources::teams, animate);
00880 
00881     resources::units->move(src_loc, vacant_dst);
00882     unit::clear_status_caches();
00883 
00884     u = resources::units->find(vacant_dst);
00885     u->set_standing();
00886 
00887     if (resources::game_map->is_village(vacant_dst)) {
00888         get_village(vacant_dst, side);
00889     }
00890 
00891     resources::screen->invalidate_unit_after_move(src_loc, dst);
00892 
00893     resources::screen->draw();
00894 }
00895 
00896 WML_HANDLER_FUNCTION(volume, /*event_info*/, cfg)
00897 {
00898 
00899     int vol;
00900     float rel;
00901     std::string music = cfg["music"];
00902     std::string sound = cfg["sound"];
00903 
00904     if(!music.empty()) {
00905         vol = preferences::music_volume();
00906         rel = atof(music.c_str());
00907         if (rel >= 0.0f && rel < 100.0f) {
00908             vol = static_cast<int>(rel*vol/100.0f);
00909         }
00910         sound::set_music_volume(vol);
00911     }
00912 
00913     if(!sound.empty()) {
00914         vol = preferences::sound_volume();
00915         rel = atof(sound.c_str());
00916         if (rel >= 0.0f && rel < 100.0f) {
00917             vol = static_cast<int>(rel*vol/100.0f);
00918         }
00919         sound::set_sound_volume(vol);
00920     }
00921 
00922 }
00923 
00924 static void color_adjust(const vconfig& cfg) {
00925     game_display &screen = *resources::screen;
00926     screen.adjust_color_overlay(cfg["red"], cfg["green"], cfg["blue"]);
00927     screen.invalidate_all();
00928     screen.draw(true,true);
00929 }
00930 
00931 WML_HANDLER_FUNCTION(color_adjust, /*event_info*/, cfg)
00932 {
00933     color_adjust(cfg);
00934 }
00935 
00936 WML_HANDLER_FUNCTION(scroll, /*event_info*/, cfg)
00937 {
00938     game_display &screen = *resources::screen;
00939     screen.scroll(cfg["x"], cfg["y"]);
00940     screen.draw(true,true);
00941 }
00942 
00943 // store time of day config in a WML variable; useful for those who
00944 // are too lazy to calculate the corresponding time of day for a given turn,
00945 // or if the turn / time-of-day sequence mutates in a scenario.
00946 WML_HANDLER_FUNCTION(store_time_of_day, /*event_info*/, cfg)
00947 {
00948     const map_location loc = cfg_to_loc(cfg, -999, -999);
00949     int turn = cfg["turn"];
00950     // using 0 will use the current turn
00951     const time_of_day& tod = resources::tod_manager->get_time_of_day(loc,turn);
00952 
00953     std::string variable = cfg["variable"];
00954     if(variable.empty()) {
00955         variable = "time_of_day";
00956     }
00957 
00958     variable_info store(variable, true, variable_info::TYPE_CONTAINER);
00959 
00960     config tod_cfg;
00961     tod.write(tod_cfg);
00962 
00963     (*store.vars).add_child(store.key, tod_cfg);
00964 }
00965 
00966 WML_HANDLER_FUNCTION(inspect, /*event_info*/, cfg)
00967 {
00968     gui2::tgamestate_inspector inspect_dialog(cfg);
00969     inspect_dialog.show(resources::screen->video());
00970 }
00971 
00972 WML_HANDLER_FUNCTION(modify_ai, /*event_info*/, cfg)
00973 {
00974     const vconfig& filter_side = cfg.child("filter_side");
00975     std::vector<int> sides;
00976     if(!filter_side.null()) {
00977         WRN_NG << "[modify_ai][filter_side] is deprecated, use only an inline SSF\n";
00978         if(!cfg["side"].str().empty()) {
00979             ERR_NG << "duplicate side information in [modify_ai]\n";
00980             return;
00981         }
00982         side_filter ssf(filter_side);
00983         sides = ssf.get_teams();
00984     } else {
00985         side_filter ssf(cfg);
00986         sides = ssf.get_teams();
00987     }
00988     foreach (const int &side_num, sides)
00989     {
00990         ai::manager::modify_active_ai_for_side(side_num,cfg.get_parsed_config());
00991     }
00992 }
00993 
00994 WML_HANDLER_FUNCTION(modify_side, /*event_info*/, cfg)
00995 {
00996     std::vector<team> &teams = *resources::teams;
00997 
00998     bool invalidate_screen = false;
00999 
01000     std::string team_name = cfg["team_name"];
01001     std::string user_team_name = cfg["user_team_name"];
01002     std::string controller = cfg["controller"];
01003     std::string recruit_str = cfg["recruit"];
01004     std::string shroud_data = cfg["shroud_data"];
01005     std::string village_support = cfg["village_support"];
01006     const config& parsed = cfg.get_parsed_config();
01007     const config::const_child_itors &ai = parsed.child_range("ai");
01008     std::string switch_ai = cfg["switch_ai"];
01009 
01010     std::vector<int> sides = game_events::get_sides_vector(cfg);
01011     size_t team_index;
01012 
01013     foreach (const int &side_num, sides)
01014     {
01015         team_index = side_num - 1;
01016         LOG_NG << "modifying side: " << side_num << "\n";
01017         if(!team_name.empty()) {
01018             LOG_NG << "change side's team to team_name '" << team_name << "'\n";
01019             teams[team_index].change_team(team_name,
01020                     user_team_name);
01021         } else if(!user_team_name.empty()) {
01022             LOG_NG << "change side's user_team_name to '" << user_team_name << "'\n";
01023             teams[team_index].change_team(teams[team_index].team_name(),
01024                     user_team_name);
01025         }
01026         // Modify recruit list (override)
01027         if (!recruit_str.empty()) {
01028             std::vector<std::string> recruit = utils::split(recruit_str);
01029             if (recruit.size() == 1 && recruit.back() == "")
01030                 recruit.clear();
01031 
01032             teams[team_index].set_recruits(std::set<std::string>(recruit.begin(),recruit.end()));
01033         }
01034         // Modify income
01035         config::attribute_value income = cfg["income"];
01036         if (!income.empty()) {
01037             teams[team_index].set_base_income(income.to_int() + game_config::base_income);
01038         }
01039         // Modify total gold
01040         config::attribute_value gold = cfg["gold"];
01041         if (!gold.empty()) {
01042             teams[team_index].set_gold(gold);
01043         }
01044         // Set controller
01045         if (!controller.empty()) {
01046             teams[team_index].change_controller(controller);
01047         }
01048         // Set shroud
01049         config::attribute_value shroud = cfg["shroud"];
01050         if (!shroud.empty()) {
01051             teams[team_index].set_shroud(shroud.to_bool(true));
01052             invalidate_screen = true;
01053         }
01054         // Reset shroud
01055         if ( cfg["reset_maps"].to_bool(false) ) {
01056             teams[team_index].reshroud();
01057             invalidate_screen = true;
01058         }
01059         // Merge shroud data
01060         if (!shroud_data.empty()) {
01061             teams[team_index].merge_shroud_map_data(shroud_data);
01062             invalidate_screen = true;
01063         }
01064         // Set whether team is hidden in status table
01065         config::attribute_value hidden = cfg["hidden"];
01066         if (!hidden.empty()) {
01067             teams[team_index].set_hidden(hidden.to_bool(true));
01068         }
01069         // Set fog
01070         config::attribute_value fog = cfg["fog"];
01071         if (!fog.empty()) {
01072             teams[team_index].set_fog(fog.to_bool(true));
01073             invalidate_screen = true;
01074         }
01075         // Reset fog
01076         if ( cfg["reset_view"].to_bool(false) ) {
01077             teams[team_index].refog();
01078             invalidate_screen = true;
01079         }
01080         // Set income per village
01081         config::attribute_value village_gold = cfg["village_gold"];
01082         if (!village_gold.empty()) {
01083             teams[team_index].set_village_gold(village_gold);
01084         }
01085         // Set support (unit levels supported per village, for upkeep purposes)
01086         if (!village_support.empty()) {
01087             teams[team_index].set_village_support(lexical_cast_default<int>(village_support, game_config::village_support));
01088         }
01089         // Redeploy ai from location (this ignores current AI parameters)
01090         if (!switch_ai.empty()) {
01091             ai::manager::add_ai_for_side_from_file(side_num,switch_ai,true);
01092         }
01093         // Override AI parameters
01094         if (ai.first != ai.second) {
01095             ai::manager::modify_active_ai_config_old_for_side(side_num,ai);
01096         }
01097         // Change team color
01098         config::attribute_value color = cfg["color"];
01099         if(!color.empty()) {
01100             teams[team_index].set_color(color);
01101             invalidate_screen = true;
01102         }
01103         // Add shared view to current team
01104         config::attribute_value share_view = cfg["share_view"];
01105         if (!share_view.empty()){
01106             teams[team_index].set_share_view(share_view.to_bool(true));
01107             team::clear_caches();
01108             invalidate_screen = true;
01109         }
01110         // Add shared maps to current team
01111         // IMPORTANT: this MUST happen *after* share_view is changed
01112         config::attribute_value share_maps = cfg["share_maps"];
01113         if (!share_maps.empty()){
01114             teams[team_index].set_share_maps(share_maps.to_bool(true));
01115             team::clear_caches();
01116             invalidate_screen = true;
01117         }
01118 
01119     }
01120 
01121     // Flag an update of the screen, if needed.
01122     if ( invalidate_screen ) {
01123         resources::screen->recalculate_minimap();
01124         resources::screen->invalidate_all();
01125     }
01126 }
01127 
01128 WML_HANDLER_FUNCTION(modify_turns, /*event_info*/, cfg)
01129 {
01130     config::attribute_value value = cfg["value"];
01131     std::string add = cfg["add"];
01132     config::attribute_value current = cfg["current"];
01133     tod_manager& tod_man = *resources::tod_manager;
01134     if(!add.empty()) {
01135         tod_man.modify_turns(add);
01136     } else if(!value.empty()) {
01137         tod_man.set_number_of_turns(value.to_int(-1));
01138     }
01139     // change current turn only after applying mods
01140     if(!current.empty()) {
01141         const unsigned int current_turn_number = tod_man.turn();
01142         int new_turn_number = current.to_int(current_turn_number);
01143         const unsigned int new_turn_number_u = static_cast<unsigned int>(new_turn_number);
01144         if(new_turn_number_u < 1 || (new_turn_number > tod_man.number_of_turns() && tod_man.number_of_turns() != -1)) {
01145             ERR_NG << "attempted to change current turn number to one out of range (" << new_turn_number << ")\n";
01146         } else if(new_turn_number_u != current_turn_number) {
01147             tod_man.set_turn(new_turn_number_u);
01148             resources::screen->new_turn();
01149         }
01150     }
01151 }
01152 
01153 namespace {
01154 
01155 game_display::fake_unit *create_fake_unit(const vconfig& cfg)
01156 {
01157     std::string type = cfg["type"];
01158     std::string variation = cfg["variation"];
01159     std::string img_mods = cfg["image_mods"];
01160 
01161     size_t side_num = cfg["side"].to_int(1) - 1;
01162     if (side_num >= resources::teams->size()) side_num = 0;
01163 
01164     unit_race::GENDER gender = string_gender(cfg["gender"]);
01165     const unit_type *ut = unit_types.find(type);
01166     if (!ut) return NULL;
01167     game_display::fake_unit * fake_unit = new game_display::fake_unit(unit(ut, side_num + 1, false, gender));
01168 
01169     if(!variation.empty()) {
01170         config mod;
01171         config &effect = mod.add_child("effect");
01172         effect["apply_to"] = "variation";
01173         effect["name"] = variation;
01174         fake_unit->add_modification("variation",mod);
01175     }
01176 
01177     if(!img_mods.empty()) {
01178         config mod;
01179         config &effect = mod.add_child("effect");
01180         effect["apply_to"] = "image_mod";
01181         effect["add"] = img_mods;
01182         fake_unit->add_modification("image_mod",mod);
01183     }
01184 
01185     return fake_unit;
01186 }
01187 
01188 std::vector<map_location> fake_unit_path(const unit& fake_unit, const std::vector<std::string>& xvals, const std::vector<std::string>& yvals)
01189 {
01190     gamemap *game_map = resources::game_map;
01191     std::vector<map_location> path;
01192     map_location src;
01193     map_location dst;
01194     for(size_t i = 0; i != std::min(xvals.size(),yvals.size()); ++i) {
01195         if(i==0){
01196             src.x = atoi(xvals[i].c_str())-1;
01197             src.y = atoi(yvals[i].c_str())-1;
01198             if (!game_map->on_board(src)) {
01199                 ERR_CF << "invalid move_unit_fake source: " << src << '\n';
01200                 break;
01201             }
01202             path.push_back(src);
01203             continue;
01204         }
01205         pathfind::shortest_path_calculator calc(fake_unit,
01206                 (*resources::teams)[fake_unit.side()-1],
01207                 *resources::units,
01208                 *resources::teams,
01209                 *game_map);
01210 
01211         dst.x = atoi(xvals[i].c_str())-1;
01212         dst.y = atoi(yvals[i].c_str())-1;
01213         if (!game_map->on_board(dst)) {
01214             ERR_CF << "invalid move_unit_fake destination: " << dst << '\n';
01215             break;
01216         }
01217 
01218         pathfind::plain_route route = pathfind::a_star_search(src, dst, 10000, &calc,
01219             game_map->w(), game_map->h());
01220 
01221         if (route.steps.empty()) {
01222             WRN_NG << "Could not find move_unit_fake route from " << src << " to " << dst << ": ignoring complexities\n";
01223             pathfind::emergency_path_calculator calc(fake_unit, *game_map);
01224 
01225             route = pathfind::a_star_search(src, dst, 10000, &calc,
01226                     game_map->w(), game_map->h());
01227             if(route.steps.empty()) {
01228                 // This would occur when trying to do a MUF of a unit
01229                 // over locations which are unreachable to it (infinite movement
01230                 // costs). This really cannot fail.
01231                 WRN_NG << "Could not find move_unit_fake route from " << src << " to " << dst << ": ignoring terrain\n";
01232                 pathfind::dummy_path_calculator calc(fake_unit, *game_map);
01233                 route = a_star_search(src, dst, 10000, &calc, game_map->w(), game_map->h());
01234                 assert(!route.steps.empty());
01235             }
01236         }
01237         // we add this section to the end of the complete path
01238         // skipping section's head because already included
01239         // by the previous iteration
01240         path.insert(path.end(),
01241                 route.steps.begin()+1, route.steps.end());
01242 
01243         src = dst;
01244     }
01245     return path;
01246 }
01247 
01248 } //end of anonymous namespace
01249 
01250 // Moving a 'unit' - i.e. a dummy unit
01251 // that is just moving for the visual effect
01252 WML_HANDLER_FUNCTION(move_unit_fake, /*event_info*/, cfg)
01253 {
01254     util::unique_ptr<unit> dummy_unit(create_fake_unit(cfg));
01255     if(!dummy_unit.get())
01256         return;
01257 
01258     const std::string x = cfg["x"];
01259     const std::string y = cfg["y"];
01260 
01261     const std::vector<std::string> xvals = utils::split(x);
01262     const std::vector<std::string> yvals = utils::split(y);
01263 
01264     const std::vector<map_location>& path = fake_unit_path(*dummy_unit, xvals, yvals);
01265     if (!path.empty())
01266         unit_display::move_unit(path, *dummy_unit, *resources::teams);
01267 }
01268 
01269 WML_HANDLER_FUNCTION(move_units_fake, /*event_info*/, cfg)
01270 {
01271     LOG_NG << "Processing [move_units_fake]\n";
01272 
01273     const vconfig::child_list unit_cfgs = cfg.get_children("fake_unit");
01274     size_t num_units = unit_cfgs.size();
01275     boost::scoped_array<util::unique_ptr<game_display::fake_unit> > units(
01276         new util::unique_ptr<game_display::fake_unit>[num_units]);
01277     std::vector<std::vector<map_location> > paths;
01278     paths.reserve(num_units);
01279     game_display* disp = game_display::get_singleton();
01280 
01281     LOG_NG << "Moving " << num_units << " units\n";
01282 
01283     size_t longest_path = 0;
01284 
01285     foreach(const vconfig& config, unit_cfgs) {
01286         const std::vector<std::string> xvals = utils::split(config["x"]);
01287         const std::vector<std::string> yvals = utils::split(config["y"]);
01288         int skip_steps = config["skip_steps"];
01289         game_display::fake_unit *u = create_fake_unit(config);
01290         units[paths.size()].reset(u);
01291         paths.push_back(fake_unit_path(*u, xvals, yvals));
01292         if(skip_steps > 0)
01293             paths.back().insert(paths.back().begin(), skip_steps, paths.back().front());
01294         longest_path = std::max(longest_path, paths.back().size());
01295         DBG_NG << "Path " << paths.size() - 1 << " has length " << paths.back().size() << '\n';
01296 
01297         u->set_location(paths.back().front());
01298         u->place_on_game_display(disp);
01299     }
01300 
01301     LOG_NG << "Units placed, longest path is " << longest_path << " long\n";
01302 
01303     std::vector<map_location> path_step(2);
01304     path_step.resize(2);
01305     for(size_t step = 1; step < longest_path; ++step) {
01306         DBG_NG << "Doing step " << step << "...\n";
01307         for(size_t un = 0; un < num_units; ++un) {
01308             if(step >= paths[un].size() || paths[un][step - 1] == paths[un][step])
01309                 continue;
01310             DBG_NG << "Moving unit " << un << ", doing step " << step << '\n';
01311             path_step[0] = paths[un][step - 1];
01312             path_step[1] = paths[un][step];
01313             unit_display::move_unit(path_step, *units[un], *resources::teams);
01314             units[un]->set_location(path_step[1]);
01315             units[un]->set_standing();
01316         }
01317     }
01318 
01319     LOG_NG << "Units moved\n";
01320 
01321     for(size_t un = 0; un < num_units; ++un) {
01322         units[un]->remove_from_game_display();
01323     }
01324 
01325     LOG_NG << "Units removed\n";
01326 }
01327 
01328 WML_HANDLER_FUNCTION(set_variable, /*event_info*/, cfg)
01329 {
01330     game_state *state_of_game = resources::state_of_game;
01331 
01332     const std::string name = cfg["name"];
01333     if(name.empty()) {
01334         ERR_NG << "trying to set a variable with an empty name:\n" << cfg.get_config().debug();
01335         return;
01336     }
01337     config::attribute_value &var = state_of_game->get_variable(name);
01338 
01339     config::attribute_value literal = cfg.get_config()["literal"]; // no $var substitution
01340     if (!literal.blank()) {
01341         var = literal;
01342     }
01343 
01344     config::attribute_value value = cfg["value"];
01345     if (!value.blank()) {
01346         var = value;
01347     }
01348 
01349     const std::string to_variable = cfg["to_variable"];
01350     if(to_variable.empty() == false) {
01351         var = state_of_game->get_variable(to_variable);
01352     }
01353 
01354     config::attribute_value add = cfg["add"];
01355     if (!add.empty()) {
01356         var = var.to_double() + add.to_double();
01357     }
01358 
01359     config::attribute_value sub = cfg["sub"];
01360     if (!sub.empty()) {
01361         var = var.to_double() - sub.to_double();
01362     }
01363 
01364     config::attribute_value multiply = cfg["multiply"];
01365     if (!multiply.empty()) {
01366         var = var.to_double() * multiply.to_double();
01367     }
01368 
01369     config::attribute_value divide = cfg["divide"];
01370     if (!divide.empty()) {
01371         if (divide.to_double() == 0) {
01372             ERR_NG << "division by zero on variable " << name << "\n";
01373             return;
01374         }
01375         var = var.to_double() / divide.to_double();
01376     }
01377 
01378     config::attribute_value modulo = cfg["modulo"];
01379     if (!modulo.empty()) {
01380         if (modulo.to_double() == 0) {
01381             ERR_NG << "division by zero on variable " << name << "\n";
01382             return;
01383         }
01384         var = std::fmod(var.to_double(), modulo.to_double());
01385     }
01386 
01387     config::attribute_value round_val = cfg["round"];
01388     if (!round_val.empty()) {
01389         double value = var.to_double();
01390         if (round_val == "ceil") {
01391             var = int(std::ceil(value));
01392         } else if (round_val == "floor") {
01393             var = int(std::floor(value));
01394         } else {
01395             // We assume the value is an integer.
01396             // Any non-numerical values will be interpreted as 0
01397             // Which is probably what was intended anyway
01398             int decimals = round_val.to_int();
01399             value *= std::pow(10.0, decimals); //add $decimals zeroes
01400             value = round_portable(value); // round() isn't implemented everywhere
01401             value *= std::pow(10.0, -decimals); //and remove them
01402             var = value;
01403         }
01404     }
01405 
01406     config::attribute_value ipart = cfg["ipart"];
01407     if (!ipart.empty()) {
01408         double result;
01409         std::modf(ipart.to_double(), &result);
01410         var = int(result);
01411     }
01412 
01413     config::attribute_value fpart = cfg["fpart"];
01414     if (!fpart.empty()) {
01415         double ignore;
01416         var = std::modf(fpart.to_double(), &ignore);
01417     }
01418 
01419     config::attribute_value string_length_target = cfg["string_length"];
01420     if (!string_length_target.blank()) {
01421         var = int(string_length_target.str().size());
01422     }
01423 
01424     // Note: maybe we add more options later, eg. strftime formatting.
01425     // For now make the stamp mandatory.
01426     const std::string time = cfg["time"];
01427     if(time == "stamp") {
01428         var = int(SDL_GetTicks());
01429     }
01430 
01431     // Random generation works as follows:
01432     // rand=[comma delimited list]
01433     // Each element in the list will be considered a separate choice,
01434     // unless it contains "..". In this case, it must be a numerical
01435     // range (i.e. -1..-10, 0..100, -10..10, etc).
01436     const std::string rand = cfg["rand"];
01437 
01438     // The new random generator, the logic is a copy paste of the old random.
01439     if(rand.empty() == false) {
01440         assert(state_of_game);
01441 
01442         std::string random_value;
01443 
01444         std::string word;
01445         std::vector<std::string> words;
01446         std::vector<std::pair<long,long> > ranges;
01447         int num_choices = 0;
01448         std::string::size_type pos = 0, pos2 = std::string::npos;
01449         std::stringstream ss(std::stringstream::in|std::stringstream::out);
01450         while (pos2 != rand.length()) {
01451             pos = pos2+1;
01452             pos2 = rand.find(",", pos);
01453 
01454             if (pos2 == std::string::npos)
01455                 pos2 = rand.length();
01456 
01457             word = rand.substr(pos, pos2-pos);
01458             words.push_back(word);
01459             std::string::size_type tmp = word.find("..");
01460 
01461 
01462             if (tmp == std::string::npos) {
01463                 // Treat this element as a string
01464                 ranges.push_back(std::pair<int, int>(0,0));
01465                 num_choices += 1;
01466             }
01467             else {
01468                 // Treat as a numerical range
01469                 const std::string first = word.substr(0, tmp);
01470                 const std::string second = word.substr(tmp+2,
01471                         rand.length());
01472 
01473                 long low, high;
01474                 ss << first + " " + second;
01475                 ss >> low;
01476                 ss >> high;
01477                 ss.clear();
01478 
01479                 if (low > high) {
01480                     std::swap(low, high);
01481                 }
01482                 ranges.push_back(std::pair<long, long>(low,high));
01483                 num_choices += (high - low) + 1;
01484 
01485                 // Make 0..0 ranges work
01486                 if (high == 0 && low == 0) {
01487                     words.pop_back();
01488                     words.push_back("0");
01489                 }
01490             }
01491         }
01492 
01493         /*
01494          * Choice gets a value in the range [0..32768).
01495          * So need to add a second set of random values when a value
01496          * outside the range is requested.
01497          */
01498         if(num_choices > 0x3fffffff) {
01499             WRN_NG << "Requested random number with an upper bound of "
01500                     << num_choices
01501                     << " however the maximum number generated will be "
01502                     << 0x3fffffff
01503                     << ".\n";
01504         }
01505         int choice = state_of_game->rng().get_next_random();
01506         if(num_choices >= 32768) {
01507             choice <<= 15;
01508             choice += state_of_game->rng().get_next_random();
01509         }
01510         choice %= num_choices;
01511         int tmp = 0;
01512         for(size_t i = 0; i < ranges.size(); ++i) {
01513             tmp += (ranges[i].second - ranges[i].first) + 1;
01514             if (tmp > choice) {
01515                 if (ranges[i].first == 0 && ranges[i].second == 0) {
01516                     random_value = words[i];
01517                 }
01518                 else {
01519                     tmp = (ranges[i].second - (tmp - choice)) + 1;
01520                     ss << tmp;
01521                     ss >> random_value;
01522                 }
01523                 break;
01524             }
01525         }
01526 
01527         var = random_value;
01528     }
01529 
01530 
01531     const vconfig::child_list join_elements = cfg.get_children("join");
01532     if(!join_elements.empty())
01533     {
01534         const vconfig join_element=join_elements.front();
01535 
01536         std::string array_name=join_element["variable"];
01537         std::string separator=join_element["separator"];
01538         std::string key_name=join_element["key"];
01539 
01540         if(key_name.empty())
01541         {
01542             key_name="value";
01543         }
01544 
01545         bool remove_empty = join_element["remove_empty"].to_bool();
01546 
01547         std::string joined_string;
01548 
01549         variable_info vi(array_name, true, variable_info::TYPE_ARRAY);
01550         bool first = true;
01551         foreach (const config &cfg, vi.as_array())
01552         {
01553             std::string current_string = cfg[key_name];
01554             if (remove_empty && current_string.empty()) continue;
01555             if (first) first = false;
01556             else joined_string += separator;
01557             joined_string += current_string;
01558         }
01559 
01560         var=joined_string;
01561     }
01562 
01563 }
01564 
01565 
01566 WML_HANDLER_FUNCTION(set_variables, /*event_info*/, cfg)
01567 {
01568     const t_string& name = cfg["name"];
01569     variable_info dest(name, true, variable_info::TYPE_CONTAINER);
01570     if(name.empty()) {
01571         ERR_NG << "trying to set a variable with an empty name:\n" << cfg.get_config().debug();
01572         return;
01573     }
01574 
01575     std::string mode = cfg["mode"]; // replace, append, merge, or insert
01576     if(mode == "extend") {
01577         mode = "append";
01578     } else if(mode != "append" && mode != "merge") {
01579         if(mode == "insert") {
01580             size_t child_count = dest.vars->child_count(dest.key);
01581             if(dest.index >= child_count) {
01582                 while(dest.index >= ++child_count) {
01583                     //inserting past the end requires empty data
01584                     dest.vars->append(config(dest.key));
01585                 }
01586                 //inserting at the end is handled by an append
01587                 mode = "append";
01588             }
01589         } else {
01590             mode = "replace";
01591         }
01592     }
01593 
01594     const vconfig::child_list values = cfg.get_children("value");
01595     const vconfig::child_list literals = cfg.get_children("literal");
01596     const vconfig::child_list split_elements = cfg.get_children("split");
01597 
01598     config data;
01599 
01600     if(cfg.has_attribute("to_variable"))
01601     {
01602         variable_info tovar(cfg["to_variable"], false, variable_info::TYPE_CONTAINER);
01603         if(tovar.is_valid) {
01604             if(tovar.explicit_index) {
01605                 data.add_child(dest.key, tovar.as_container());
01606             } else {
01607                 variable_info::array_range range = tovar.as_array();
01608                 while(range.first != range.second)
01609                 {
01610                     data.add_child(dest.key, *range.first++);
01611                 }
01612             }
01613         }
01614     } else if(!values.empty()) {
01615         for(vconfig::child_list::const_iterator i=values.begin(); i!=values.end(); ++i)
01616         {
01617             data.add_child(dest.key, (*i).get_parsed_config());
01618         }
01619     } else if(!literals.empty()) {
01620         for(vconfig::child_list::const_iterator i=literals.begin(); i!=literals.end(); ++i)
01621         {
01622             data.add_child(dest.key, i->get_config());
01623         }
01624     } else if(!split_elements.empty()) {
01625         const vconfig split_element=split_elements.front();
01626 
01627         std::string split_string=split_element["list"];
01628         std::string separator_string=split_element["separator"];
01629         std::string key_name=split_element["key"];
01630         if(key_name.empty())
01631         {
01632             key_name="value";
01633         }
01634 
01635         bool remove_empty = split_element["remove_empty"].to_bool();
01636 
01637         char* separator = separator_string.empty() ? NULL : &separator_string[0];
01638 
01639         std::vector<std::string> split_vector;
01640 
01641         //if no separator is specified, explode the string
01642         if(separator == NULL)
01643         {
01644             for(std::string::iterator i=split_string.begin(); i!=split_string.end(); ++i)
01645             {
01646                 split_vector.push_back(std::string(1, *i));
01647             }
01648         }
01649         else {
01650             split_vector=utils::split(split_string, *separator, remove_empty ? utils::REMOVE_EMPTY | utils::STRIP_SPACES : utils::STRIP_SPACES);
01651         }
01652 
01653         for(std::vector<std::string>::iterator i=split_vector.begin(); i!=split_vector.end(); ++i)
01654         {
01655             data.add_child(dest.key)[key_name]=*i;
01656         }
01657     }
01658     if(mode == "replace")
01659     {
01660         if(dest.explicit_index) {
01661             dest.vars->remove_child(dest.key, dest.index);
01662         } else {
01663             dest.vars->clear_children(dest.key);
01664         }
01665     }
01666     if(!data.empty())
01667     {
01668         if(mode == "merge")
01669         {
01670             if(dest.explicit_index) {
01671                 // merging multiple children into a single explicit index
01672                 // requires that they first be merged with each other
01673                 data.merge_children(dest.key);
01674                 dest.as_container().merge_with(data.child(dest.key));
01675             } else {
01676                 dest.vars->merge_with(data);
01677             }
01678         } else if(mode == "insert" || dest.explicit_index) {
01679             foreach (const config &child, data.child_range(dest.key))
01680             {
01681                 dest.vars->add_child_at(dest.key, child, dest.index++);
01682             }
01683         } else {
01684             dest.vars->append(data);
01685         }
01686     }
01687 }
01688 
01689 WML_HANDLER_FUNCTION(role, /*event_info*/, cfg)
01690 {
01691     bool found = false;
01692 
01693     // role= represents the instruction, so we can't filter on it
01694     config item = cfg.get_config();
01695     item.remove_attribute("role");
01696     vconfig filter(item);
01697 
01698     // try to match units on the gamemap before the recall lists
01699     std::vector<std::string> types = utils::split(filter["type"]);
01700     const bool has_any_types = !types.empty();
01701     std::vector<std::string>::iterator ti = types.begin(),
01702         ti_end = types.end();
01703     // loop to give precendence based on type order
01704     do {
01705         if (has_any_types) {
01706             item["type"] = *ti;
01707         }
01708         unit_map::iterator itor;
01709         foreach (unit &u, *resources::units) {
01710             if (game_events::unit_matches_filter(u, filter)) {
01711                 u.set_role(cfg["role"]);
01712                 found = true;
01713                 break;
01714             }
01715         }
01716     } while(!found && has_any_types && ++ti != ti_end);
01717     if(!found) {
01718         // next try to match units on the recall lists
01719         std::set<std::string> player_ids;
01720         std::vector<std::string> sides = utils::split(cfg["side"]);
01721         const bool has_any_sides = !sides.empty();
01722         foreach(std::string const& side_str, sides) {
01723             size_t side_num = lexical_cast_default<size_t>(side_str,0);
01724             if(side_num > 0 && side_num <= resources::teams->size()) {
01725                 player_ids.insert((resources::teams->begin() + (side_num - 1))->save_id());
01726             }
01727         }
01728         // loop to give precendence based on type order
01729         std::vector<std::string>::iterator ti = types.begin();
01730         do {
01731             if (has_any_types) {
01732                 item["type"] = *ti;
01733             }
01734             std::vector<team>::iterator pi,
01735                 pi_end = resources::teams->end();
01736             for (pi = resources::teams->begin(); pi != pi_end; ++pi)
01737             {
01738                 std::string const& player_id = pi->save_id();
01739                 // Verify the filter's side= includes this player
01740                 if(has_any_sides && !player_ids.count(player_id)) {
01741                     continue;
01742                 }
01743                 // Iterate over the player's recall list to find a match
01744                 for(size_t i=0; i < pi->recall_list().size(); ++i) {
01745                     unit& u = pi->recall_list()[i];
01746                     scoped_recall_unit auto_store("this_unit", player_id, i);
01747                     if (u.matches_filter(filter, map_location())) {
01748                         u.set_role(cfg["role"]);
01749                         found=true;
01750                         break;
01751                     }
01752                 }
01753             }
01754         } while(!found && has_any_types && ++ti != ti_end);
01755     }
01756 }
01757 
01758 WML_HANDLER_FUNCTION(sound_source, /*event_info*/, cfg)
01759 {
01760     soundsource::sourcespec spec(cfg.get_parsed_config());
01761     resources::soundsources->add(spec);
01762 }
01763 
01764 WML_HANDLER_FUNCTION(remove_sound_source, /*event_info*/, cfg)
01765 {
01766     resources::soundsources->remove(cfg["id"]);
01767 }
01768 
01769 void change_terrain(const map_location &loc, const t_translation::t_terrain &t,
01770     gamemap::tmerge_mode mode, bool replace_if_failed)
01771 {
01772     gamemap *game_map = resources::game_map;
01773 
01774     t_translation::t_terrain
01775         old_t = game_map->get_terrain(loc),
01776         new_t = game_map->merge_terrains(old_t, t, mode, replace_if_failed);
01777     if (new_t == t_translation::NONE_TERRAIN) return;
01778     preferences::encountered_terrains().insert(new_t);
01779 
01780     if (game_map->is_village(old_t) && !game_map->is_village(new_t)) {
01781         int owner = village_owner(loc, *resources::teams);
01782         if (owner != -1)
01783             (*resources::teams)[owner].lose_village(loc);
01784     }
01785 
01786     game_map->set_terrain(loc, new_t);
01787     screen_needs_rebuild = true;
01788 
01789     foreach (const t_translation::t_terrain &ut, game_map->underlying_union_terrain(loc)) {
01790         preferences::encountered_terrains().insert(ut);
01791     }
01792 }
01793 
01794 // Creating a mask of the terrain
01795 WML_HANDLER_FUNCTION(terrain_mask, /*event_info*/, cfg)
01796 {
01797     map_location loc = cfg_to_loc(cfg, 1, 1);
01798 
01799     gamemap mask_map(*resources::game_map);
01800 
01801     //config level;
01802     std::string mask = cfg["mask"];
01803     std::string usage = "mask";
01804     int border_size = 0;
01805 
01806     if (mask.empty()) {
01807         usage = cfg["usage"].str();
01808         border_size = cfg["border_size"];
01809         mask = cfg["data"].str();
01810     }
01811 
01812     try {
01813         mask_map.read(mask, false, border_size, usage);
01814     } catch(incorrect_map_format_error&) {
01815         ERR_NG << "terrain mask is in the incorrect format, and couldn't be applied\n";
01816         return;
01817     } catch(twml_exception& e) {
01818         e.show(*resources::screen);
01819         return;
01820     }
01821     bool border = cfg["border"].to_bool();
01822     resources::game_map->overlay(mask_map, cfg.get_parsed_config(), loc.x, loc.y, border);
01823     screen_needs_rebuild = true;
01824 }
01825 
01826 static bool try_add_unit_to_recall_list(const map_location& loc, const unit& u)
01827 {
01828     if((*resources::teams)[u.side()-1].persistent()) {
01829         (*resources::teams)[u.side()-1].recall_list().push_back(u);
01830         return true;
01831     } else {
01832         ERR_NG << "unit with id " << u.id() << ": location (" << loc.x << "," << loc.y <<") is not on the map, and player "
01833             << u.side() << " has no recall list.\n";
01834         return false;
01835     }
01836 }
01837 
01838 // If we should spawn a new unit on the map somewhere
01839 WML_HANDLER_FUNCTION(unit, /*event_info*/, cfg)
01840 {
01841     config parsed_cfg = cfg.get_parsed_config();
01842 
01843     config::attribute_value to_variable = cfg["to_variable"];
01844     if (!to_variable.blank())
01845     {
01846         parsed_cfg.remove_attribute("to_variable");
01847         unit new_unit(parsed_cfg, true, resources::state_of_game);
01848         config &var = resources::state_of_game->get_variable_cfg(to_variable);
01849         var.clear();
01850         new_unit.write(var);
01851         if (const config::attribute_value *v = parsed_cfg.get("x")) var["x"] = *v;
01852         if (const config::attribute_value *v = parsed_cfg.get("y")) var["y"] = *v;
01853         return;
01854     }
01855 
01856     int side = parsed_cfg["side"].to_int(1);
01857 
01858 
01859     if ((side<1)||(side > static_cast<int>(resources::teams->size()))) {
01860         ERR_NG << "wrong side in [unit] tag - no such side: "<<side<<" ( number of teams :"<<resources::teams->size()<<")"<<std::endl;
01861         DBG_NG << parsed_cfg.debug();
01862         return;
01863     }
01864     team &tm = resources::teams->at(side-1);
01865 
01866     unit_creator uc(tm,resources::game_map->starting_position(side));
01867 
01868     uc
01869         .allow_add_to_recall(true)
01870         .allow_discover(true)
01871         .allow_get_village(true)
01872         .allow_invalidate(true)
01873         .allow_rename_side(true)
01874         .allow_show(true);
01875 
01876     uc.add_unit(parsed_cfg, &cfg);
01877 
01878 }
01879 
01880 // If we should recall units that match a certain description
01881 WML_HANDLER_FUNCTION(recall, /*event_info*/, cfg)
01882 {
01883     LOG_NG << "recalling unit...\n";
01884     config temp_config(cfg.get_config());
01885     // Prevent the recall unit filter from using the location as a criterion
01886 
01887     /**
01888      * @todo FIXME: we should design the WML to avoid these types of
01889      * collisions; filters should be named consistently and always have a
01890      * distinct scope.
01891      */
01892     temp_config["x"] = "recall";
01893     temp_config["y"] = "recall";
01894     vconfig unit_filter(temp_config);
01895     const vconfig leader_filter = cfg.child("secondary_unit");
01896 
01897     for(int index = 0; index < int(resources::teams->size()); ++index) {
01898         LOG_NG << "for side " << index + 1 << "...\n";
01899         const std::string player_id = (*resources::teams)[index].save_id();
01900 
01901         if((*resources::teams)[index].recall_list().size() < 1) {
01902             DBG_NG << "recall list is empty when trying to recall!\n"
01903                    << "player_id: " << player_id << " side: " << index+1 << "\n";
01904             continue;
01905         }
01906 
01907         std::vector<unit>& avail = (*resources::teams)[index].recall_list();
01908         std::vector<unit_map::unit_iterator> leaders = resources::units->find_leaders(index + 1);
01909 
01910         for(std::vector<unit>::iterator u = avail.begin(); u != avail.end(); ++u) {
01911             DBG_NG << "checking unit against filter...\n";
01912             scoped_recall_unit auto_store("this_unit", player_id, u - avail.begin());
01913             if (u->matches_filter(unit_filter, map_location())) {
01914                 DBG_NG << u->id() << " matched the filter...\n";
01915                 const unit to_recruit(*u);
01916                 const unit* pass_check = &to_recruit;
01917                 if(!cfg["check_passability"].to_bool(true)) pass_check = NULL;
01918                 const map_location cfg_loc = cfg_to_loc(cfg);
01919 
01920                 //TODO fendrin: comment this monster
01921                 foreach (unit_map::const_unit_iterator leader, leaders) {
01922                     DBG_NG << "...considering " + leader->id() + " as the recalling leader...\n";
01923                     map_location loc = cfg_loc;
01924                     if ( (leader_filter.null() || leader->matches_filter(leader_filter, leader->get_location())) &&
01925                             (u->matches_filter(vconfig(leader->recall_filter()), map_location())) ) {
01926                         DBG_NG << "...matched the leader filter and is able to recall the unit.\n";
01927                         if(!resources::game_map->on_board(loc))
01928                             loc = leader->get_location();
01929                         if(pass_check || (resources::units->count(loc) > 0))
01930                             loc = pathfind::find_vacant_tile(*resources::game_map,
01931                                     *resources::units, loc, pathfind::VACANT_ANY, pass_check);
01932                         if(resources::game_map->on_board(loc)) {
01933                             DBG_NG << "...valid location for the recall found. Recalling.\n";
01934                             avail.erase(u); // Erase before recruiting, since recruiting can fire more events
01935                             place_recruit(to_recruit, loc, leader->get_location(), true,
01936                                     cfg["show"].to_bool(true), cfg["fire_event"].to_bool(false), true, true);
01937                             return;
01938                         }
01939                     }
01940                 }
01941                 if (resources::game_map->on_board(cfg_loc)) {
01942                     map_location loc = cfg_loc;
01943                     if(pass_check || (resources::units->count(loc) > 0))
01944                         loc = pathfind::find_vacant_tile(*resources::game_map,
01945                                 *resources::units, loc, pathfind::VACANT_ANY, pass_check);
01946                     // Check if we still have a valid location
01947                     if (resources::game_map->on_board(loc)) {
01948                         DBG_NG << "No usable leader found, but found usable location. Recalling.\n";
01949                         avail.erase(u); // Erase before recruiting, since recruiting can fire more events
01950                         map_location null_location = map_location::null_location;
01951                         place_recruit(to_recruit, loc, null_location, true,
01952                                 cfg["show"].to_bool(true), cfg["fire_event"].to_bool(false), true, true);
01953                         return;
01954                     }
01955                 }
01956             }
01957         }
01958     }
01959     //TODO I don't know about that error throwing. Sometimes a unit is just not available,
01960     //the designer needs to check with [have_unit] or fetch the recall event.
01961     ERR_NG << "A [recall] tag with the following content failed:\n" << cfg.get_config().debug();
01962 }
01963 
01964 WML_HANDLER_FUNCTION(object, event_info, cfg)
01965 {
01966     const vconfig filter = cfg.child("filter");
01967 
01968     std::string id = cfg["id"];
01969 
01970     // If this item has already been used
01971     if(id != "" && used_items.count(id))
01972         return;
01973 
01974     std::string image = cfg["image"];
01975     std::string caption = cfg["name"];
01976     std::string text;
01977 
01978     map_location loc;
01979     if(!filter.null()) {
01980         foreach (const unit &u, *resources::units) {
01981             if (game_events::unit_matches_filter(u, filter)) {
01982                 loc = u.get_location();
01983                 break;
01984             }
01985         }
01986     }
01987 
01988     if(loc.valid() == false) {
01989         loc = event_info.loc1;
01990     }
01991 
01992     const unit_map::iterator u = resources::units->find(loc);
01993 
01994     std::string command_type = "then";
01995 
01996     if (u != resources::units->end() && (filter.null() || game_events::unit_matches_filter(*u, filter)))
01997     {
01998         //@deprecated This can be removed (and a proper duration=level implemented) after 1.11.2
01999         // Don't forget to remove it from wmllint too!
02000         const std::string& duration = cfg["duration"];
02001         if (duration == "level") {
02002             lg::wml_error << "[object]duration=level is deprecated. Use duration=scenario instead.\n";
02003         }
02004 
02005         text = cfg["description"].str();
02006 
02007         if(cfg["delayed_variable_substitution"].to_bool(false))
02008             u->add_modification("object", cfg.get_config());
02009         else
02010             u->add_modification("object", cfg.get_parsed_config());
02011 
02012         resources::screen->select_hex(event_info.loc1);
02013         resources::screen->invalidate_unit();
02014 
02015         // Mark this item as used up.
02016         used_items.insert(id);
02017     } else {
02018         text = cfg["cannot_use_message"].str();
02019         command_type = "else";
02020     }
02021 
02022     if (!cfg["silent"].to_bool())
02023     {
02024         // Redraw the unit, with its new stats
02025         resources::screen->draw();
02026 
02027         try {
02028             gui2::show_transient_message(resources::screen->video(), caption, text, image, true);
02029         } catch(utils::invalid_utf8_exception&) {
02030             // we already had a warning so do nothing.
02031         }
02032     }
02033 
02034     foreach (const vconfig &cmd, cfg.get_children(command_type)) {
02035         handle_event_commands(event_info, cmd);
02036     }
02037 }
02038 
02039 WML_HANDLER_FUNCTION(print, /*event_info*/, cfg)
02040 {
02041     // Remove any old message.
02042     if (floating_label)
02043         font::remove_floating_label(floating_label);
02044 
02045     // Display a message on-screen
02046     std::string text = cfg["text"];
02047     if(text.empty())
02048         return;
02049 
02050     int size = cfg["size"].to_int(font::SIZE_SMALL);
02051     int lifetime = cfg["duration"].to_int(50);
02052     SDL_Color color = create_color(cfg["red"], cfg["green"], cfg["blue"]);
02053 
02054     const SDL_Rect& rect = resources::screen->map_outside_area();
02055 
02056     font::floating_label flabel(text);
02057     flabel.set_font_size(size);
02058     flabel.set_color(color);
02059     flabel.set_position(rect.w/2,rect.h/2);
02060     flabel.set_lifetime(lifetime);
02061     flabel.set_clip_rect(rect);
02062 
02063     floating_label = font::add_floating_label(flabel);
02064 }
02065 
02066 WML_HANDLER_FUNCTION(deprecated_message, /*event_info*/, cfg)
02067 {
02068     game_events::handle_deprecated_message( cfg.get_parsed_config() );
02069 }
02070 
02071 WML_HANDLER_FUNCTION(wml_message, /*event_info*/, cfg)
02072 {
02073     game_events::handle_wml_log_message( cfg.get_parsed_config() );
02074 }
02075 
02076 
02077 typedef std::map<map_location, int> recursion_counter;
02078 
02079 class recursion_preventer {
02080     static recursion_counter counter_;
02081     static const int max_recursion = 10;
02082     map_location loc_;
02083     bool too_many_recursions_;
02084 
02085     public:
02086     recursion_preventer(map_location& loc) :
02087         loc_(loc),
02088         too_many_recursions_(false)
02089     {
02090         recursion_counter::iterator inserted = counter_.insert(std::make_pair(loc_, 0)).first;
02091         ++inserted->second;
02092         too_many_recursions_ = inserted->second >= max_recursion;
02093     }
02094     ~recursion_preventer()
02095     {
02096         recursion_counter::iterator itor = counter_.find(loc_);
02097         if (--itor->second == 0)
02098         {
02099             counter_.erase(itor);
02100         }
02101     }
02102     bool too_many_recursions() const
02103     {
02104         return too_many_recursions_;
02105     }
02106 };
02107 
02108 recursion_counter recursion_preventer::counter_ = recursion_counter();
02109 
02110 typedef boost::scoped_ptr<recursion_preventer> recursion_preventer_ptr;
02111 
02112 WML_HANDLER_FUNCTION(kill, event_info, cfg)
02113 {
02114     bool secondary_unit = cfg.has_child("secondary_unit");
02115     game_events::entity_location killer_loc(map_location(0, 0));
02116     if(cfg["fire_event"].to_bool() && secondary_unit)
02117     {
02118         secondary_unit = false;
02119         for(unit_map::const_unit_iterator unit = resources::units->begin();
02120             unit != resources::units->end(); ++unit) {
02121                 if(game_events::unit_matches_filter(*unit, cfg.child("secondary_unit")))
02122                 {
02123                     killer_loc = game_events::entity_location(*unit);
02124                     secondary_unit = true;
02125                     break;
02126                 }
02127         }
02128         if(!secondary_unit) {
02129             WRN_NG << "failed to match [secondary_unit] in [kill] with a single on-board unit\n";
02130         }
02131     }
02132 
02133     //Find all the dead units first, because firing events ruins unit_map iteration
02134     std::vector<unit *> dead_men_walking;
02135     foreach(unit & u, *resources::units){
02136         if(game_events::unit_matches_filter(u, cfg)){
02137             dead_men_walking.push_back(&u);
02138         }
02139     }
02140 
02141     foreach(unit * un, dead_men_walking) {
02142         map_location loc(un->get_location());
02143         bool fire_event = false;
02144         game_events::entity_location death_loc(*un);
02145         if(!secondary_unit) {
02146             killer_loc = game_events::entity_location(*un);
02147         }
02148 
02149         if (cfg["fire_event"].to_bool())
02150             {
02151                 // Prevent infinite recursion of 'die' events
02152                 fire_event = true;
02153                 recursion_preventer_ptr recursion_prevent;
02154 
02155                 if (event_info.loc1 == death_loc && (event_info.name == "die" || event_info.name == "last breath"))
02156                     {
02157                         recursion_prevent.reset(new recursion_preventer(death_loc));
02158 
02159                         if(recursion_prevent->too_many_recursions())
02160                             {
02161                                 fire_event = false;
02162 
02163                                 ERR_NG << "tried to fire 'die' or 'last breath' event on primary_unit inside its own 'die' or 'last breath' event with 'first_time_only' set to false!\n";
02164                             }
02165                     }
02166             }
02167         if (fire_event) {
02168             game_events::fire("last breath", death_loc, killer_loc);
02169         }
02170 
02171         // Visual consequences of the kill.
02172         if (cfg["animate"].to_bool()) {
02173             resources::screen->scroll_to_tile(loc);
02174             unit_map::iterator iun = resources::units->find(loc);
02175             if (iun != resources::units->end() && iun.valid()) {
02176                 unit_display::unit_die(loc, *iun);
02177             }
02178         } else {
02179             // Make sure the unit gets (fully) cleaned off the screen.
02180             resources::screen->invalidate(loc);
02181             unit_map::iterator iun = resources::units->find(loc);
02182             if ( iun != resources::units->end()  &&  iun.valid() )
02183                 iun->invalidate(loc);
02184         }
02185         resources::screen->redraw_minimap();
02186 
02187         if (fire_event) {
02188             game_events::fire("die", death_loc, killer_loc);
02189             unit_map::iterator iun = resources::units->find(death_loc);
02190             if (iun != resources::units->end() && death_loc.matches_unit(*iun)) {
02191                 resources::units->erase(iun);
02192             }
02193         }
02194         else resources::units->erase(loc);
02195     }
02196 
02197     // If the filter doesn't contain positional information,
02198     // then it may match units on all recall lists.
02199     t_string const& cfg_x = cfg["x"];
02200     t_string const& cfg_y = cfg["y"];
02201     if((cfg_x.empty() || cfg_x == "recall")
02202     && (cfg_y.empty() || cfg_y == "recall"))
02203     {
02204         //remove the unit from the corresponding team's recall list
02205         for(std::vector<team>::iterator pi = resources::teams->begin();
02206                 pi!=resources::teams->end(); ++pi)
02207         {
02208             std::vector<unit>& avail_units = pi->recall_list();
02209             for(std::vector<unit>::iterator j = avail_units.begin(); j != avail_units.end();) {
02210                 scoped_recall_unit auto_store("this_unit", pi->save_id(), j - avail_units.begin());
02211                 if (j->matches_filter(cfg, map_location())) {
02212                     j = avail_units.erase(j);
02213                 } else {
02214                     ++j;
02215                 }
02216             }
02217         }
02218     }
02219 }
02220 
02221 // Setting of menu items
02222 WML_HANDLER_FUNCTION(set_menu_item, /*event_info*/, cfg)
02223 {
02224     /*
02225        [set_menu_item]
02226        id=test1
02227        image="buttons/group_all.png"
02228        description="Summon Troll"
02229        [show_if]
02230        [not]
02231        [have_unit]
02232        x,y=$x1,$y1
02233        [/have_unit]
02234        [/not]
02235        [/show_if]
02236        [filter_location]
02237        [/filter_location]
02238        [command]
02239        {LOYAL_UNIT $side_number (Troll) $x1 $y1 (Myname) ( _ "Myname")}
02240        [/command]
02241        [/set_menu_item]
02242        */
02243     std::string id = cfg["id"];
02244     wml_menu_item*& mref = resources::state_of_game->wml_menu_items[id];
02245     if(mref == NULL) {
02246         mref = new wml_menu_item(id);
02247     }
02248     if(cfg.has_attribute("image")) {
02249         mref->image = cfg["image"].str();
02250     }
02251     if(cfg.has_attribute("description")) {
02252         mref->description = cfg["description"];
02253     }
02254     if(cfg.has_attribute("needs_select")) {
02255         mref->needs_select = cfg["needs_select"].to_bool();
02256     }
02257     if(cfg.has_child("show_if")) {
02258         mref->show_if = cfg.child("show_if").get_config();
02259     }
02260     if(cfg.has_child("filter_location")) {
02261         mref->filter_location = cfg.child("filter_location").get_config();
02262     }
02263     if(cfg.has_child("command")) {
02264         const vconfig& cmd = cfg.child("command");
02265         const bool delayed = cmd["delayed_variable_substitution"].to_bool(true);
02266         config* new_command = new config(delayed ? cmd.get_config() : cmd.get_parsed_config());
02267         wmi_command_changes.push_back(wmi_command_change(id, new_command));
02268     }
02269 }
02270 
02271 WML_HANDLER_FUNCTION(clear_menu_item, /*event_info*/, cfg)
02272 {
02273     const std::string ids = cfg["id"].str();
02274     foreach(const std::string& id, utils::split(ids, ',', utils::STRIP_SPACES)) {
02275         if(id.empty()) {
02276             WRN_NG << "[clear_menu_item] has been given an empty id=, ignoring\n";
02277             continue;
02278         }
02279 
02280         std::map<std::string, wml_menu_item*>& menu_items = resources::state_of_game->wml_menu_items;
02281         if(menu_items.find(id) == menu_items.end()) {
02282             WRN_NG << "trying to remove non-existent menu item '" << id << "', ignoring\n";
02283             continue;
02284         }
02285 
02286         std::vector<wmi_command_change>::iterator wcc = wmi_command_changes.begin();
02287         while(wcc != wmi_command_changes.end()) {
02288             if(wcc->first != id) {
02289                 ++wcc;
02290                 continue;
02291             }
02292             delete wcc->second;
02293             wcc->second = NULL;
02294             wcc = wmi_command_changes.erase(wcc);
02295         }
02296         event_handlers.remove_event_handler(id);
02297 
02298         wml_menu_item*& mi = menu_items[id];
02299         delete mi;
02300         mi = NULL;
02301         menu_items.erase(id);
02302     }
02303 }
02304 
02305 struct unstore_unit_advance_choice: mp_sync::user_choice
02306 {
02307     int nb_options;
02308     map_location loc;
02309     bool use_dialog;
02310 
02311     unstore_unit_advance_choice(int o, const map_location &l, bool d)
02312         : nb_options(o), loc(l), use_dialog(d)
02313     {}
02314 
02315     virtual config query_user() const
02316     {
02317         int selected;
02318         if (use_dialog) {
02319             DBG_NG << "dialog requested\n";
02320             selected = dialogs::advance_unit_dialog(loc);
02321         } else {
02322             // VITAL this is NOT done using the synced RNG
02323             selected = rand() % nb_options;
02324         }
02325         config cfg;
02326         cfg["value"] = selected;
02327         return cfg;
02328     }
02329 
02330     virtual config random_choice(rand_rng::simple_rng &rng) const
02331     {
02332         config cfg;
02333         cfg["value"] = rng.get_next_random() % nb_options;
02334         return cfg;
02335     }
02336 };
02337 
02338 // Unit serialization to variables
02339 WML_HANDLER_FUNCTION(unstore_unit, /*event_info*/, cfg)
02340 {
02341     const config &var = resources::state_of_game->get_variable_cfg(cfg["variable"]);
02342 
02343     try {
02344         config tmp_cfg(var);
02345         const unit u(tmp_cfg, false);
02346 
02347         preferences::encountered_units().insert(u.type_id());
02348         map_location loc = cfg_to_loc(
02349             (cfg.has_attribute("x") && cfg.has_attribute("y")) ? cfg : vconfig(var));
02350         const bool advance = cfg["advance"].to_bool(true);
02351         if(resources::game_map->on_board(loc)) {
02352             if (cfg["find_vacant"].to_bool()) {
02353                 const unit* pass_check = NULL;
02354                 if (cfg["check_passability"].to_bool(true)) pass_check = &u;
02355                 loc = pathfind::find_vacant_tile(
02356                         *resources::game_map,
02357                         *resources::units,
02358                         loc,
02359                         pathfind::VACANT_ANY,
02360                         pass_check);
02361             }
02362 
02363             resources::units->erase(loc);
02364             resources::units->add(loc, u);
02365 
02366             std::string text = cfg["text"];
02367             play_controller *controller = resources::controller;
02368             if(!text.empty() && !controller->is_skipping_replay())
02369             {
02370                 // Print floating label
02371                 resources::screen->float_label(loc, text, cfg["red"], cfg["green"], cfg["blue"]);
02372             }
02373 
02374             const int side = controller->current_side();
02375             if (advance &&
02376                 unit_helper::will_certainly_advance(resources::units->find(loc)))
02377             {
02378                 int total_opt = unit_helper::number_of_possible_advances(u);
02379                 bool use_dialog = side == u.side() &&
02380                     (*resources::teams)[side - 1].is_human();
02381                 config selected = mp_sync::get_user_choice("choose",
02382                     unstore_unit_advance_choice(total_opt, loc, use_dialog));
02383                 dialogs::animate_unit_advancement(loc, selected["value"], cfg["fire_event"].to_bool(false));
02384             }
02385         } else {
02386             if(advance && u.advances()) {
02387                 WRN_NG << "Cannot advance units when unstoring to the recall list.\n";
02388             }
02389 
02390             team& t = (*resources::teams)[u.side()-1];
02391 
02392             if(t.persistent()) {
02393 
02394                 // Test whether the recall list has duplicates if so warn.
02395                 // This might be removed at some point but the uniqueness of
02396                 // the description is needed to avoid the recall duplication
02397                 // bugs. Duplicates here might cause the wrong unit being
02398                 // replaced by the wrong unit.
02399                 if(t.recall_list().size() > 1) {
02400                     std::vector<size_t> desciptions;
02401                     for(std::vector<unit>::const_iterator citor =
02402                             t.recall_list().begin();
02403                             citor != t.recall_list().end(); ++citor) {
02404 
02405                         const size_t desciption =
02406                             citor->underlying_id();
02407                         if(std::find(desciptions.begin(), desciptions.end(),
02408                                     desciption) != desciptions.end()) {
02409 
02410                             lg::wml_error << "Recall list has duplicate unit "
02411                                 "underlying_ids '" << desciption
02412                                 << "' unstore_unit may not work as expected.\n";
02413                         } else {
02414                             desciptions.push_back(desciption);
02415                         }
02416                     }
02417                 }
02418 
02419                 // Avoid duplicates in the list.
02420                 /**
02421                  * @todo it would be better to change recall_list() from
02422                  * a vector to a map and use the underlying_id as key.
02423                  */
02424                 const size_t key = u.underlying_id();
02425                 for(std::vector<unit>::iterator itor =
02426                         t.recall_list().begin();
02427                         itor != t.recall_list().end(); ++itor) {
02428 
02429                     LOG_NG << "Replaced unit '"
02430                         << key << "' on the recall list\n";
02431                     if(itor->underlying_id() == key) {
02432                         t.recall_list().erase(itor);
02433                         break;
02434                     }
02435                 }
02436                 t.recall_list().push_back(u);
02437             } else {
02438                 ERR_NG << "Cannot unstore unit: recall list is empty for player " << u.side()
02439                     << " and the map location is invalid.\n";
02440             }
02441         }
02442 
02443         // If we unstore a leader make sure the team gets a leader if not the loading
02444         // in MP might abort since a side without a leader has a recall list.
02445         if(u.can_recruit()) {
02446             (*resources::teams)[u.side() - 1].have_leader();
02447         }
02448 
02449     } catch (game::game_error &e) {
02450         ERR_NG << "could not de-serialize unit: '" << e.message << "'\n";
02451     }
02452 }
02453 
02454 /* [store_villages] : store villages into an array
02455  * Keys:
02456  * - variable (mandatory): variable to store in
02457  * - side: if present, the village should be owned by this side (0=unowned villages)
02458  * - terrain: if present, filter the village types against this list of terrain types
02459  */
02460 WML_HANDLER_FUNCTION(store_villages, /*event_info*/, cfg)
02461 {
02462     log_scope("store_villages");
02463     std::string variable = cfg["variable"];
02464     if (variable.empty()) {
02465         variable="location";
02466     }
02467     config to_store;
02468     variable_info varinfo(variable, true, variable_info::TYPE_ARRAY);
02469 
02470     std::vector<map_location> locs = resources::game_map->villages();
02471 
02472     for(std::vector<map_location>::const_iterator j = locs.begin(); j != locs.end(); ++j) {
02473         bool matches = terrain_filter(cfg, *resources::units).match(*j);
02474         if(matches) {
02475             config &loc_store = to_store.add_child(varinfo.key);
02476             j->write(loc_store);
02477             resources::game_map->write_terrain(*j, loc_store);
02478             int side = village_owner(*j, *resources::teams) + 1;
02479             loc_store["owner_side"] = side;
02480         }
02481     }
02482     varinfo.vars->clear_children(varinfo.key);
02483     varinfo.vars->append(to_store);
02484 }
02485 
02486 WML_HANDLER_FUNCTION(end_turn, /*event_info*/, /*cfg*/)
02487 {
02488     resources::controller->force_end_turn();
02489 }
02490 
02491 WML_HANDLER_FUNCTION(endlevel, /*event_info*/, cfg)
02492 {
02493     end_level_data &data = resources::controller->get_end_level_data();
02494     if(data.disabled) {
02495         WRN_NG << "repeated [endlevel] execution, ignoring\n";
02496         return;
02497     }
02498     data.disabled = true;
02499 
02500     game_state *state_of_game = resources::state_of_game;
02501     unit_map *units = resources::units;
02502 
02503     // Remove 0-hp units from the unit map to avoid the following problem:
02504     // In case a die event triggers an endlevel the dead unit is still as a
02505     // 'ghost' in linger mode. After save loading in linger mode the unit
02506     // is fully visible again.
02507     unit_map::iterator u = units->begin();
02508     while (u != units->end()) {
02509         if (u->hitpoints() <= 0) {
02510             units->erase(u++);
02511         } else {
02512             ++u;
02513         }
02514     }
02515 
02516     std::string next_scenario = cfg["next_scenario"];
02517     if (!next_scenario.empty()) {
02518         state_of_game->classification().next_scenario = next_scenario;
02519     }
02520 
02521     std::string end_of_campaign_text = cfg["end_text"];
02522     if (!end_of_campaign_text.empty()) {
02523         state_of_game->classification().end_text = end_of_campaign_text;
02524     }
02525 
02526     config::attribute_value end_of_campaign_text_delay = cfg["end_text_duration"];
02527     if (!end_of_campaign_text_delay.empty()) {
02528         state_of_game->classification().end_text_duration =
02529             end_of_campaign_text_delay.to_int(state_of_game->classification().end_text_duration);
02530     }
02531 
02532     if(cfg.has_attribute("end_credits")) {
02533         state_of_game->classification().end_credits = cfg["end_credits"].to_bool(true);
02534     }
02535 
02536 
02537     std::string result = cfg["result"];
02538     VALIDATE_WITH_DEV_MESSAGE(
02539               result.empty() || result == "victory" || result == "defeat"
02540             , _("Invalid value in the result key for [end_level]")
02541             , "result = '"  + result + "'.");
02542     data.custom_endlevel_music = cfg["music"].str();
02543     data.carryover_report = cfg["carryover_report"].to_bool(true);
02544     data.prescenario_save = cfg["save"].to_bool(true);
02545     data.replay_save = cfg["replay_save"].to_bool(true);
02546     data.linger_mode = cfg["linger_mode"].to_bool(true)
02547         && !resources::teams->empty();
02548     data.reveal_map = cfg["reveal_map"].to_bool(true);
02549     data.gold_bonus = cfg["bonus"].to_bool(true);
02550     data.carryover_percentage = cfg["carryover_percentage"].to_int(game_config::gold_carryover_percentage);
02551     data.carryover_add = cfg["carryover_add"].to_bool();
02552 
02553     if(result == "defeat") {
02554         data.carryover_add = false;
02555         resources::controller->force_end_level(DEFEAT);
02556     } else {
02557         resources::controller->force_end_level(VICTORY);
02558     }
02559 }
02560 
02561 WML_HANDLER_FUNCTION(redraw, /*event_info*/, cfg)
02562 {
02563     game_display &screen = *resources::screen;
02564 
02565     const config::attribute_value clear_shroud_av = cfg["clear_shroud"];
02566     const config::attribute_value side = cfg["side"];
02567     bool clear_shroud_bool = clear_shroud_av.to_bool(false);
02568     if(clear_shroud_av.blank() && !side.blank()) {
02569         //Backwards compat, behavior of the tag was to clear shroud in case that side= is given.
02570         clear_shroud_bool = true;
02571     }
02572 
02573     if (clear_shroud_bool) {
02574         side_filter filter(cfg);
02575         foreach(const int side, filter.get_teams()){
02576             clear_shroud(side);
02577         }
02578         screen.recalculate_minimap();
02579     }
02580     if (screen_needs_rebuild) {
02581         screen_needs_rebuild = false;
02582         screen.recalculate_minimap();
02583         screen.rebuild_all();
02584     }
02585     screen.invalidate_all();
02586     screen.draw(true,true);
02587 }
02588 
02589 WML_HANDLER_FUNCTION(animate_unit, event_info, cfg)
02590 {
02591     const events::command_disabler disable_commands;
02592     unit_display::wml_animation(cfg, event_info.loc1);
02593 }
02594 
02595 WML_HANDLER_FUNCTION(label, /*event_info*/, cfg)
02596 {
02597     game_display &screen = *resources::screen;
02598 
02599     terrain_label label(screen.labels(), cfg.get_config());
02600 
02601     screen.labels().set_label(label.location(), label.text(),
02602         label.team_name(), label.color(), label.visible_in_fog(), label.visible_in_shroud(), label.immutable());
02603 }
02604 
02605 WML_HANDLER_FUNCTION(heal_unit, event_info, cfg)
02606 {
02607     unit_map* units = resources::units;
02608 
02609     const vconfig healers_filter = cfg.child("filter_second");
02610     std::vector<unit*> healers;
02611     if (!healers_filter.null()) {
02612         foreach (unit& u, *units) {
02613             if (game_events::unit_matches_filter(u, healers_filter) && u.has_ability_type("heals")) {
02614                 healers.push_back(&u);
02615             }
02616         }
02617     }
02618 
02619     const config::attribute_value amount = cfg["amount"];
02620     const config::attribute_value moves = cfg["moves"];
02621     const bool restore_attacks = cfg["restore_attacks"].to_bool(false);
02622     const bool restore_statuses = cfg["restore_statuses"].to_bool(true);
02623     const bool animate = cfg["animate"].to_bool(false);
02624 
02625     const vconfig healed_filter = cfg.child("filter");
02626     unit_map::unit_iterator u;
02627     bool only_unit_at_loc1 = healed_filter.null();
02628     bool heal_amount_to_set = true;
02629     for(u  = units->begin(); u != units->end(); ++u) {
02630         if (only_unit_at_loc1)
02631         {
02632             u = units->find(event_info.loc1);
02633             if(!u.valid()) return;
02634         }
02635         else if (!game_events::unit_matches_filter(*u, healed_filter)) continue;
02636 
02637         int heal_amount = u->max_hitpoints() - u->hitpoints();
02638         if(amount.blank() || amount == "full") u->set_hitpoints(u->max_hitpoints());
02639         else {
02640             heal_amount = lexical_cast_default<int, config::attribute_value> (amount, heal_amount);
02641             const int& new_hitpoints = std::max(1, std::min(u->max_hitpoints(), u->hitpoints() + heal_amount));
02642             heal_amount = new_hitpoints - u->hitpoints();
02643             u->set_hitpoints(new_hitpoints);
02644         }
02645 
02646         if(!moves.blank()) {
02647             if(moves == "full") u->set_movement(u->total_movement());
02648             else {
02649                 // set_movement doesn't set below 0
02650                 u->set_movement(std::min<int>(
02651                     u->total_movement(),
02652                     u->movement_left() + lexical_cast_default<int, config::attribute_value> (moves, 0)
02653                     ));
02654             }
02655         }
02656 
02657         if(restore_attacks) u->set_attacks(u->max_attacks());
02658 
02659         if(restore_statuses)
02660         {
02661             u->set_state(unit::STATE_POISONED, false);
02662             u->set_state(unit::STATE_SLOWED, false);
02663             u->set_state(unit::STATE_PETRIFIED, false);
02664             u->set_state(unit::STATE_UNHEALABLE, false);
02665         }
02666 
02667         if (heal_amount_to_set)
02668         {
02669             heal_amount_to_set = false;
02670             resources::state_of_game->get_variable("heal_amount") = heal_amount;
02671         }
02672 
02673         if(animate) unit_display::unit_healing(*u, u->get_location(), healers, heal_amount);
02674         if(only_unit_at_loc1) return;
02675     }
02676 }
02677 
02678 // Allow undo sets the flag saying whether the event has mutated the game to false
02679 WML_HANDLER_FUNCTION(allow_undo,/*event_info*/,/*cfg*/)
02680 {
02681     current_context->mutated = false;
02682 }
02683 
02684 WML_HANDLER_FUNCTION(open_help,  /*event_info*/, cfg)
02685 {
02686     game_display &screen = *resources::screen;
02687     t_string topic_id = cfg["topic"];
02688     help::show_help(screen, topic_id.to_serialized());
02689 }
02690 // Helper namespace to do some subparts for message function
02691 namespace {
02692 
02693 /**
02694  * Helper to handle the speaker part of the message.
02695  *
02696  * @param event_info              event_info of message.
02697  * @param cfg                     cfg of message.
02698  *
02699  * @returns                       The unit who's the speaker or units->end().
02700  */
02701 unit_map::iterator handle_speaker(
02702         const game_events::queued_event& event_info,
02703         const vconfig& cfg,
02704         bool scroll)
02705 {
02706     unit_map *units = resources::units;
02707     game_display &screen = *resources::screen;
02708 
02709     unit_map::iterator speaker = units->end();
02710     const std::string speaker_str = cfg["speaker"];
02711 
02712     if(speaker_str == "unit") {
02713         speaker = units->find(event_info.loc1);
02714     } else if(speaker_str == "second_unit") {
02715         speaker = units->find(event_info.loc2);
02716     } else if(speaker_str != "narrator") {
02717         for(speaker = units->begin(); speaker != units->end(); ++speaker){
02718             if (game_events::unit_matches_filter(*speaker, cfg))
02719                 break;
02720         }
02721     }
02722     if(speaker != units->end()) {
02723         LOG_NG << "set speaker to '" << speaker->name() << "'\n";
02724         const map_location &spl = speaker->get_location();
02725         screen.highlight_hex(spl);
02726         if(scroll) {
02727             LOG_DP << "scrolling to speaker..\n";
02728             const int offset_from_center = std::max<int>(0, spl.y - 1);
02729             screen.scroll_to_tile(map_location(spl.x, offset_from_center));
02730         }
02731         screen.highlight_hex(spl);
02732     } else if(speaker_str == "narrator") {
02733         LOG_NG << "no speaker\n";
02734         screen.highlight_hex(map_location::null_location);
02735     } else {
02736         return speaker;
02737     }
02738 
02739     screen.draw(false);
02740     LOG_DP << "done scrolling to speaker...\n";
02741     return speaker;
02742 }
02743 
02744 /**
02745  * Helper to handle the image part of the message.
02746  *
02747  * @param cfg                     cfg of message.
02748  * @param speaker                 The speaker of the message.
02749  *
02750  * @returns                       The image to show.
02751  */
02752 std::string get_image(const vconfig& cfg, unit_map::iterator speaker)
02753 {
02754     std::string image = cfg["image"];
02755     if (image.empty() && speaker != resources::units->end())
02756     {
02757         image = speaker->big_profile();
02758 #ifndef LOW_MEM
02759         if(image == speaker->absolute_image()) {
02760             image += speaker->image_mods();
02761         }
02762 #endif
02763     }
02764     return image;
02765 }
02766 
02767 /**
02768  * Helper to handle the caption part of the message.
02769  *
02770  * @param cfg                     cfg of message.
02771  * @param speaker                 The speaker of the message.
02772  *
02773  * @returns                       The caption to show.
02774  */
02775 std::string get_caption(const vconfig& cfg, unit_map::iterator speaker)
02776 {
02777     std::string caption = cfg["caption"];
02778     if (caption.empty() && speaker != resources::units->end()) {
02779         caption = speaker->name();
02780         if(caption.empty()) {
02781             caption = speaker->type_name();
02782         }
02783     }
02784     return caption;
02785 }
02786 
02787 } // namespace
02788 
02789 struct message_user_choice : mp_sync::user_choice
02790 {
02791     vconfig cfg;
02792     unit_map::iterator speaker;
02793     vconfig text_input_element;
02794     bool has_text_input;
02795     const std::vector<std::string> &options;
02796 
02797     message_user_choice(const vconfig &c, const unit_map::iterator &s,
02798         const vconfig &t, bool ht, const std::vector<std::string> &o)
02799         : cfg(c), speaker(s), text_input_element(t)
02800         , has_text_input(ht), options(o)
02801     {}
02802 
02803     virtual config query_user() const
02804     {
02805         std::string image = get_image(cfg, speaker);
02806         std::string caption = get_caption(cfg, speaker);
02807 
02808         size_t right_offset = image.find("~RIGHT()");
02809         bool left_side = right_offset == std::string::npos;
02810         if (!left_side) {
02811             image.erase(right_offset);
02812         }
02813 
02814         // Parse input text, if not available all fields are empty
02815         std::string text_input_label = text_input_element["label"];
02816         std::string text_input_content = text_input_element["text"];
02817         unsigned input_max_size = text_input_element["max_length"].to_int(256);
02818         if (input_max_size > 1024 || input_max_size < 1) {
02819             lg::wml_error << "invalid maximum size for input "
02820                 << input_max_size << '\n';
02821             input_max_size = 256;
02822         }
02823 
02824         int option_chosen;
02825         int dlg_result = gui2::show_wml_message(left_side,
02826             resources::screen->video(), caption, cfg["message"],
02827             image, false, has_text_input, text_input_label,
02828             &text_input_content, input_max_size, options,
02829             &option_chosen);
02830 
02831         /* Since gui2::show_wml_message needs to do undrawing the
02832            chatlines can get garbled and look dirty on screen. Force a
02833            redraw to fix it. */
02834         /** @todo This hack can be removed once gui2 is finished. */
02835         resources::screen->invalidate_all();
02836         resources::screen->draw(true,true);
02837 
02838         if (dlg_result == gui2::twindow::CANCEL) {
02839             current_context->skip_messages = true;
02840         }
02841 
02842         config cfg;
02843         if (!options.empty()) cfg["value"] = option_chosen;
02844         if (has_text_input) cfg["text"] = text_input_content;
02845         return cfg;
02846     }
02847 
02848     virtual config random_choice(rand_rng::simple_rng &) const
02849     {
02850         return config();
02851     }
02852 };
02853 
02854 // Display a message dialog
02855 WML_HANDLER_FUNCTION(message, event_info, cfg)
02856 {
02857     // Check if there is any input to be made, if not the message may be skipped
02858     const vconfig::child_list menu_items = cfg.get_children("option");
02859 
02860     const vconfig::child_list text_input_elements = cfg.get_children("text_input");
02861     const bool has_text_input = (text_input_elements.size() == 1);
02862 
02863     bool has_input= (has_text_input || !menu_items.empty() );
02864 
02865     // skip messages during quick replay
02866     play_controller *controller = resources::controller;
02867     if(!has_input && (
02868              controller->is_skipping_replay() ||
02869              current_context->skip_messages
02870              ))
02871     {
02872         return;
02873     }
02874 
02875     // Check if this message is for this side
02876     std::string side_for_raw = cfg["side_for"];
02877     if (!side_for_raw.empty())
02878     {
02879         /* Always ignore side_for when the message has some input
02880            boxes, but display the error message only if side_for is
02881            used for an inactive side. */
02882         bool side_for_show = has_input;
02883         if (has_input && side_for_raw != str_cast(resources::controller->current_side()))
02884             lg::wml_error << "[message]side_for= cannot query any user input out of turn.\n";
02885 
02886         std::vector<std::string> side_for =
02887             utils::split(side_for_raw, ',', utils::STRIP_SPACES | utils::REMOVE_EMPTY);
02888         std::vector<std::string>::iterator itSide;
02889         size_t side;
02890 
02891         // Check if any of side numbers are human controlled
02892         for (itSide = side_for.begin(); itSide != side_for.end(); ++itSide)
02893         {
02894             side = lexical_cast_default<size_t>(*itSide);
02895             // Make sanity check that side number is good
02896             // then check if this side is human controlled.
02897             if (side > 0 && side <= resources::teams->size() &&
02898                 (*resources::teams)[side-1].is_human())
02899             {
02900                 side_for_show = true;
02901                 break;
02902             }
02903         }
02904         if (!side_for_show)
02905         {
02906             DBG_NG << "player isn't controlling side which should get message\n";
02907             return;
02908         }
02909     }
02910 
02911     unit_map::iterator speaker = handle_speaker(event_info, cfg, cfg["scroll"].to_bool(true));
02912     if (speaker == resources::units->end() && cfg["speaker"] != "narrator") {
02913         // No matching unit found, so the dialog can't come up.
02914         // Continue onto the next message.
02915         WRN_NG << "cannot show message\n";
02916         return;
02917     }
02918 
02919     std::vector<std::string> options;
02920     std::vector<vconfig::child_list> option_events;
02921 
02922     for(vconfig::child_list::const_iterator mi = menu_items.begin();
02923             mi != menu_items.end(); ++mi) {
02924         std::string msg_str = (*mi)["message"];
02925         if (!mi->has_child("show_if")
02926             || game_events::conditional_passed(mi->child("show_if")))
02927         {
02928             options.push_back(msg_str);
02929             option_events.push_back((*mi).get_children("command"));
02930         }
02931     }
02932 
02933     has_input = !options.empty() || has_text_input;
02934     if (!has_input && get_replay_source().is_skipping()) {
02935         // No input to get and the user is not interested either.
02936         return;
02937     }
02938 
02939     if (cfg.has_attribute("sound")) {
02940         sound::play_sound(cfg["sound"]);
02941     }
02942 
02943     if(text_input_elements.size()>1) {
02944         lg::wml_error << "too many text_input tags, only one accepted\n";
02945     }
02946 
02947     const vconfig text_input_element = has_text_input ?
02948         text_input_elements.front() : vconfig::empty_vconfig();
02949 
02950     int option_chosen = 0;
02951     std::string text_input_result;
02952 
02953     DBG_DP << "showing dialog...\n";
02954 
02955     message_user_choice msg(cfg, speaker, text_input_element, has_text_input,
02956         options);
02957     if (!has_input)
02958     {
02959         /* Always show the dialog if it has no input, whether we are
02960            replaying or not. */
02961         msg.query_user();
02962     }
02963     else
02964     {
02965         config choice = mp_sync::get_user_choice("input", msg, 0, true);
02966         option_chosen = choice["value"];
02967         text_input_result = choice["text"].str();
02968     }
02969 
02970     // Implement the consequences of the choice
02971     if(options.empty() == false) {
02972         if(size_t(option_chosen) >= menu_items.size()) {
02973             std::stringstream errbuf;
02974             errbuf << "invalid choice (" << option_chosen
02975                 << ") was specified, choice 0 to " << (menu_items.size() - 1)
02976                 << " was expected.\n";
02977             replay::process_error(errbuf.str());
02978             return;
02979         }
02980 
02981         foreach (const vconfig &cmd, option_events[option_chosen]) {
02982             handle_event_commands(event_info, cmd);
02983         }
02984     }
02985     if(has_text_input) {
02986         std::string variable_name=text_input_element["variable"];
02987         if(variable_name.empty())
02988             variable_name="input";
02989         resources::state_of_game->set_variable(variable_name, text_input_result);
02990     }
02991 }
02992 
02993 // Adding/removing new time_areas dynamically with Standard Location Filters.
02994 WML_HANDLER_FUNCTION(time_area, /*event_info*/, cfg)
02995 {
02996     log_scope("time_area");
02997 
02998     bool remove = cfg["remove"].to_bool();
02999     std::string ids = cfg["id"];
03000 
03001     if(remove) {
03002         const std::vector<std::string> id_list =
03003             utils::split(ids, ',', utils::STRIP_SPACES | utils::REMOVE_EMPTY);
03004         foreach(const std::string& id, id_list) {
03005             resources::tod_manager->remove_time_area(id);
03006             LOG_NG << "event WML removed time_area '" << id << "'\n";
03007         }
03008     }
03009     else {
03010         std::string id;
03011         if(ids.find(',') != std::string::npos) {
03012             id = utils::split(ids,',',utils::STRIP_SPACES | utils::REMOVE_EMPTY).front();
03013             ERR_NG << "multiple ids for inserting a new time_area; will use only the first\n";
03014         } else {
03015             id = ids;
03016         }
03017         std::set<map_location> locs;
03018         const terrain_filter filter(cfg, *resources::units);
03019         filter.get_locations(locs, true);
03020         config parsed_cfg = cfg.get_parsed_config();
03021         resources::tod_manager->add_time_area(id, locs, parsed_cfg);
03022         LOG_NG << "event WML inserted time_area '" << id << "'\n";
03023     }
03024 }
03025 
03026 //Replacing the current time of day schedule
03027 WML_HANDLER_FUNCTION(replace_schedule, /*event_info*/, cfg)
03028 {
03029     if(cfg.get_children("time").empty()) {
03030         ERR_NG << "attempted to to replace ToD schedule with empty schedule\n";
03031     } else {
03032         resources::tod_manager->replace_schedule(cfg.get_parsed_config());
03033         resources::screen->new_turn();
03034         LOG_NG << "replaced ToD schedule\n";
03035     }
03036 }
03037 
03038 WML_HANDLER_FUNCTION(allow_end_turn, /*event_info*/, /*cfg*/)
03039 {
03040     resources::state_of_game->set_allow_end_turn(true);
03041 }
03042 
03043 WML_HANDLER_FUNCTION(disallow_end_turn, /*event_info*/, /*cfg*/)
03044 {
03045     resources::state_of_game->set_allow_end_turn(false);
03046 }
03047 
03048 // Adding new events
03049 WML_HANDLER_FUNCTION(event, /*event_info*/, cfg)
03050 {
03051     if (cfg["remove"].to_bool(false)) {
03052         event_handlers.remove_event_handler(cfg["id"]);
03053     } else if (!cfg["delayed_variable_substitution"].to_bool(true)) {
03054         event_handlers.add_event_handler(game_events::event_handler(cfg.get_parsed_config()));
03055     } else {
03056         event_handlers.add_event_handler(game_events::event_handler(cfg.get_config()));
03057     }
03058 }
03059 
03060 // Experimental map replace
03061 WML_HANDLER_FUNCTION(replace_map, /*event_info*/, cfg)
03062 {
03063     gamemap *game_map = resources::game_map;
03064 
03065     gamemap map(*game_map);
03066     try {
03067         map.read(cfg["map"], false);
03068     } catch(incorrect_map_format_error&) {
03069         lg::wml_error << "replace_map: Unable to load map " << cfg["map"] << "\n";
03070         return;
03071     } catch(twml_exception& e) {
03072         e.show(*resources::screen);
03073         return;
03074     }
03075     if (map.total_width() > game_map->total_width()
03076     || map.total_height() > game_map->total_height()) {
03077         if (!cfg["expand"].to_bool()) {
03078             lg::wml_error << "replace_map: Map dimension(s) increase but expand is not set\n";
03079             return;
03080         }
03081     }
03082     if (map.total_width() < game_map->total_width()
03083     || map.total_height() < game_map->total_height()) {
03084         if (!cfg["shrink"].to_bool()) {
03085             lg::wml_error << "replace_map: Map dimension(s) decrease but shrink is not set\n";
03086             return;
03087         }
03088         unit_map *units = resources::units;
03089         unit_map::iterator itor;
03090         for (itor = units->begin(); itor != units->end(); ) {
03091             if (!map.on_board(itor->get_location())) {
03092                 if (!try_add_unit_to_recall_list(itor->get_location(), *itor)) {
03093                     lg::wml_error << "replace_map: Cannot add a unit that would become off-map to the recall list\n";
03094                 }
03095                 units->erase(itor++);
03096             } else {
03097                 ++itor;
03098             }
03099         }
03100     }
03101     *game_map = map;
03102     resources::screen->reload_map();
03103     screen_needs_rebuild = true;
03104     ai::manager::raise_map_changed();
03105 }
03106 
03107 // Experimental data persistence
03108 WML_HANDLER_FUNCTION(set_global_variable,/**/,pcfg)
03109 {
03110     if (get_replay_source().at_end() || (network::nconnections() != 0))
03111         verify_and_set_global_variable(pcfg);
03112 }
03113 WML_HANDLER_FUNCTION(get_global_variable,/**/,pcfg)
03114 {
03115     verify_and_get_global_variable(pcfg);
03116 }
03117 WML_HANDLER_FUNCTION(clear_global_variable,/**/,pcfg)
03118 {
03119     if (get_replay_source().at_end() || (network::nconnections() != 0))
03120         verify_and_clear_global_variable(pcfg);
03121 }
03122 
03123 /** Handles all the different types of actions that can be triggered by an event. */
03124 
03125 static void commit_wmi_commands() {
03126     // Commit WML Menu Item command changes
03127     while(wmi_command_changes.size() > 0) {
03128         wmi_command_change wcc = wmi_command_changes.front();
03129         const bool is_empty_command = wcc.second->empty();
03130 
03131         wml_menu_item*& mref = resources::state_of_game->wml_menu_items[wcc.first];
03132         const bool has_current_handler = !mref->command.empty();
03133 
03134         config::attribute_value event_id = (*wcc.second)["id"];
03135         if(event_id.empty()) {
03136             event_id = mref->event_id;
03137             if(!event_id.empty())
03138                 (*wcc.second)["id"] = event_id;
03139         }
03140         mref->command = *(wcc.second);
03141         mref->command["name"] = mref->name;
03142         mref->command["first_time_only"] = false;
03143 
03144         if(has_current_handler) {
03145             foreach(game_events::event_handler& hand, event_handlers) {
03146                 if(hand.is_menu_item() && hand.matches_name(mref->name)) {
03147                     LOG_NG << "changing command for " << mref->name << " to:\n" << *wcc.second;
03148                     hand = game_events::event_handler(mref->command, true);
03149                 }
03150             }
03151         } else if(!is_empty_command) {
03152             LOG_NG << "setting command for " << mref->name << " to:\n" << *wcc.second;
03153             event_handlers.add_event_handler(game_events::event_handler(mref->command, true));
03154         }
03155 
03156         delete wcc.second;
03157         wmi_command_changes.erase(wmi_command_changes.begin());
03158     }
03159 }
03160 
03161 static bool process_event(game_events::event_handler& handler, const game_events::queued_event& ev)
03162 {
03163     if(handler.disabled())
03164         return false;
03165 
03166     unit_map *units = resources::units;
03167     unit_map::iterator unit1 = units->find(ev.loc1);
03168     unit_map::iterator unit2 = units->find(ev.loc2);
03169     bool filtered_unit1 = false, filtered_unit2 = false;
03170     scoped_xy_unit first_unit("unit", ev.loc1.x, ev.loc1.y, *units);
03171     scoped_xy_unit second_unit("second_unit", ev.loc2.x, ev.loc2.y, *units);
03172     scoped_weapon_info first_weapon("weapon", ev.data.child("first"));
03173     scoped_weapon_info second_weapon("second_weapon", ev.data.child("second"));
03174     vconfig filters(handler.get_config());
03175 
03176 
03177     foreach (const vconfig &condition, filters.get_children("filter_condition"))
03178     {
03179         if (!game_events::conditional_passed(condition)) {
03180             return false;
03181         }
03182     }
03183 
03184     foreach (const vconfig &f, filters.get_children("filter"))
03185     {
03186         if (unit1 == units->end() || !game_events::unit_matches_filter(*unit1, f)) {
03187             return false;
03188         }
03189         if (!f.empty()) {
03190             filtered_unit1 = true;
03191         }
03192     }
03193 
03194     foreach (const vconfig &f, filters.get_children("filter_side"))
03195     {
03196         side_filter ssf(f);
03197         const int current_side = resources::controller->current_side();
03198         if(!ssf.match(current_side)) return false;
03199     }
03200 
03201     vconfig::child_list special_filters = filters.get_children("filter_attack");
03202     bool special_matches = special_filters.empty();
03203     foreach (const vconfig &f, special_filters)
03204     {
03205         if (unit1 != units->end() && game_events::matches_special_filter(ev.data.child("first"), f)) {
03206             special_matches = true;
03207         }
03208         if (!f.empty()) {
03209             filtered_unit1 = true;
03210         }
03211     }
03212     if(!special_matches) {
03213         return false;
03214     }
03215 
03216     foreach (const vconfig &f, filters.get_children("filter_second"))
03217     {
03218         if (unit2 == units->end() || !game_events::unit_matches_filter(*unit2, f)) {
03219             return false;
03220         }
03221         if (!f.empty()) {
03222             filtered_unit2 = true;
03223         }
03224     }
03225 
03226     special_filters = filters.get_children("filter_second_attack");
03227     special_matches = special_filters.empty();
03228     foreach (const vconfig &f, special_filters)
03229     {
03230         if (unit2 != units->end() && game_events::matches_special_filter(ev.data.child("second"), f)) {
03231             special_matches = true;
03232         }
03233         if (!f.empty()) {
03234             filtered_unit2 = true;
03235         }
03236     }
03237     if(!special_matches) {
03238         return false;
03239     }
03240     if (ev.loc1.requires_unit() && filtered_unit1 &&
03241         (unit1 == units->end() || !ev.loc1.matches_unit(*unit1))) {
03242         // Wrong or missing entity at src location
03243         return false;
03244     }
03245     if (ev.loc2.requires_unit() && filtered_unit2 &&
03246         (unit2 == units->end() || !ev.loc2.matches_unit(*unit2))) {
03247         // Wrong or missing entity at dst location
03248         return false;
03249     }
03250 
03251     // The event hasn't been filtered out, so execute the handler.
03252     scoped_context evc;
03253     handler.handle_event(ev);
03254 
03255     if(ev.name == "select") {
03256         resources::state_of_game->last_selected = ev.loc1;
03257     }
03258 
03259     if (screen_needs_rebuild) {
03260         screen_needs_rebuild = false;
03261         game_display *screen = resources::screen;
03262         screen->recalculate_minimap();
03263         screen->invalidate_all();
03264         screen->rebuild_all();
03265     }
03266 
03267 
03268     return current_context->mutated;
03269 }
03270 
03271 namespace game_events {
03272 
03273     event_handler::event_handler(const config &cfg, bool imi) :
03274         first_time_only_(cfg["first_time_only"].to_bool(true)),
03275         disabled_(false), is_menu_item_(imi), cfg_(cfg)
03276     {}
03277 
03278     void event_handler::handle_event(const game_events::queued_event& event_info)
03279     {
03280         if (first_time_only_)
03281         {
03282             disabled_ = true;
03283         }
03284 
03285         if (is_menu_item_) {
03286             DBG_NG << cfg_["name"] << " will now invoke the following command(s):\n" << cfg_;
03287         }
03288 
03289         handle_event_commands(event_info, vconfig(cfg_));
03290     }
03291 
03292     void handle_event_commands(const game_events::queued_event& event_info, const vconfig &cfg)
03293     {
03294         resources::lua_kernel->run_wml_action("command", cfg, event_info);
03295     }
03296 
03297     void handle_event_command(const std::string &cmd,
03298         const game_events::queued_event &event_info, const vconfig &cfg)
03299     {
03300         log_scope2(log_engine, "handle_event_command");
03301         LOG_NG << "handling command '" << cmd << "' from "
03302             << (cfg.is_volatile()?"volatile ":"") << "cfg 0x"
03303             << std::hex << std::setiosflags(std::ios::uppercase)
03304             << reinterpret_cast<uintptr_t>(&cfg.get_config()) << std::dec << "\n";
03305 
03306         if (!resources::lua_kernel->run_wml_action(cmd, cfg, event_info))
03307         {
03308             ERR_NG << "Couldn't find function for wml tag: "<< cmd <<"\n";
03309         }
03310 
03311         DBG_NG << "done handling command...\n";
03312     }
03313 
03314     bool event_handler::matches_name(const std::string &name) const
03315     {
03316         const t_string& t_my_names = cfg_["name"];
03317         const std::string& my_names = t_my_names;
03318         std::string::const_iterator itor,
03319             it_begin = my_names.begin(),
03320             it_end = my_names.end(),
03321             match_it = name.begin(),
03322             match_begin = name.begin(),
03323             match_end = name.end();
03324         int skip_count = 0;
03325         for(itor = it_begin; itor != it_end; ++itor) {
03326             bool do_eat = false,
03327                 do_skip = false;
03328             switch(*itor) {
03329             case ',':
03330                 if(itor - it_begin - skip_count == match_it - match_begin && match_it == match_end) {
03331                     return true;
03332                 }
03333                 it_begin = itor + 1;
03334                 match_it = match_begin;
03335                 skip_count = 0;
03336                 continue;
03337             case '\f':
03338             case '\n':
03339             case '\r':
03340             case '\t':
03341             case '\v':
03342                 do_skip = (match_it == match_begin || match_it == match_end);
03343                 break;
03344             case ' ':
03345                 do_skip = (match_it == match_begin || match_it == match_end);
03346                 // fall through to case '_'
03347             case '_':
03348                 do_eat = (match_it != match_end && (*match_it == ' ' || *match_it == '_'));
03349                 break;
03350             default:
03351                 do_eat = (match_it != match_end && *match_it == *itor);
03352                 break;
03353             }
03354             if(do_eat) {
03355                 ++match_it;
03356             } else if(do_skip) {
03357                 ++skip_count;
03358             } else {
03359                 itor = std::find(itor, it_end, ',');
03360                 if(itor == it_end) {
03361                     return false;
03362                 }
03363                 it_begin = itor + 1;
03364                 match_it = match_begin;
03365                 skip_count = 0;
03366             }
03367         }
03368         if(itor - it_begin - skip_count == match_it - match_begin && match_it == match_end) {
03369             return true;
03370         }
03371         return false;
03372     }
03373 
03374     bool matches_special_filter(const config &cfg, const vconfig& filter)
03375     {
03376         if (!cfg) {
03377             WRN_NG << "attempt to filter attack for an event with no attack data.\n";
03378             // better to not execute the event (so the problem is more obvious)
03379             return false;
03380         }
03381         const attack_type attack(cfg);
03382         bool matches = attack.matches_filter(filter.get_parsed_config());
03383 
03384         // Handle [and], [or], and [not] with in-order precedence
03385         vconfig::all_children_iterator cond_i = filter.ordered_begin();
03386         vconfig::all_children_iterator cond_end = filter.ordered_end();
03387         while(cond_i != cond_end)
03388         {
03389             const std::string& cond_name = cond_i.get_key();
03390             const vconfig& cond_filter = cond_i.get_child();
03391 
03392             // Handle [and]
03393             if(cond_name == "and")
03394             {
03395                 matches = matches && matches_special_filter(cfg, cond_filter);
03396             }
03397             // Handle [or]
03398             else if(cond_name == "or")
03399             {
03400                 matches = matches || matches_special_filter(cfg, cond_filter);
03401             }
03402             // Handle [not]
03403             else if(cond_name == "not")
03404             {
03405                 matches = matches && !matches_special_filter(cfg, cond_filter);
03406             }
03407             ++cond_i;
03408         }
03409         return matches;
03410     }
03411 
03412     bool unit_matches_filter(const unit &u, const vconfig& filter)
03413     {
03414         return u.matches_filter(filter, u.get_location());
03415     }
03416 
03417     static std::set<std::string> unit_wml_ids;
03418 
03419     manager::manager(const config& cfg)
03420         : variable_manager()
03421     {
03422         assert(!manager_running);
03423         foreach (const config &ev, cfg.child_range("event")) {
03424             event_handlers.add_event_handler(game_events::event_handler(ev));
03425         }
03426         foreach (const std::string &id, utils::split(cfg["unit_wml_ids"])) {
03427             unit_wml_ids.insert(id);
03428         }
03429 
03430         resources::lua_kernel = new LuaKernel(cfg);
03431         manager_running = true;
03432 
03433         foreach (static_wml_action_map::value_type &action, static_wml_actions) {
03434             resources::lua_kernel->set_wml_action(action.first, action.second);
03435         }
03436 
03437         const std::string used = cfg["used_items"];
03438         if(!used.empty()) {
03439             const std::vector<std::string>& v = utils::split(used);
03440             for(std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i) {
03441                 used_items.insert(*i);
03442             }
03443         }
03444         int wmi_count = 0;
03445         typedef std::pair<std::string, wml_menu_item *> item;
03446         foreach (const item &itor, resources::state_of_game->wml_menu_items) {
03447             if (!itor.second->command.empty()) {
03448                 event_handlers.add_event_handler(game_events::event_handler(itor.second->command, true));
03449             }
03450             ++wmi_count;
03451         }
03452         if(wmi_count > 0) {
03453             LOG_NG << wmi_count << " WML menu items found, loaded." << std::endl;
03454         }
03455     }
03456 
03457     void write_events(config& cfg)
03458     {
03459         assert(manager_running);
03460         foreach (const game_events::event_handler &eh, event_handlers) {
03461             if (eh.disabled() || eh.is_menu_item()) continue;
03462             cfg.add_child("event", eh.get_config());
03463         }
03464 
03465         std::stringstream used;
03466         std::set<std::string>::const_iterator u;
03467         for(u = used_items.begin(); u != used_items.end(); ++u) {
03468             if(u != used_items.begin())
03469                 used << ",";
03470 
03471             used << *u;
03472         }
03473 
03474         cfg["used_items"] = used.str();
03475         std::stringstream ids;
03476         for(u = unit_wml_ids.begin(); u != unit_wml_ids.end(); ++u) {
03477             if(u != unit_wml_ids.begin())
03478                 ids << ",";
03479 
03480             ids << *u;
03481         }
03482 
03483         cfg["unit_wml_ids"] = ids.str();
03484 
03485         if (resources::soundsources)
03486             resources::soundsources->write_sourcespecs(cfg);
03487 
03488         resources::lua_kernel->save_game(cfg);
03489     }
03490 
03491     manager::~manager() {
03492         assert(manager_running);
03493         manager_running = false;
03494         events_queue.clear();
03495         event_handlers.clear();
03496         reports::reset_generators();
03497         delete resources::lua_kernel;
03498         resources::lua_kernel = NULL;
03499         unit_wml_ids.clear();
03500         used_items.clear();
03501     }
03502 
03503     void raise(const std::string& event,
03504             const entity_location& loc1,
03505             const entity_location& loc2,
03506             const config& data)
03507     {
03508         assert(manager_running);
03509         if(!events_init())
03510             return;
03511 
03512         DBG_EH << "raising event: " << event << "\n";
03513 
03514         events_queue.push_back(game_events::queued_event(event,loc1,loc2,data));
03515     }
03516 
03517     bool fire(const std::string& event,
03518             const entity_location& loc1,
03519             const entity_location& loc2,
03520             const config& data)
03521     {
03522         assert(manager_running);
03523         raise(event,loc1,loc2,data);
03524         return pump();
03525     }
03526 
03527     void add_events(const config::const_child_itors &cfgs, const std::string& type)
03528     {
03529         if(!type.empty()) {
03530             if(std::find(unit_wml_ids.begin(),unit_wml_ids.end(),type) != unit_wml_ids.end()) return;
03531             unit_wml_ids.insert(type);
03532         }
03533         foreach (const config &new_ev, cfgs) {
03534             if(type.empty() && new_ev["id"].empty())
03535             {
03536                 WRN_NG << "attempt to add an [event] with empty id=, ignoring \n";
03537                 continue;
03538             }
03539             event_handlers.add_event_handler(game_events::event_handler(new_ev));
03540         }
03541     }
03542 
03543     void commit()
03544     {
03545         DBG_EH << "committing new event handlers, number of pump_instances: " <<
03546             pump_manager::count() << "\n";
03547         event_handlers.commit_buffer();
03548         commit_wmi_commands();
03549         // Dialogs can only be shown if the display is not locked
03550         if (!resources::screen->video().update_locked()) {
03551             show_wml_errors();
03552             show_wml_messages();
03553         }
03554     }
03555 
03556     bool pump()
03557     {
03558         //ensure the whiteboard doesn't attempt to build its future unit map
03559         //for the duration of this method
03560         wb::real_map real_unit_map;
03561 
03562         assert(manager_running);
03563         if(!events_init())
03564             return false;
03565 
03566         pump_manager pump_instance;
03567         if(pump_manager::count() >= game_config::max_loop) {
03568             ERR_NG << "game_events::pump() waiting to process new events because "
03569                 << "recursion level would exceed maximum " << game_config::max_loop << '\n';
03570             return false;
03571         }
03572 
03573         if(!lg::debug.dont_log("event_handler")) {
03574             std::stringstream ss;
03575             foreach(const game_events::queued_event& ev, events_queue) {
03576                 ss << "name=" << ev.name << "; ";
03577             }
03578             DBG_EH << "processing queued events: " << ss.str() << "\n";
03579         }
03580 
03581         //Notify the whiteboard of any event; this is used to track when moves, recruits, etc. happen
03582         if(!events_queue.empty()) {
03583             resources::whiteboard->on_gamestate_change();
03584         }
03585 
03586         bool result = false;
03587         while(events_queue.empty() == false) {
03588             if(pump_manager::count() <= 1)
03589                 event_handlers.start_buffering();
03590             game_events::queued_event ev = events_queue.front();
03591             events_queue.pop_front();   // pop now for exception safety
03592             const std::string& event_name = ev.name;
03593 
03594             // Clear the unit cache, since the best clearing time is hard to figure out
03595             // due to status changes by WML. Every event will flush the cache.
03596             unit::clear_status_caches();
03597 
03598             /* bool lua_event_triggered = */ resources::lua_kernel->run_event(ev);
03599 
03600             bool init_event_vars = true;
03601 
03602             foreach(game_events::event_handler& handler, event_handlers) {
03603                 if(!handler.matches_name(event_name))
03604                     continue;
03605                 // Set the variables for the event
03606                 if (init_event_vars) {
03607                     resources::state_of_game->get_variable("x1") = ev.loc1.x + 1;
03608                     resources::state_of_game->get_variable("y1") = ev.loc1.y + 1;
03609                     resources::state_of_game->get_variable("x2") = ev.loc2.x + 1;
03610                     resources::state_of_game->get_variable("y2") = ev.loc2.y + 1;
03611                     init_event_vars = false;
03612                 }
03613 
03614                 DBG_EH << "processing event " << event_name << " with id="<<
03615                     handler.get_config()["id"] << "\n";
03616                 if(process_event(handler, ev))
03617                 {
03618                     result = true;
03619                 }
03620             }
03621 
03622             if(pump_manager::count() <= 1)
03623                 event_handlers.stop_buffering();
03624             // Only commit new handlers when finished iterating over event_handlers.
03625             commit();
03626         }
03627 
03628         return result;
03629     }
03630 
03631     entity_location::entity_location(const map_location &loc, size_t id)
03632         : map_location(loc), id_(id)
03633     {}
03634 
03635     entity_location::entity_location(const unit &u)
03636         : map_location(u.get_location()), id_(u.underlying_id())
03637     {}
03638 
03639     bool entity_location::matches_unit(const unit& u) const
03640     {
03641         return id_ == u.underlying_id();
03642     }
03643 
03644     bool entity_location::requires_unit() const
03645     {
03646         return id_ > 0;
03647     }
03648 
03649 } // end namespace game_events (2)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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