game_events.cpp

Go to the documentation of this file.
00001 /* $Id: game_events.cpp 26769 2008-05-22 13:21:04Z mog $ */
00002 /*
00003    Copyright (C) 2003 - 2008 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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file game_events.cpp
00016 //! Processing of WML-events.
00017 
00018 #include "global.hpp"
00019 #include "actions.hpp"
00020 #include "construct_dialog.hpp"
00021 #include "game_display.hpp"
00022 #include "dialogs.hpp"
00023 #include "game_errors.hpp"
00024 #include "game_events.hpp"
00025 #include "image.hpp"
00026 #include "language.hpp"
00027 #include "log.hpp"
00028 #include "map.hpp"
00029 #include "menu_events.hpp"
00030 #include "game_preferences.hpp"
00031 #include "replay.hpp"
00032 #include "SDL_timer.h"
00033 #include "sound.hpp"
00034 #include "team.hpp"
00035 #include "terrain_filter.hpp"
00036 #include "unit_display.hpp"
00037 #include "util.hpp"
00038 #include "gettext.hpp"
00039 #include "serialization/string_utils.hpp"
00040 #include "wml_exception.hpp"
00041 #include "foreach.hpp"
00042 
00043 #include <cassert>
00044 #include <cstdlib>
00045 #include <deque>
00046 #include <iostream>
00047 #include <iterator>
00048 #include <set>
00049 #include <string>
00050 
00051 #define DBG_NG LOG_STREAM(debug, engine)
00052 #define LOG_NG LOG_STREAM(info, engine)
00053 #define WRN_NG LOG_STREAM(warn, engine)
00054 #define ERR_NG LOG_STREAM(err, engine)
00055 #define DBG_DP LOG_STREAM(debug, display)
00056 #define LOG_DP LOG_STREAM(info, display)
00057 #define ERR_CF LOG_STREAM(err, config)
00058 
00059 #define LOG_NO LOG_STREAM(info, notifs)
00060 #define WRN_NO LOG_STREAM(warn, notifs)
00061 #define ERR_NO LOG_STREAM(err, notifs)
00062 
00063 namespace {
00064 
00065 game_display* screen = NULL;
00066 soundsource::manager* soundsources = NULL;
00067 gamemap* game_map = NULL;
00068 unit_map* units = NULL;
00069 std::vector<team>* teams = NULL;
00070 game_state* state_of_game = NULL;
00071 gamestatus* status_ptr = NULL;
00072 int floating_label = 0;
00073 Uint32 unit_mutations = 0;
00074 
00075 class event_handler;
00076 std::vector< event_handler > new_handlers;
00077 typedef std::pair< std::string, config* > wmi_command_change;
00078 std::vector< wmi_command_change > wmi_command_changes;
00079 
00080 const gui::msecs prevent_misclick_duration = 10;
00081 const gui::msecs average_frame_time = 30;
00082 
00083 class wml_event_dialog : public gui::message_dialog {
00084 public:
00085     wml_event_dialog(game_display &disp, const std::string& title="", const std::string& message="", const gui::DIALOG_TYPE type=gui::MESSAGE)
00086         : message_dialog(disp, title, message, type)
00087     {}
00088     void action(gui::dialog_process_info &info) {
00089         if(result() == gui::CLOSE_DIALOG && !info.key_down && info.key[SDLK_ESCAPE]) {
00090             set_result(gui::ESCAPE_DIALOG);
00091         }
00092     }
00093 };
00094 
00095 } // end anonymous namespace (1)
00096 
00097 #ifdef _MSC_VER
00098 // std::getline might be broken in Visual Studio so show a warning
00099  #if _MSC_VER < 1300
00100   #ifndef GETLINE_PATCHED
00101    #pragma message("warning: the std::getline implementation in your compiler might be broken.")
00102    #pragma message(" http://support.microsoft.com/default.aspx?scid=kb;EN-US;q240015")
00103   #endif
00104  #endif
00105 #endif
00106 
00107 /**
00108  * Shows a summary of the errors encountered in WML thusfar,
00109  * to avoid a lot of the same messages to be shown.
00110  * Identical messages are shown once, with (between braces)
00111  * the number of times that message was encountered.
00112  * The order in which the messages are shown does not need
00113  * to be the order in which these messages are encountered.
00114  * Messages are always written to std::cerr.
00115  */
00116 static void show_wml_errors()
00117 {
00118     // Get all unique messages in messages,
00119     // with the number of encounters for these messages
00120     std::map<std::string, int> messages;
00121     while(true) {
00122         std::string msg;
00123         std::getline(lg::wml_error, msg);
00124 
00125         if(lg::wml_error.eof()) {
00126             break;
00127         }
00128 
00129         if(msg == "") {
00130             continue;
00131         }
00132 
00133         if(messages.find(msg) == messages.end()) {
00134             messages[msg] = 1;
00135         } else {
00136             messages[msg]++;
00137         }
00138     }
00139     // Make sure the eof flag is cleared otherwise no new messages are shown
00140     lg::wml_error.clear();
00141 
00142     // Show the messages collected
00143     std::string caption = "Deprecated WML found";
00144     for(std::map<std::string, int>::const_iterator itor = messages.begin();
00145             itor != messages.end(); ++itor) {
00146 
00147         std::stringstream msg;
00148         msg << itor->first;
00149         if(itor->second > 1) {
00150             msg << " (" << itor->second << ")";
00151         }
00152 
00153         screen->add_chat_message(time(NULL), caption, 0, msg.str(),
00154                 game_display::MESSAGE_PUBLIC, false);
00155         std::cerr << caption << ": " << msg.str() << '\n';
00156     }
00157 }
00158 
00159 namespace game_events {
00160 
00161 static bool unit_matches_filter(const unit& u, const vconfig filter,const gamemap::location& loc);
00162 static bool matches_special_filter(const config* cfg, const vconfig filter);
00163 
00164 game_state* get_state_of_game()
00165 {
00166     return state_of_game;
00167 }
00168 
00169 static bool internal_conditional_passed(const unit_map* units,
00170                         const vconfig cond, bool& backwards_compat)
00171 {
00172 
00173     // If the if statement requires we have a certain unit,
00174     // then check for that.
00175     const vconfig::child_list& have_unit = cond.get_children("have_unit");
00176     backwards_compat = backwards_compat && have_unit.empty();
00177     for(vconfig::child_list::const_iterator u = have_unit.begin(); u != have_unit.end(); ++u) {
00178 
00179         if(units == NULL)
00180             return false;
00181 
00182         unit_map::const_iterator itor;
00183         for(itor = units->begin(); itor != units->end(); ++itor) {
00184             if(itor->second.hitpoints() > 0 && game_events::unit_matches_filter(itor, *u)) {
00185                 break;
00186             }
00187         }
00188 
00189         if(itor == units->end()) {
00190             return false;
00191         }
00192     }
00193 
00194     // If the if statement requires we have a certain location,
00195     // then check for that.
00196     const vconfig::child_list& have_location = cond.get_children("have_location");
00197     backwards_compat = backwards_compat && have_location.empty();
00198     for(vconfig::child_list::const_iterator v = have_location.begin(); v != have_location.end(); ++v) {
00199         std::set<gamemap::location> res;
00200         assert(game_map != NULL && units != NULL && status_ptr != NULL);
00201         terrain_filter(*v, *game_map, *status_ptr, *units).get_locations(res);
00202         if(res.empty()) {
00203             return false;
00204         }
00205     }
00206 
00207     // Check against each variable statement,
00208     // to see if the variable matches the conditions or not.
00209     const vconfig::child_list& variables = cond.get_children("variable");
00210     backwards_compat = backwards_compat && variables.empty();
00211     for(vconfig::child_list::const_iterator var = variables.begin(); var != variables.end(); ++var) {
00212         const vconfig& values = *var;
00213 
00214         const std::string name = values["name"];
00215         assert(state_of_game != NULL);
00216         const std::string& value = state_of_game->get_variable_const(name);
00217 
00218         const double num_value = atof(value.c_str());
00219 
00220         const std::string equals = values["equals"];
00221         if(values.get_attribute("equals") != "" && value != equals) {
00222             return false;
00223         }
00224 
00225         const std::string numerical_equals = values["numerical_equals"];
00226         if(values.get_attribute("numerical_equals") != "" && atof(numerical_equals.c_str()) != num_value){
00227             return false;
00228         }
00229 
00230         const std::string not_equals = values["not_equals"];
00231         if(values.get_attribute("not_equals") != "" && not_equals == value) {
00232             return false;
00233         }
00234 
00235         const std::string numerical_not_equals = values["numerical_not_equals"];
00236         if(values.get_attribute("numerical_not_equals") != "" && atof(numerical_not_equals.c_str()) == num_value){
00237             return false;
00238         }
00239 
00240         const std::string greater_than = values["greater_than"];
00241         if(values.get_attribute("greater_than") != "" && atof(greater_than.c_str()) >= num_value){
00242             return false;
00243         }
00244 
00245         const std::string less_than = values["less_than"];
00246         if(values.get_attribute("less_than") != "" && atof(less_than.c_str()) <= num_value){
00247             return false;
00248         }
00249 
00250         const std::string greater_than_equal_to = values["greater_than_equal_to"];
00251         if(values.get_attribute("greater_than_equal_to") != "" && atof(greater_than_equal_to.c_str()) > num_value){
00252             return false;
00253         }
00254 
00255         const std::string less_than_equal_to = values["less_than_equal_to"];
00256         if(values.get_attribute("less_than_equal_to") != "" && atof(less_than_equal_to.c_str()) < num_value) {
00257             return false;
00258         }
00259         const std::string boolean_equals = values["boolean_equals"];
00260         if(values.get_attribute("boolean_equals") != ""
00261         && (utils::string_bool(value) != utils::string_bool(boolean_equals))) {
00262             return false;
00263         }
00264         const std::string boolean_not_equals = values["boolean_not_equals"];
00265         if(values.get_attribute("boolean_not_equals") != ""
00266         && (utils::string_bool(value) == utils::string_bool(boolean_not_equals))) {
00267             return false;
00268         }
00269         const std::string contains = values["contains"];
00270         if(values.get_attribute("contains") != "" && value.find(contains) == std::string::npos) {
00271             return false;
00272         }
00273     }
00274     return true;
00275 }
00276 
00277 bool conditional_passed(const unit_map* units,
00278                         const vconfig cond, bool backwards_compat)
00279 {
00280     bool allow_backwards_compat = backwards_compat = backwards_compat &&
00281         utils::string_bool(cond["backwards_compat"],true);
00282     bool matches = internal_conditional_passed(units, cond, allow_backwards_compat);
00283 
00284     // Handle [and], [or], and [not] with in-order precedence
00285     int or_count = 0;
00286     vconfig::all_children_iterator cond_i = cond.ordered_begin();
00287     vconfig::all_children_iterator cond_end = cond.ordered_end();
00288     while(cond_i != cond_end)
00289     {
00290         const std::string& cond_name = cond_i.get_key();
00291         const vconfig& cond_filter = cond_i.get_child();
00292 
00293         // Handle [and]
00294         if(cond_name == "and")
00295         {
00296             matches = matches && conditional_passed(units, cond_filter, backwards_compat);
00297             backwards_compat = false;
00298         }
00299         // Handle [or]
00300         else if(cond_name == "or")
00301         {
00302             matches = matches || conditional_passed(units, cond_filter, backwards_compat);
00303             ++or_count;
00304         }
00305         // Handle [not]
00306         else if(cond_name == "not")
00307         {
00308             matches = matches && !conditional_passed(units, cond_filter, backwards_compat);
00309             backwards_compat = false;
00310         }
00311         ++cond_i;
00312     }
00313     // Check for deprecated [or] syntax
00314     if(matches && or_count > 1 && allow_backwards_compat)
00315     {
00316         lg::wml_error << "possible deprecated [or] syntax: now forcing re-interpretation\n";
00317         //! @todo For now we will re-interpret it according to the old rules,
00318         // but this should be later to prevent re-interpretation errors.
00319         const vconfig::child_list& orcfgs = cond.get_children("or");
00320         for(unsigned int i=0; i < orcfgs.size(); ++i) {
00321             if(conditional_passed(units, orcfgs[i])) {
00322                 return true;
00323             }
00324         }
00325         return false;
00326     }
00327     return matches;
00328 }
00329 
00330 } // end namespace game_events (1)
00331 
00332 namespace {
00333 
00334 std::set<std::string> used_items;
00335 
00336 } // end anonymous namespace (2)
00337 
00338 static bool events_init() { return screen != NULL; }
00339 
00340 namespace {
00341 
00342 struct queued_event {
00343     queued_event(const std::string& name, const game_events::entity_location& loc1,
00344                                           const game_events::entity_location& loc2,
00345                                           const config& data)
00346             : name(name), loc1(loc1), loc2(loc2),data(data) {}
00347 
00348     std::string name;
00349     game_events::entity_location loc1;
00350     game_events::entity_location loc2;
00351     config data;
00352 };
00353 
00354 std::deque<queued_event> events_queue;
00355 
00356 class event_handler
00357 {
00358 public:
00359     event_handler(const vconfig& cfg) :
00360         names_(utils::split(cfg["name"])),
00361         first_time_only_(utils::string_bool(cfg["first_time_only"],true)),
00362         disabled_(false),rebuild_screen_(false),
00363         cfg_(cfg)
00364     {}
00365 
00366     void write(config& cfg) const
00367     {
00368         if(disabled_)
00369             return;
00370 
00371         cfg = cfg_.get_config();
00372     }
00373 
00374     const std::vector< std::string >& names() const { return names_; }
00375 
00376     bool first_time_only() const { return first_time_only_; }
00377 
00378     void disable() { disabled_ = true; }
00379     bool disabled() const { return disabled_; }
00380 
00381     bool is_menu_item() const {
00382         assert(state_of_game != NULL);
00383         std::map<std::string, wml_menu_item *>::iterator itor = state_of_game->wml_menu_items.begin();
00384         while(itor != state_of_game->wml_menu_items.end()) {
00385             if(&cfg_.get_config() == &itor->second->command) {
00386                 return true;
00387             }
00388             ++itor;
00389         }
00390         return false;
00391     }
00392 
00393     const vconfig::child_list first_arg_filters()
00394     {
00395         return cfg_.get_children("filter");
00396     }
00397     const vconfig::child_list first_special_filters()
00398     {
00399         vconfig::child_list kids;
00400         kids = cfg_.get_children("filter_attack");
00401         // FIXME OBSOLETE: remove in 1.5.3
00402         if (!kids.empty())
00403             return kids;
00404         else
00405             return cfg_.get_children("special_filter");
00406 
00407     }
00408 
00409     const vconfig::child_list second_arg_filters()
00410     {
00411         return cfg_.get_children("filter_second");
00412     }
00413     const vconfig::child_list second_special_filters()
00414     {
00415         vconfig::child_list kids;
00416         kids = cfg_.get_children("filter_second_attack");
00417         // FIXME OBSOLETE: remove in 1.5.3
00418         if (!kids.empty())
00419             return kids;
00420         else
00421             return cfg_.get_children("special_filter_second");
00422     }
00423 
00424     bool handle_event(const queued_event& event_info,
00425             const vconfig cfg = vconfig());
00426 
00427     bool& rebuild_screen() {return rebuild_screen_;}
00428 
00429 private:
00430     void handle_event_command(const queued_event& event_info, const std::string& cmd, const vconfig cfg, bool& mutated, bool& skip_messages);
00431 
00432     std::vector< std::string > names_;
00433     bool first_time_only_;
00434     bool disabled_;
00435     bool rebuild_screen_;
00436     vconfig cfg_;
00437 };
00438 
00439 } // end anonymous namespace (3)
00440 
00441 static gamemap::location cfg_to_loc(const vconfig& cfg,int defaultx = 0, int defaulty = 0)
00442 {
00443     int x = lexical_cast_default(cfg["x"], defaultx) - 1;
00444     int y = lexical_cast_default(cfg["y"], defaulty) - 1;
00445 
00446     return gamemap::location(x, y);
00447 }
00448 
00449 static std::vector<gamemap::location> multiple_locs(const vconfig cfg)
00450 {
00451     return parse_location_range(cfg["x"],cfg["y"]);
00452 }
00453 
00454 namespace {
00455 
00456 std::multimap<std::string,event_handler> events_map;
00457 
00458 //! Handles all the different types of actions that can be triggered by an event.
00459 void event_handler::handle_event_command(const queued_event& event_info,
00460         const std::string& cmd, const vconfig cfg, bool& mutated, bool& skip_messages)
00461 {
00462     log_scope2(engine, "handle_event_command");
00463     LOG_NG << "handling command: '" << cmd << "'\n";
00464 
00465     // Sub commands that need to be handled in a guaranteed ordering
00466     if(cmd == "command") {
00467         if(!handle_event(event_info, cfg)) {
00468             mutated = false;
00469         }
00470     }
00471 
00472     // Allow undo sets the flag saying whether the event has mutated the game to false
00473     else if(cmd == "allow_undo") {
00474         mutated = false;
00475     }
00476     // Change shroud settings for portions of the map
00477     else if(cmd == "remove_shroud" || cmd == "place_shroud") {
00478         const bool remove = cmd == "remove_shroud";
00479 
00480         std::string side = cfg["side"];
00481         assert(state_of_game != NULL);
00482         const int side_num = lexical_cast_default<int>(side,1);
00483         const size_t index = side_num-1;
00484 
00485         if(index < teams->size()) {
00486             const std::vector<gamemap::location>& locs = multiple_locs(cfg);
00487             for(std::vector<gamemap::location>::const_iterator j = locs.begin(); j != locs.end(); ++j) {
00488                 if(remove) {
00489                     (*teams)[index].clear_shroud(*j);
00490                 } else {
00491                     (*teams)[index].place_shroud(*j);
00492                 }
00493             }
00494         }
00495 
00496         screen->labels().recalculate_shroud();
00497         screen->invalidate_all();
00498     }
00499 
00500 
00501     // Teleport a unit from one location to another
00502     else if(cmd == "teleport") {
00503 
00504         unit_map::iterator u = units->find(event_info.loc1);
00505 
00506         // Search for a valid unit filter, and if we have one, look for the matching unit
00507         const vconfig filter = cfg.child("filter");
00508         if(!filter.null()) {
00509             for(u = units->begin(); u != units->end(); ++u){
00510                 if(game_events::unit_matches_filter(u, filter))
00511                     break;
00512             }
00513         }
00514 
00515         // We have found a unit that matches the filter
00516         if(u != units->end()) {
00517             const gamemap::location dst = cfg_to_loc(cfg);
00518             if(dst != u->first && game_map->on_board(dst)) {
00519                 const gamemap::location vacant_dst = find_vacant_tile(*game_map,*units,dst);
00520                 if(game_map->on_board(vacant_dst)) {
00521                     const int side = u->second.side();
00522 
00523                     screen->invalidate(u->first);
00524                     std::pair<gamemap::location,unit> *up = units->extract(u->first);
00525                     up->first = vacant_dst;
00526                     units->add(up);
00527                     unit_mutations++;
00528                     if(game_map->is_village(vacant_dst)) {
00529                         get_village(vacant_dst, *screen,*teams,side-1,*units);
00530                     }
00531 
00532                     if(utils::string_bool(cfg["clear_shroud"],true)) {
00533                         clear_shroud(*screen,*game_map,*units,*teams,side-1);
00534                     }
00535 
00536                     screen->invalidate(dst);
00537                     screen->draw();
00538                 }
00539             }
00540         }
00541     }
00542 
00543     // Remove units from being turned to stone
00544     else if(cmd == "unstone") {
00545         const vconfig filter = cfg.child("filter");
00546         // Store which side will need a shroud/fog update
00547         std::vector<bool> clear_fog_side(teams->size(),false);
00548 
00549         for(unit_map::iterator i = units->begin(); i != units->end(); ++i) {
00550             if(utils::string_bool(i->second.get_state("stoned"))) {
00551                 if(filter.null() || game_events::unit_matches_filter(i, filter)) {
00552                     i->second.set_state("stoned","");
00553                     clear_fog_side[i->second.side()-1] = true;
00554                 }
00555             }
00556         }
00557 
00558         for (size_t side = 0; side != teams->size(); side++) {
00559             if (clear_fog_side[side] && (*teams)[side].auto_shroud_updates()) {
00560                 clear_shroud(*screen,*game_map,*units,*teams,side);
00561             }
00562         }
00563     }
00564 
00565     // Allow a side to recruit a new type of unit
00566     else if(cmd == "allow_recruit") {
00567         std::string side = cfg["side"];
00568         assert(state_of_game != NULL);
00569         const int side_num = lexical_cast_default<int>(side,1);
00570         const size_t index = side_num-1;
00571 
00572         if(index >= teams->size())
00573             return;
00574 
00575         const std::string type = cfg["type"];
00576 
00577         const std::vector<std::string>& types = utils::split(type);
00578         for(std::vector<std::string>::const_iterator i = types.begin(); i != types.end(); ++i) {
00579             (*teams)[index].recruits().insert(*i);
00580             preferences::encountered_units().insert(*i);
00581 
00582             player_info *player=state_of_game->get_player((*teams)[index].save_id());
00583             if(player) {
00584                 player->can_recruit.insert(*i);
00585             }
00586         }
00587     }
00588 
00589     // Remove the ability to recruit a unit from a certain side
00590     else if(cmd == "disallow_recruit") {
00591         std::string side = cfg["side"];
00592         assert(state_of_game != NULL);
00593         const int side_num = lexical_cast_default<int>(side,1);
00594         const size_t index = side_num-1;
00595 
00596         if(index >= teams->size())
00597             return;
00598 
00599         const std::string type = cfg["type"];
00600         const std::vector<std::string>& types = utils::split(type);
00601         for(std::vector<std::string>::const_iterator i = types.begin(); i != types.end(); ++i) {
00602             (*teams)[index].recruits().erase(*i);
00603 
00604             player_info *player=state_of_game->get_player((*teams)[index].save_id());
00605             if(player) {
00606                 player->can_recruit.erase(*i);
00607             }
00608         }
00609     }
00610 
00611     else if(cmd == "set_recruit") {
00612         std::string side = cfg["side"];
00613         assert(state_of_game != NULL);
00614         const int side_num = lexical_cast_default<int>(side,1);
00615         const size_t index = side_num-1;
00616 
00617         if(index >= teams->size())
00618             return;
00619 
00620         std::vector<std::string> recruit = utils::split(cfg["recruit"]);
00621         if(recruit.size() == 1 && recruit.back() == "")
00622             recruit.clear();
00623 
00624         std::set<std::string>& can_recruit = (*teams)[index].recruits();
00625         can_recruit.clear();
00626         std::copy(recruit.begin(),recruit.end(),std::inserter(can_recruit,can_recruit.end()));
00627 
00628         player_info *player=state_of_game->get_player((*teams)[index].save_id());
00629         if(player) {
00630             player->can_recruit = can_recruit;
00631         }
00632     }
00633 
00634     else if(cmd == "music") {
00635         sound::play_music_config(cfg.get_parsed_config());
00636     }
00637 
00638     else if(cmd == "sound") {
00639         std::string sound = cfg["name"];
00640         const int repeats = lexical_cast_default<int>(cfg["repeat"], 0);
00641         assert(state_of_game != NULL);
00642         sound::play_sound(sound, sound::SOUND_FX, repeats);
00643     }
00644 
00645     else if(cmd == "colour_adjust") {
00646         std::string red = cfg["red"];
00647         std::string green = cfg["green"];
00648         std::string blue = cfg["blue"];
00649         assert(state_of_game != NULL);
00650         const int r = atoi(red.c_str());
00651         const int g = atoi(green.c_str());
00652         const int b = atoi(blue.c_str());
00653         screen->adjust_colours(r,g,b);
00654         screen->invalidate_all();
00655         screen->draw(true,true);
00656     }
00657 
00658     else if(cmd == "delay") {
00659         std::string delay_string = cfg["time"];
00660         assert(state_of_game != NULL);
00661         const int delay_time = atoi(delay_string.c_str());
00662         screen->delay(delay_time);
00663     }
00664 
00665     else if(cmd == "scroll") {
00666         std::string x = cfg["x"];
00667         std::string y = cfg["y"];
00668         assert(state_of_game != NULL);
00669         const int xoff = atoi(x.c_str());
00670         const int yoff = atoi(y.c_str());
00671         screen->scroll(xoff,yoff);
00672         screen->draw(true,true);
00673     }
00674 
00675     else if(cmd == "scroll_to") {
00676         assert(state_of_game != NULL);
00677         const gamemap::location loc = cfg_to_loc(cfg);
00678         std::string check_fogged = cfg["check_fogged"];
00679         screen->scroll_to_tile(loc,game_display::SCROLL,utils::string_bool(check_fogged,false));
00680     }
00681 
00682     else if(cmd == "scroll_to_unit") {
00683         unit_map::const_iterator u;
00684         for(u = units->begin(); u != units->end(); ++u){
00685             if(game_events::unit_matches_filter(u,cfg))
00686                 break;
00687         }
00688         std::string check_fogged = cfg["check_fogged"];
00689         if(u != units->end()) {
00690             screen->scroll_to_tile(u->first,game_display::SCROLL,utils::string_bool(check_fogged,false));
00691         }
00692     }
00693 
00694     // An award of gold to a particular side
00695     else if(cmd == "gold") {
00696         std::string side = cfg["side"];
00697         std::string amount = cfg["amount"];
00698         assert(state_of_game != NULL);
00699         const int side_num = lexical_cast_default<int>(side,1);
00700         const int amount_num = atoi(amount.c_str());
00701         const size_t team_index = side_num-1;
00702         if(team_index < teams->size()) {
00703             (*teams)[team_index].spend_gold(-amount_num);
00704         }
00705     }
00706 
00707     // Modifications of some attributes of a side: gold, income, team name
00708     else if(cmd == "modify_side") {
00709         std::string side = cfg["side"];
00710         std::string income = cfg["income"];
00711         std::string name = cfg["name"];
00712         std::string team_name = cfg["team_name"];
00713         std::string user_team_name = cfg["user_team_name"];
00714         std::string gold = cfg["gold"];
00715         std::string controller = cfg["controller"];
00716         std::string recruit_str = cfg["recruit"];
00717         std::string fog = cfg["fog"];
00718         std::string shroud = cfg["shroud"];
00719         std::string shroud_data = cfg["shroud_data"];
00720         std::string village_gold = cfg["village_gold"];
00721         const config::child_list& ai = cfg.get_parsed_config().get_children("ai");
00722         //!@todo also allow client to modify a side's colour if it
00723         //!is possible to change it on the fly without causing visual glitches
00724 
00725         assert(state_of_game != NULL);
00726         const int side_num = lexical_cast_default<int>(side,1);
00727         const size_t team_index = side_num-1;
00728 
00729         if(team_index < teams->size()) {
00730             LOG_NG << "modifying side: " << side_num << "\n";
00731             if(!team_name.empty()) {
00732                 LOG_NG << "change side's team to team_name '" << team_name << "'\n";
00733                 (*teams)[team_index].change_team(team_name,
00734                                                  user_team_name);
00735             }
00736             // Modify recruit list (override)
00737             if (!recruit_str.empty()) {
00738                 std::vector<std::string> recruit = utils::split(recruit_str);
00739                 if (recruit.size() == 1 && recruit.back() == "")
00740                     recruit.clear();
00741 
00742                 std::set<std::string>& rlist_set = (*teams)[team_index].recruits();
00743                 rlist_set.clear();
00744 
00745                 std::copy( recruit.begin(), recruit.end(), std::inserter(rlist_set,rlist_set.end()) );
00746                 player_info *player = state_of_game->get_player((*teams)[team_index].save_id());
00747 
00748                 if (player) player->can_recruit = rlist_set;
00749             }
00750             // Modify income
00751             if(!income.empty()) {
00752                 (*teams)[team_index].set_income(lexical_cast_default<int>(income));
00753             }
00754             // Modify total gold
00755             if(!gold.empty()) {
00756                 (*teams)[team_index].spend_gold((*teams)[team_index].gold()-lexical_cast_default<int>(gold));
00757             }
00758             // Set controller
00759             if(!controller.empty()) {
00760                 (*teams)[team_index].change_controller(controller);
00761             }
00762             // Set shroud
00763             if (!shroud.empty()) {
00764                 (*teams)[team_index].set_shroud( utils::string_bool(shroud, true) );
00765             }
00766             // Merge shroud data
00767             if (!shroud_data.empty()) {
00768                 (*teams)[team_index].merge_shroud_map_data(shroud_data);
00769             }
00770             // Set fog
00771             if (!fog.empty()) {
00772                 (*teams)[team_index].set_fog( utils::string_bool(fog, true) );
00773             }
00774             // Set income per village
00775             if (!village_gold.empty()) {
00776                 (*teams)[team_index].set_village_gold(lexical_cast_default<int>(village_gold));
00777             }
00778             // Override AI parameters
00779             if (!ai.empty()) {
00780                 (*teams)[team_index].set_ai_parameters(ai);
00781             }
00782         }
00783     }
00784     // Stores of some attributes of a side: gold, income, team name
00785     else if(cmd == "store_side" || cmd == "store_gold") {
00786         t_string *gold_store;
00787         std::string side = cfg["side"];
00788         std::string var_name = cfg["variable"];
00789         if(var_name.empty()) {
00790             var_name = cmd.substr(cmd.find_first_of('_') + 1);
00791         }
00792         assert(state_of_game != NULL);
00793         const int side_num = lexical_cast_default<int>(side,1);
00794         const size_t team_index = side_num-1;
00795         if(team_index < teams->size()) {
00796             if(cmd == "store_side") {
00797                 config side_data;
00798                 (*teams)[team_index].write(side_data);
00799                 state_of_game->get_variable(var_name+".controller") = side_data["controller"];
00800                 state_of_game->get_variable(var_name+".recruit") = side_data["recruit"];
00801                 state_of_game->get_variable(var_name+".fog") = side_data["fog"];
00802                 state_of_game->get_variable(var_name+".shroud") = side_data["shroud"];
00803 
00804                 state_of_game->get_variable(var_name+".income") = lexical_cast_default<std::string>((*teams)[team_index].income(),"");
00805                 state_of_game->get_variable(var_name+".village_gold") = lexical_cast_default<std::string>((*teams)[team_index].village_gold(),"");
00806                 state_of_game->get_variable(var_name+".name") = (*teams)[team_index].name();
00807                 state_of_game->get_variable(var_name+".team_name") = (*teams)[team_index].team_name();
00808                 state_of_game->get_variable(var_name+".user_team_name") = (*teams)[team_index].user_team_name();
00809                 state_of_game->get_variable(var_name+".colour") = (*teams)[team_index].map_colour_to();
00810 
00811                 gold_store = &state_of_game->get_variable(var_name+".gold");
00812             } else {
00813                 gold_store = &state_of_game->get_variable(var_name);
00814             }
00815             *gold_store = lexical_cast_default<std::string>((*teams)[team_index].gold(),"");
00816         }
00817     }
00818     else if(cmd == "modify_turns") {
00819         std::string value = cfg["value"];
00820         std::string add = cfg["add"];
00821         assert(state_of_game != NULL);
00822         assert(status_ptr != NULL);
00823         if(add != "") {
00824             status_ptr->modify_turns(add);
00825         } else {
00826             status_ptr->add_turns(-status_ptr->number_of_turns());
00827             status_ptr->add_turns(lexical_cast_default<int>(value,50));
00828         }
00829     }
00830     else if(cmd == "store_turns") {
00831         std::string var_name = cfg["variable"];
00832         if(var_name.empty()) {
00833             var_name = cmd.substr(cmd.find_first_of('_') + 1);
00834         }
00835         assert(state_of_game != NULL);
00836         assert(status_ptr != NULL);
00837         int turns = status_ptr->number_of_turns();
00838         state_of_game->get_variable(var_name) = lexical_cast_default<std::string>(turns,"");
00839     }
00840 
00841     // Moving a 'unit' - i.e. a dummy unit
00842     // that is just moving for the visual effect
00843     else if(cmd == "move_unit_fake") {
00844         std::string type = cfg["type"];
00845         std::string side = cfg["side"];
00846         std::string x = cfg["x"];
00847         std::string y = cfg["y"];
00848         std::string variation = cfg["variation"];
00849         assert(state_of_game != NULL);
00850 
00851         size_t side_num = lexical_cast_default<int>(side,1)-1;
00852         if (side_num >= teams->size()) side_num = 0;
00853 
00854         const unit_race::GENDER gender = string_gender(cfg["gender"]);
00855         const unit_type_data::unit_type_map::const_iterator itor = unit_type_data::types().find(type);
00856         if(itor != unit_type_data::types().end()) {
00857             assert(units != NULL);
00858             assert(game_map != NULL);
00859             assert(status_ptr != NULL);
00860             unit dummy_unit(units,game_map,status_ptr,teams,&itor->second,side_num+1,false,true,gender,variation);
00861             const std::vector<std::string> xvals = utils::split(x);
00862             const std::vector<std::string> yvals = utils::split(y);
00863             std::vector<gamemap::location> path;
00864             gamemap::location src;
00865             gamemap::location dst;
00866             for(size_t i = 0; i != minimum(xvals.size(),yvals.size()); ++i) {
00867                 if(i==0){
00868                     src.x = atoi(xvals[i].c_str())-1;
00869                     src.y = atoi(yvals[i].c_str())-1;
00870                     if (!game_map->on_board(src)) {
00871                         ERR_CF << "invalid move_unit_fake source: " << src << '\n';
00872                         break;
00873                     }
00874                     continue;
00875                 }
00876                 shortest_path_calculator calc(dummy_unit,
00877                         (*teams)[side_num],
00878                         *units,
00879                         *teams,
00880                         *game_map);
00881 
00882                 dst.x = atoi(xvals[i].c_str())-1;
00883                 dst.y = atoi(yvals[i].c_str())-1;
00884                 if (!game_map->on_board(dst)) {
00885                     ERR_CF << "invalid move_unit_fake destination: " << dst << '\n';
00886                     break;
00887                 }
00888 
00889                 paths::route route = a_star_search(src, dst, 10000, &calc,
00890                                                    game_map->w(), game_map->h());
00891 
00892                 if (route.steps.size() == 0) {
00893                     WRN_NG << "Could not find move_unit_fake route from " << src << " to " << dst << ": ignoring complexities\n";
00894                     emergency_path_calculator calc(dummy_unit, *game_map);
00895 
00896                     route = a_star_search(src, dst, 10000, &calc,
00897                                           game_map->w(), game_map->h());
00898                     assert(route.steps.size() > 0);
00899                 }
00900                 unit_display::move_unit(route.steps, dummy_unit, *teams);
00901 
00902                 src = dst;
00903             }
00904         }
00905     }
00906 
00907     // Provide a means of specifying win/loss conditions:
00908     // [event]
00909     // name=prestart
00910     // [objectives]
00911     //   side=1
00912     //   summary="Escape the forest alive"
00913     //   victory_string="Victory:"
00914     //   defeat_string="Defeat:"
00915     //   [objective]
00916     //     condition=win
00917     //     description="Defeat all enemies"
00918     //   [/objective]
00919     //   [objective]
00920     //     description="Death of Konrad"
00921     //     condition=lose
00922     //   [/objective]
00923     // [/objectives]
00924     // [/event]
00925     //instead of the current (but still supported):
00926     // objectives= _ "
00927     // Victory:
00928     // @Move Konrad to the signpost in the north-west
00929     // Defeat:
00930     // #Death of Konrad
00931     // #Death of Delfador
00932     // #Turns run out"
00933     //
00934     // If side is set to 0, the new objectives are added to each player.
00935     //
00936     // The new objectives will be automatically displayed,
00937     // but only to the player whose objectives did change,
00938     // and only when it's this player's turn.
00939     else if(cmd == "objectives") {
00940         const std::string win_str = "@";
00941         const std::string lose_str = "#";
00942 
00943         assert(state_of_game != NULL);
00944         const t_string summary = cfg["summary"];
00945         const t_string note = cfg["note"];
00946         std::string side = cfg["side"];
00947         bool silent = utils::string_bool(cfg["silent"]);
00948         const size_t side_num = lexical_cast_default<size_t>(side,0);
00949 
00950         if(side_num != 0 && (side_num - 1) >= teams->size()) {
00951             ERR_NG << "Invalid side: " << cfg["side"] << " in objectives event\n";
00952             return;
00953         }
00954 
00955         t_string win_string = cfg["victory_string"];
00956         if(win_string.empty())
00957             win_string = t_string(N_("Victory:"), "wesnoth");
00958         t_string lose_string = cfg["defeat_string"];
00959         if(lose_string.empty())
00960             lose_string = t_string(N_("Defeat:"), "wesnoth");
00961 
00962         t_string win_objectives;
00963         t_string lose_objectives;
00964 
00965         const vconfig::child_list objectives = cfg.get_children("objective");
00966         for(vconfig::child_list::const_iterator obj_it = objectives.begin();
00967                 obj_it != objectives.end(); ++obj_it) {
00968 
00969             t_string description = (*obj_it)["description"];
00970             std::string condition = (*obj_it)["condition"];
00971             LOG_NG << condition << " objective: " << description << "\n";
00972             if(condition == "win") {
00973                 win_objectives += "\n";
00974                 win_objectives += win_str;
00975                 win_objectives += description;
00976             } else if(condition == "lose") {
00977                 lose_objectives += "\n";
00978                 lose_objectives += lose_str;
00979                 lose_objectives += description;
00980             } else {
00981                 ERR_NG << "unknown condition '" << condition << "', ignoring\n";
00982             }
00983         }
00984 
00985         t_string objs;
00986         if(!summary.empty())
00987             objs += "*" + summary + "\n";
00988         if(!win_objectives.empty()) {
00989             objs += "*" + win_string + "\n";
00990             objs += win_objectives + "\n";
00991         }
00992         if(!lose_objectives.empty()) {
00993             objs += "*" + lose_string + "\n";
00994             objs += lose_objectives + "\n";
00995         }
00996         if(!note.empty())
00997             objs += note + "\n";
00998 
00999         if(side_num == 0) {
01000             for(std::vector<team>::iterator itor = teams->begin();
01001                     itor != teams->end(); ++itor) {
01002 
01003                 itor->set_objectives(objs, silent);
01004             }
01005         } else {
01006             (*teams)[side_num - 1].set_objectives(objs, silent);
01007         }
01008     }
01009 
01010 
01011     // Setting a variable
01012     else if(cmd == "set_variable") {
01013         assert(state_of_game != NULL);
01014 
01015         const std::string name = cfg["name"];
01016         t_string& var = state_of_game->get_variable(name);
01017 
01018         const t_string& literal = cfg.get_attribute("literal"); // no $var substitution
01019         if(literal.empty() == false) {
01020             var = literal;
01021         }
01022 
01023         const t_string value = cfg["value"];
01024         if(value.empty() == false) {
01025             var = value;
01026         }
01027 
01028         const t_string format = cfg["format"];  // Deprecated, use value
01029         if(format.empty() == false) {
01030             var = format;
01031         }
01032 
01033         const std::string to_variable = cfg["to_variable"];
01034         if(to_variable.empty() == false) {
01035             var = state_of_game->get_variable(to_variable);
01036         }
01037 
01038         const std::string add = cfg["add"];
01039         if(add.empty() == false) {
01040             int value = int(atof(var.c_str()));
01041             value += atoi(add.c_str());
01042             char buf[50];
01043             snprintf(buf,sizeof(buf),"%d",value);
01044             var = buf;
01045         }
01046 
01047         const std::string multiply = cfg["multiply"];
01048         if(multiply.empty() == false) {
01049             int value = int(atof(var.c_str()));
01050             value = int(double(value) * atof(multiply.c_str()));
01051             char buf[50];
01052             snprintf(buf,sizeof(buf),"%d",value);
01053             var = buf;
01054         }
01055 
01056         const std::string divide = cfg["divide"];
01057         if(divide.empty() == false) {
01058             int value = int(atof(var.c_str()));
01059             double divider = atof(divide.c_str());
01060             if (divider == 0) {
01061                 ERR_NG << "division by zero on variable " << name << "\n";
01062                 return;
01063             } else {
01064                 value = int(double(value) / divider);
01065                 char buf[50];
01066                 snprintf(buf,sizeof(buf),"%d",value);
01067                 var = buf;
01068             }
01069         }
01070 
01071         const std::string modulo = cfg["modulo"];
01072         if(modulo.empty() == false) {
01073             int value = atoi(var.c_str());
01074             int divider = atoi(modulo.c_str());
01075             if (divider == 0) {
01076                 ERR_NG << "division by zero on variable " << name << "\n";
01077                 return;
01078             } else {
01079                 value %= divider;
01080                 var = str_cast(value);
01081             }
01082         }
01083 
01084         const t_string string_length_target = cfg["string_length"];
01085         if(string_length_target.empty() == false) {
01086             const int value = string_length_target.str().length();
01087             var = str_cast(value);
01088         }
01089 
01090         // Note: maybe we add more options later, eg. strftime formatting.
01091         // For now make the stamp mandatory.
01092         const std::string time = cfg["time"];
01093         if(time == "stamp") {
01094             char buf[50];
01095             snprintf(buf,sizeof(buf),"%d",SDL_GetTicks());
01096             var = buf;
01097         }
01098 
01099         // Random generation works as follows:
01100         // random=[comma delimited list]
01101         // Each element in the list will be considered a separate choice,
01102         // unless it contains "..". In this case, it must be a numerical
01103         // range (i.e. -1..-10, 0..100, -10..10, etc).
01104         const std::string random = cfg["random"];
01105         if(random.empty() == false) {
01106             // random is deprecated but will be available in the 1.4 branch
01107             // so enable the message after forking
01108             //! @todo Enable after branching and once rand works fully in MP
01109             //! including synchronizing.
01110             //lg::wml_error << "Usage of 'random' is deprecated use 'rand' instead, "
01111             //  "support will be removed in 1.5.2.\n";
01112             std::string random_value;
01113             // If we're not replaying, create a random number
01114             if(get_replay_source().at_end()) {
01115                 std::string word;
01116                 std::vector<std::string> words;
01117                 std::vector<std::pair<long,long> > ranges;
01118                 int num_choices = 0;
01119                 std::string::size_type pos = 0, pos2 = std::string::npos;
01120                 std::stringstream ss(std::stringstream::in|std::stringstream::out);
01121                 while (pos2 != random.length()) {
01122                     pos = pos2+1;
01123                     pos2 = random.find(",", pos);
01124 
01125                     if (pos2 == std::string::npos)
01126                         pos2 = random.length();
01127 
01128                     word = random.substr(pos, pos2-pos);
01129                     words.push_back(word);
01130                     std::string::size_type tmp = word.find("..");
01131 
01132 
01133                     if (tmp == std::string::npos) {
01134                         // Treat this element as a string
01135                         ranges.push_back(std::pair<int, int>(0,0));
01136                         num_choices += 1;
01137                     }
01138                     else {
01139                         // Treat as a numerical range
01140                         const std::string first = word.substr(0, tmp);
01141                         const std::string second = word.substr(tmp+2,
01142                                 random.length());
01143 
01144                         long low, high;
01145                         ss << first + " " + second;
01146                         ss >> low;
01147                         ss >> high;
01148                         ss.clear();
01149 
01150                         if (low > high) {
01151                             tmp = low;
01152                             low = high;
01153                             high = tmp;
01154                         }
01155                         ranges.push_back(std::pair<long, long>(low,high));
01156                         num_choices += (high - low) + 1;
01157                     }
01158                 }
01159 
01160                 int choice = get_random() % num_choices;
01161                 int tmp = 0;
01162                 for(size_t i = 0; i < ranges.size(); i++) {
01163                     tmp += (ranges[i].second - ranges[i].first) + 1;
01164                     if (tmp > choice) {
01165                         if (ranges[i].first == 0 && ranges[i].second == 0) {
01166                             random_value = words[i];
01167                         }
01168                         else {
01169                             tmp = (ranges[i].second - (tmp - choice)) + 1;
01170                             ss << tmp;
01171                             ss >> random_value;
01172                         }
01173                         break;
01174                     }
01175                 }
01176                 recorder.set_random_value(random_value.c_str());
01177             }
01178 
01179             // Otherwise get the random value from the replay data
01180             else {
01181                 const config* const action = get_replay_source().get_next_action();
01182                 if(action == NULL || action->get_children("random_number").empty()) {
01183                     replay::throw_error("random_number expected but none found\n");
01184                 }
01185 
01186                 const std::string& val = (*(action->get_children("random_number").front()))["value"];
01187                 random_value = val;
01188             }
01189             var = random_value;
01190         }
01191 
01192         // The new random generator, the logic is a copy paste of the old random.
01193         const std::string rand = cfg["rand"];
01194         if(rand.empty() == false) {
01195             assert(state_of_game);
01196 
01197             std::string random_value;
01198 
01199             std::string word;
01200             std::vector<std::string> words;
01201             std::vector<std::pair<long,long> > ranges;
01202             int num_choices = 0;
01203             std::string::size_type pos = 0, pos2 = std::string::npos;
01204             std::stringstream ss(std::stringstream::in|std::stringstream::out);
01205             while (pos2 != rand.length()) {
01206                 pos = pos2+1;
01207                 pos2 = rand.find(",", pos);
01208 
01209                 if (pos2 == std::string::npos)
01210                     pos2 = rand.length();
01211 
01212                 word = rand.substr(pos, pos2-pos);
01213                 words.push_back(word);
01214                 std::string::size_type tmp = word.find("..");
01215 
01216 
01217                 if (tmp == std::string::npos) {
01218                     // Treat this element as a string
01219                     ranges.push_back(std::pair<int, int>(0,0));
01220                     num_choices += 1;
01221                 }
01222                 else {
01223                     // Treat as a numerical range
01224                     const std::string first = word.substr(0, tmp);
01225                     const std::string second = word.substr(tmp+2,
01226                             rand.length());
01227 
01228                     long low, high;
01229                     ss << first + " " + second;
01230                     ss >> low;
01231                     ss >> high;
01232                     ss.clear();
01233 
01234                     if (low > high) {
01235                         tmp = low;
01236                         low = high;
01237                         high = tmp;
01238                     }
01239                     ranges.push_back(std::pair<long, long>(low,high));
01240                     num_choices += (high - low) + 1;
01241                 }
01242             }
01243 
01244             int choice = state_of_game->rng().get_random() % num_choices;
01245             int tmp = 0;
01246             for(size_t i = 0; i < ranges.size(); i++) {
01247                 tmp += (ranges[i].second - ranges[i].first) + 1;
01248                 if (tmp > choice) {
01249                     if (ranges[i].first == 0 && ranges[i].second == 0) {
01250                         random_value = words[i];
01251                     }
01252                     else {
01253                         tmp = (ranges[i].second - (tmp - choice)) + 1;
01254                         ss << tmp;
01255                         ss >> random_value;
01256                     }
01257                     break;
01258                 }
01259             }
01260 
01261             var = random_value;
01262         }
01263 
01264 
01265         const vconfig::child_list join_elements = cfg.get_children("join");
01266         if(!join_elements.empty())
01267         {
01268             const vconfig join_element=join_elements.front();
01269 
01270             std::string array_name=join_element["variable"];
01271             std::string separator=join_element["separator"];
01272             std::string key_name=join_element["key"];
01273 
01274             if(key_name.empty())
01275             {
01276                 key_name="value";
01277             }
01278 
01279             bool remove_empty=utils::string_bool(join_element["remove_empty"]);
01280 
01281             variable_info::array_range array=state_of_game->get_variable_cfgs(array_name);
01282 
01283             std::string joined_string;
01284             std::string current_string;
01285 
01286             for(std::vector<config*>::iterator i=array.first; i!=array.second; ++i)
01287             {
01288                 current_string=(**i)[key_name];
01289                 if(remove_empty && current_string.empty())
01290                 {
01291                     continue;
01292                 }
01293 
01294                 joined_string+=current_string;
01295                 if(i+1!=array.second)
01296                 {
01297                     joined_string+=separator;
01298                 }
01299             }
01300 
01301             var=joined_string;
01302         }
01303 
01304     }
01305 
01306     else if(cmd == "set_variables")
01307     {
01308         assert(state_of_game != NULL);
01309 
01310         const std::string name = cfg["name"];
01311 
01312         std::string mode = cfg["mode"]; //should be one of replace, extend, merge
01313         if(mode!="append"&&mode!="merge")
01314         {
01315             mode="replace";
01316         }
01317 
01318         const t_string to_variable = cfg["to_variable"];
01319         const vconfig::child_list values = cfg.get_children("value");
01320         const vconfig::child_list literals = cfg.get_children("literal");
01321         const vconfig::child_list split_elements = cfg.get_children("split");
01322 
01323         std::vector<config> data;
01324 
01325         if(!to_variable.empty())
01326         {
01327             variable_info::array_range range = state_of_game->get_variable_cfgs(to_variable);
01328             for( ; range.first != range.second; ++range.first)
01329             {
01330                 data.push_back(**range.first);
01331             }
01332         } else if(!values.empty()) {
01333             for(vconfig::child_list::const_iterator i=values.begin(); i!=values.end(); ++i)
01334             {
01335                 data.push_back((*i).get_parsed_config());
01336             }
01337         } else if(!literals.empty()) {
01338             for(vconfig::child_list::const_iterator i=literals.begin(); i!=literals.end(); ++i)
01339             {
01340                 data.push_back(i->get_config());
01341             }
01342         } else if(!split_elements.empty()) {
01343             const vconfig split_element=split_elements.front();
01344 
01345             std::string split_string=split_element["list"];
01346             std::string separator_string=split_element["separator"];
01347             std::string key_name=split_element["key"];
01348             if(key_name.empty())
01349             {
01350                 key_name="value";
01351             }
01352 
01353             bool remove_empty=utils::string_bool(split_element["remove_empty"]);
01354 
01355             char* separator = separator_string.empty() ? NULL : &separator_string[0];
01356 
01357             std::vector<std::string> split_vector;
01358 
01359             //if no separator is specified, explode the string
01360             if(separator == NULL)
01361             {
01362                 for(std::string::iterator i=split_string.begin(); i!=split_string.end(); ++i)
01363                 {
01364                     split_vector.push_back(&*i);
01365                 }
01366             }
01367             else {
01368                 split_vector=utils::split(split_string, *separator, remove_empty ? utils::REMOVE_EMPTY | utils::STRIP_SPACES : utils::STRIP_SPACES);
01369             }
01370 
01371             state_of_game->clear_variable_cfg(name);
01372             for(std::vector<std::string>::iterator i=split_vector.begin(); i!=split_vector.end(); ++i)
01373             {
01374                 config item = config();
01375                 item[key_name]=*i;
01376                 data.push_back(item);
01377             }
01378         }
01379 
01380         if(!data.empty())
01381         {
01382             if(mode == "replace")
01383             {
01384                 state_of_game->clear_variable_cfg(name);
01385             }
01386             if(mode == "merge")
01387             {
01388                 variable_info::array_range target = state_of_game->get_variable_cfgs(name);
01389                 std::vector<config>::iterator i=data.begin();
01390                 config::child_list::iterator j=target.first;
01391                 while(i!=data.end())
01392                 {
01393                     if(j!=target.second)
01394                     {
01395                         (*j)->merge_with(*i);
01396                         ++j;
01397                     } else {
01398                         state_of_game->add_variable_cfg(name, *i);
01399                     }
01400                     ++i;
01401                 }
01402             } else {
01403                 for(std::vector<config>::iterator i=data.begin(); i!=data.end(); ++i)
01404                 {
01405                     state_of_game->add_variable_cfg(name, *i);
01406                 }
01407             }
01408             return;
01409         }
01410     }
01411 
01412     // Conditional statements
01413     else if(cmd == "if" || cmd == "while") {
01414         log_scope(cmd);
01415         const size_t max_iterations = (cmd == "if" ? 1 : game_config::max_loop);
01416         const std::string pass = (cmd == "if" ? "then" : "do");
01417         const std::string fail = (cmd == "if" ? "else" : "");
01418         for(size_t i = 0; i != max_iterations; ++i) {
01419             const std::string type = game_events::conditional_passed(
01420                                           units,cfg) ? pass : fail;
01421 
01422             if(type == "") {
01423                 break;
01424             }
01425 
01426             // If the if statement passed, then execute all 'then' statements,
01427             // otherwise execute 'else' statements
01428             const vconfig::child_list commands = cfg.get_children(type);
01429             for(vconfig::child_list::const_iterator cmd = commands.begin();
01430             cmd != commands.end(); ++cmd) {
01431                 if(!handle_event(event_info, *cmd)) {
01432                     mutated = false;
01433                 }
01434             }
01435         }
01436     }
01437 
01438     else if(cmd == "switch") {
01439         assert(state_of_game != NULL);
01440 
01441         const std::string var_name = cfg["variable"];
01442         const std::string& var = state_of_game->get_variable_const(var_name);
01443 
01444         bool not_found = true;
01445         const vconfig::child_list& cases = cfg.get_children("case");
01446         // execute all cases where the value matches
01447         for(vconfig::child_list::const_iterator c = cases.begin(); c != cases.end(); ++c) {
01448             const std::string value = (*c)["value"];
01449             if (var == value) {
01450                 not_found = false;
01451                 if(!handle_event(event_info, *c)) {
01452                     mutated = false;
01453                 }
01454             }
01455         }
01456         if (not_found) {
01457             // otherwise execute 'else' statements
01458             const vconfig::child_list elses = cfg.get_children("else");
01459             for(vconfig::child_list::const_iterator e = elses.begin(); e != elses.end(); ++e) {
01460                 if(!handle_event(event_info, *e)) {
01461                     mutated = false;
01462                 }
01463             }
01464         }
01465     }
01466 
01467     else if(cmd == "role") {
01468 
01469         // Get a list of the types this unit can be
01470         std::vector<std::string> types = utils::split(cfg["type"]);
01471         if (types.size() == 0) types.push_back("");
01472 
01473         std::vector<std::string> sides = utils::split(cfg["side"]);
01474 
01475         // Iterate over all the types, and for each type,
01476         // try to find a unit that matches
01477         std::vector<std::string>::iterator ti;
01478         for(ti = types.begin(); ti != types.end(); ++ti) {
01479             config item = cfg.get_config();
01480             item["type"] = *ti;
01481             item["role"] = "";
01482             vconfig filter(&item);
01483 
01484             unit_map::iterator itor;
01485             for(itor = units->begin(); itor != units->end(); ++itor) {
01486                 if(game_events::unit_matches_filter(itor, filter)) {
01487                     itor->second.assign_role(cfg["role"]);
01488                     break;
01489                 }
01490             }
01491 
01492             if(itor != units->end())
01493                 break;
01494 
01495             bool found = false;
01496 
01497             if(sides.empty() == false) {
01498                 std::vector<std::string>::const_iterator si;
01499                 for(si = sides.begin(); si != sides.end(); ++si) {
01500                     int side_num = lexical_cast_default<int>(*si,1);
01501                     const std::string player_id = (*teams)[side_num-1].save_id();
01502                     player_info* player=state_of_game->get_player(player_id);
01503 
01504                     if(!player)
01505                         continue;
01506 
01507                     // Iterate over the units, and try to find one that matches
01508                     std::vector<unit>::iterator ui;
01509                     for(ui = player->available_units.begin();
01510                             ui != player->available_units.end(); ++ui) {
01511                         ui->set_game_context(units,game_map,status_ptr,teams);
01512                         scoped_recall_unit auto_store("this_unit", player_id,
01513                             (ui - player->available_units.begin()));
01514                         if(game_events::unit_matches_filter(*ui, filter,gamemap::location())) {
01515                             ui->assign_role(cfg["role"]);
01516                             found=true;
01517                             break;
01518                         }
01519                     }
01520                 }
01521             } else {
01522                 std::map<std::string, player_info>::iterator pi;
01523                 for(pi=state_of_game->players.begin();
01524                         pi!=state_of_game->players.end(); ++pi) {
01525                     std::vector<unit>::iterator ui;
01526                     // Iterate over the units, and try to find one that matches
01527                     for(ui = pi->second.available_units.begin();
01528                             ui != pi->second.available_units.end(); ++ui) {
01529                         ui->set_game_context(units,game_map,status_ptr,teams);
01530                         scoped_recall_unit auto_store("this_unit", pi->first,
01531                             (ui - pi->second.available_units.begin()));
01532                         if(game_events::unit_matches_filter(*ui, filter,gamemap::location())) {
01533                             ui->assign_role(cfg["role"]);
01534                             found=true;
01535                             break;
01536                         }
01537                     }
01538                 }
01539             }
01540 
01541             // Stop searching if we found a unit:
01542             if (found) break;
01543         }
01544     }
01545 
01546     else if(cmd == "removeitem") {
01547         std::string img = cfg["image"];
01548         assert(state_of_game != NULL);
01549         gamemap::location loc = cfg_to_loc(cfg);
01550 
01551         if(!loc.valid()) {
01552                 loc = event_info.loc1;
01553         }
01554 
01555         if(!img.empty()) { //If image key is set remove that one item
01556             screen->remove_single_overlay(loc, img);
01557         }
01558         else { //Else remove the overlay completely
01559             screen->remove_overlay(loc);
01560         }
01561     }
01562 
01563     else if(cmd == "unit_overlay") {
01564         std::string img = cfg["image"];
01565         assert(state_of_game != NULL);
01566         for(unit_map::iterator itor = units->begin(); itor != units->end(); ++itor) {
01567             if(game_events::unit_matches_filter(itor,cfg)) {
01568                 itor->second.add_overlay(img);
01569                 break;
01570             }
01571         }
01572     }
01573 
01574     else if(cmd == "remove_unit_overlay") {
01575         std::string img = cfg["image"];
01576         assert(state_of_game != NULL);
01577         for(unit_map::iterator itor = units->begin(); itor != units->end(); ++itor) {
01578             if(game_events::unit_matches_filter(itor,cfg)) {
01579                 itor->second.remove_overlay(img);
01580                 break;
01581             }
01582         }
01583     }
01584 
01585     // Hiding units
01586     else if(cmd == "hide_unit") {
01587         const gamemap::location loc = cfg_to_loc(cfg);
01588         unit_map::iterator u = units->find(loc);
01589         if(u != units->end()) {
01590             u->second.set_hidden(true);
01591             screen->invalidate(loc);
01592             screen->draw();
01593         }
01594     }
01595 
01596     else if(cmd == "unhide_unit") {
01597         const gamemap::location loc = cfg_to_loc(cfg);
01598         unit_map::iterator u;
01599         // Unhide all for backward compatibility
01600         for(u =  units->begin(); u != units->end() ; u++) {
01601             u->second.set_hidden(false);
01602             screen->invalidate(loc);
01603             screen->draw();
01604         }
01605     }
01606 
01607     // Adding new items
01608     else if(cmd == "item") {
01609         gamemap::location loc = cfg_to_loc(cfg);
01610         std::string img = cfg["image"];
01611         std::string halo = cfg["halo"];
01612         assert(state_of_game != NULL);
01613         if(!img.empty() || !halo.empty()) {
01614             screen->add_overlay(loc,img,halo);
01615             screen->invalidate(loc);
01616             screen->draw();
01617         }
01618     }
01619 
01620     else if(cmd == "sound_source") {
01621         std::string sounds = cfg["sounds"];
01622         std::string id = cfg["id"];
01623         std::string delay = cfg["delay"];
01624         std::string chance = cfg["chance"];
01625         std::string play_fogged = cfg["check_fogged"];
01626         std::string x = cfg["x"];
01627         std::string y = cfg["y"];
01628         std::string loop = cfg["loop"];
01629         std::string full_range = cfg["full_range"];
01630         std::string fade_range = cfg["fade_range"];
01631 
01632         assert(state_of_game != NULL);
01633 
01634         if(!sounds.empty() && !delay.empty() && !chance.empty()) {
01635             const std::vector<std::string>& vx = utils::split(x);
01636             const std::vector<std::string>& vy = utils::split(y);
01637 
01638             if(vx.size() != vy.size()) {
01639                 lg::wml_error << "invalid number of sound source location coordinates";
01640                 return;
01641             }
01642 
01643             soundsource::sourcespec spec(id, sounds, lexical_cast_default<int>(delay, 1000), lexical_cast_default<int>(chance, 100));
01644 
01645             spec.loop(lexical_cast_default<int>(loop, 0));
01646 
01647             if(!full_range.empty()) {
01648                 spec.full_range(lexical_cast<int>(full_range));
01649             }
01650 
01651             if(!fade_range.empty()) {
01652                 spec.fade_range(lexical_cast<int>(fade_range));
01653             }
01654 
01655             if(play_fogged.empty()) {
01656                 spec.check_fog(true);
01657             } else {
01658                 spec.check_fog(utils::string_bool(play_fogged));
01659             }
01660 
01661             for(unsigned int i = 0; i < minimum(vx.size(), vy.size()); ++i) {
01662                 gamemap::location loc(lexical_cast<int>(vx[i]), lexical_cast<int>(vy[i]));
01663                 spec.location(loc);
01664             }
01665 
01666             soundsources->add(spec);
01667         }
01668     }
01669 
01670     else if(cmd == "remove_sound_source") {
01671         soundsources->remove(cfg["id"]);
01672     }
01673 
01674     // Changing the terrain
01675     else if(cmd == "terrain") {
01676         const std::vector<gamemap::location> locs = multiple_locs(cfg);
01677 
01678         std::string terrain_type = cfg["terrain"];
01679         // FIXME: OBSOLETE Remove this in 1.5
01680         if (terrain_type.empty())
01681           terrain_type = cfg["letter"];
01682         assert(state_of_game != NULL);
01683 
01684         t_translation::t_terrain terrain = t_translation::read_terrain_code(terrain_type);
01685 
01686         if(terrain != t_translation::NONE_TERRAIN) {
01687 
01688             gamemap::tmerge_mode mode = gamemap::BOTH;
01689             if (cfg["layer"] == "base") {
01690                 mode = gamemap::BASE;
01691             }
01692             else if (cfg["layer"] == "overlay") {
01693                 mode = gamemap::OVERLAY;
01694             }
01695 
01696             for(std::vector<gamemap::location>::const_iterator loc = locs.begin(); loc != locs.end(); ++loc) {
01697                 const t_translation::t_terrain old_terrain = game_map->get_terrain(*loc);
01698                 const t_translation::t_terrain new_terrain = game_map->merge_terrains(old_terrain, terrain, mode, utils::string_bool(cfg["replace_if_failed"]) );
01699                 if (new_terrain != t_translation::NONE_TERRAIN) {
01700 
01701                     preferences::encountered_terrains().insert(new_terrain);
01702                     const bool old_village = game_map->is_village(*loc);
01703                     const bool new_village = game_map->is_village(new_terrain);
01704                     
01705                     if(old_village && !new_village) {
01706                         int owner = village_owner(*loc, *teams);
01707                         if(owner != -1) {
01708                             (*teams)[owner].lose_village(*loc);
01709                         }
01710                     }
01711 
01712                     game_map->set_terrain(*loc, new_terrain);
01713 
01714                     const t_translation::t_list underlaying_list = game_map->underlying_union_terrain(*loc);
01715                     for (t_translation::t_list::const_iterator ut = underlaying_list.begin(); ut != underlaying_list.end(); ut++) {
01716                         preferences::encountered_terrains().insert(*ut);
01717                     };
01718                 }
01719             }
01720             rebuild_screen_ = true;
01721         }
01722     }
01723 
01724     // Creating a mask of the terrain
01725     else if(cmd == "terrain_mask") {
01726         gamemap::location loc = cfg_to_loc(cfg, 1, 1);
01727 
01728         gamemap mask(*game_map);
01729 
01730         try {
01731             mask.read(cfg["mask"]);
01732         } catch(gamemap::incorrect_format_exception&) {
01733             ERR_NG << "terrain mask is in the incorrect format, and couldn't be applied\n";
01734             return;
01735         } catch(twml_exception& e) {
01736             e.show(*screen);
01737             return;
01738         }
01739 
01740         game_map->overlay(mask, cfg.get_parsed_config(), loc.x, loc.y);
01741         rebuild_screen_ = true;
01742     }
01743 
01744     // If we should spawn a new unit on the map somewhere
01745     else if(cmd == "unit") {
01746         assert(units != NULL);
01747         assert(game_map != NULL);
01748         assert(status_ptr != NULL);
01749         assert(state_of_game != NULL);
01750         unit new_unit(units,game_map,status_ptr,teams,cfg.get_parsed_config(),true, state_of_game);
01751         preferences::encountered_units().insert(new_unit.type_id());
01752         gamemap::location loc = cfg_to_loc(cfg);
01753 
01754         if(game_map->on_board(loc)) {
01755             loc = find_vacant_tile(*game_map,*units,loc);
01756             const bool show = screen != NULL && !screen->fogged(loc);
01757             const bool animate = show && utils::string_bool(cfg["animate"], false);
01758 
01759             units->erase(loc);
01760             units->add(new std::pair<gamemap::location,unit>(loc,new_unit));
01761             unit_mutations++;
01762             if(game_map->is_village(loc)) {
01763                 get_village(loc,*screen,*teams,new_unit.side()-1,*units);
01764             }
01765 
01766             screen->invalidate(loc);
01767 
01768             unit_map::iterator un = units->find(loc);
01769 
01770             if(animate) {
01771                 unit_display::unit_recruited(loc);
01772             }
01773             else if(show) {
01774                 screen->draw();
01775             }
01776         } else {
01777             player_info* const player = state_of_game->get_player((*teams)[new_unit.side()-1].save_id());
01778 
01779             if(player != NULL) {
01780                 player->available_units.push_back(new_unit);
01781             } else {
01782               ERR_NG << "Cannot create unit: location (" << loc.x << "," << loc.y <<") is not on the map, and player "
01783                     << new_unit.side() << " has no recall list.\n";
01784             }
01785         }
01786     }
01787 
01788     // If we should recall units that match a certain description
01789     else if(cmd == "recall") {
01790         LOG_NG << "recalling unit...\n";
01791         bool unit_recalled = false;
01792         config temp_config(cfg.get_config());
01793         // Prevent the recall unit filter from using the location as a criterion
01794         //! @todo FIXME: we should design the WML to avoid these types of collisions;
01795         // filters should be named consistently and always have a distinct scope.
01796         temp_config["x"] = "";
01797         temp_config["y"] = "";
01798         vconfig unit_filter(&temp_config);
01799         for(int index = 0; !unit_recalled && index < int(teams->size()); ++index) {
01800             LOG_NG << "for side " << index << "...\n";
01801             const std::string player_id = (*teams)[index].save_id();
01802             player_info* const player = state_of_game->get_player(player_id);
01803 
01804             if(player == NULL) {
01805                 ERR_NG << "player not found!\n";
01806                 continue;
01807             }
01808 
01809             std::vector<unit>& avail = player->available_units;
01810 
01811             for(std::vector<unit>::iterator u = avail.begin(); u != avail.end(); ++u) {
01812                 DBG_NG << "checking unit against filter...\n";
01813                 u->set_game_context(units,game_map,status_ptr,teams);
01814                 scoped_recall_unit auto_store("this_unit", player_id, u - avail.begin());
01815                 if(game_events::unit_matches_filter(*u, unit_filter, gamemap::location())) {
01816                     gamemap::location loc = cfg_to_loc(cfg);
01817                     unit to_recruit(*u);
01818                     avail.erase(u); // Erase before recruiting, since recruiting can fire more events
01819                     unit_mutations++;
01820                     recruit_unit(*game_map,index+1,*units,to_recruit,loc,true,utils::string_bool(cfg["show"],true),false,true,true);
01821                     unit_recalled = true;
01822                     break;
01823                 }
01824             }
01825         }
01826     } else if(cmd == "object") {
01827         const vconfig filter = cfg.child("filter");
01828 
01829         std::string id = cfg["id"];
01830         assert(state_of_game != NULL);
01831 
01832         // If this item has already been used
01833         if(id != "" && used_items.count(id))
01834             return;
01835 
01836         std::string image = cfg["image"];
01837         std::string caption = cfg["name"];
01838         std::string text;
01839 
01840         gamemap::location loc;
01841         if(!filter.null()) {
01842             for(unit_map::const_iterator u = units->begin(); u != units->end(); ++u) {
01843                 if(game_events::unit_matches_filter(u, filter)) {
01844                     loc = u->first;
01845                     break;
01846                 }
01847             }
01848         }
01849 
01850         if(loc.valid() == false) {
01851             loc = event_info.loc1;
01852         }
01853 
01854         const unit_map::iterator u = units->find(loc);
01855 
01856         std::string command_type = "then";
01857 
01858         if(u != units->end() && (filter.null() || game_events::unit_matches_filter(u, filter))) {
01859             text = cfg["description"];
01860 
01861             u->second.add_modification("object", cfg.get_parsed_config());
01862 
01863             screen->select_hex(event_info.loc1);
01864             screen->invalidate_unit();
01865 
01866             // Mark this item as used up.
01867             used_items.insert(id);
01868         } else {
01869             text = cfg["cannot_use_message"];
01870             command_type = "else";
01871         }
01872 
01873         if(!utils::string_bool(cfg["silent"])) {
01874             surface surface(NULL);
01875 
01876             if(image.empty() == false) {
01877                 surface.assign(image::get_image(image));
01878             }
01879 
01880             // Redraw the unit, with its new stats
01881             screen->draw();
01882 
01883             try {
01884                 const std::string duration_str = cfg["duration"];
01885                 const unsigned int lifetime = average_frame_time
01886                     * lexical_cast_default<unsigned int>(duration_str, prevent_misclick_duration);
01887 
01888                 wml_event_dialog to_show(*screen,((surface.null())? caption : ""),text);
01889                 if(!surface.null()) {
01890                     to_show.set_image(surface, caption);
01891                 }
01892                 to_show.layout();
01893                 to_show.show(lifetime);
01894             } catch(utils::invalid_utf8_exception&) {
01895                 // we already had a warning so do nothing.
01896             }
01897         }
01898 
01899         const vconfig::child_list commands = cfg.get_children(command_type);
01900         for(vconfig::child_list::const_iterator cmd = commands.begin();
01901         cmd != commands.end(); ++cmd) {
01902             if(!handle_event(event_info, *cmd)) {
01903                 mutated = false;
01904             }
01905         }
01906     }
01907 
01908     // Display a message on-screen
01909     else if(cmd == "print") {
01910         std::string text = cfg["text"];
01911         std::string size_str = cfg["size"];
01912         std::string duration_str = cfg["duration"];
01913         std::string red_str = cfg["red"];
01914         std::string green_str = cfg["green"];
01915         std::string blue_str = cfg["blue"];
01916 
01917         assert(state_of_game != NULL);
01918         const int size = lexical_cast_default<int>(size_str,font::SIZE_SMALL);
01919         const int lifetime = lexical_cast_default<int>(duration_str,50);
01920         const int red = lexical_cast_default<int>(red_str,0);
01921         const int green = lexical_cast_default<int>(green_str,0);
01922         const int blue = lexical_cast_default<int>(blue_str,0);
01923 
01924         SDL_Color colour = {red,green,blue,255};
01925 
01926         // Remove any old message.
01927         if (floating_label)
01928             font::remove_floating_label(floating_label);
01929 
01930         const std::string& msg = text;
01931         if(msg != "") {
01932             const SDL_Rect rect = screen->map_outside_area();
01933             floating_label = font::add_floating_label(msg,size,colour,
01934                     rect.w/2,rect.h/2,0.0,0.0,lifetime,rect,font::CENTER_ALIGN);
01935         }
01936     }
01937 
01938     else if(cmd == "deprecated_message") {
01939         // Note: no need to translate the string, since only used for deprecated things.
01940         const std::string message = cfg["message"];
01941         lg::wml_error << message << '\n';
01942     }
01943 
01944     else if(cmd == "debug_message") {
01945         const std::string log_level = cfg["logger"];
01946         const std::string log_message = cfg["message"];
01947         if (log_level == "err" || log_level == "error")
01948             ERR_NO << log_message << "\n";
01949         else if (log_level == "warn" || log_level == "wrn" || log_level == "warning")
01950             WRN_NO << log_message << "\n";
01951         else
01952             LOG_NO << log_message << "\n";
01953     }
01954 
01955     // Display a message dialog
01956     else if(cmd == "message") {
01957         // Check if there is any input to be made, if not the message may be skipped
01958         const vconfig::child_list menu_items = cfg.get_children("option");
01959 
01960         const vconfig::child_list text_input_elements = cfg.get_children("text_input");
01961         const bool has_text_input = (text_input_elements.size() == 1);
01962 
01963         bool has_input= (has_text_input || !menu_items.empty() );
01964 
01965         if (skip_messages && !has_input ) {
01966             return;
01967         }
01968 
01969         // Check if this message is for this side
01970         std::string side_for_raw = cfg["side_for"];
01971         bool side_for_show = true;
01972         if (!side_for_raw.empty())
01973         {
01974 
01975             assert(state_of_game != 0);
01976             side_for_show = false;
01977 
01978             std::vector<std::string> side_for =
01979                 utils::split(side_for_raw, ',', utils::STRIP_SPACES | utils::REMOVE_EMPTY);
01980             std::vector<std::string>::iterator itSide;
01981             size_t side;
01982 
01983             // Check if any of side numbers are human controlled
01984             for (itSide = side_for.begin(); itSide != side_for.end(); ++itSide)
01985             {
01986                 side = lexical_cast_default<size_t>(*itSide);
01987                 // Make sanity check that side number is good
01988                 // then check if this side is human controlled.
01989                 if (side > 0
01990                     && side <= teams->size()
01991                     && (*teams)[side-1].is_human())
01992                 {
01993                     side_for_show = true;
01994                     break;
01995                 }
01996             }
01997             if (!side_for_show)
01998             {
01999                 DBG_NG << "player isn't controlling side which should get message\n";
02000             }
02001         }
02002         unit_map::iterator speaker = units->end();
02003 
02004         std::string speaker_str = cfg["speaker"];
02005         assert(state_of_game != NULL);
02006         if(speaker_str == "unit") {
02007             speaker = units->find(event_info.loc1);
02008         } else if(speaker_str == "second_unit") {
02009             speaker = units->find(event_info.loc2);
02010         } else if(speaker_str != "narrator") {
02011             for(speaker = units->begin(); speaker != units->end(); ++speaker){
02012                 if(game_events::unit_matches_filter(speaker,cfg))
02013                     break;
02014             }
02015         }
02016 
02017         if(speaker == units->end() && speaker_str != "narrator") {
02018             // No matching unit found, so the dialog can't come up.
02019             // Continue onto the next message.
02020             WRN_NG << "cannot show message\n";
02021             return;
02022         }
02023 
02024         if(speaker != units->end()) {
02025             LOG_NG << "set speaker to '" << speaker->second.name() << "'\n";
02026         } else {
02027             LOG_NG << "no speaker\n";
02028         }
02029 
02030         std::string sfx = cfg["sound"];
02031         if(sfx != "") {
02032             sound::play_sound(sfx);
02033         }
02034 
02035         std::string image = cfg["image"];
02036         std::string caption = cfg["caption"];
02037 
02038         if(speaker != units->end()) {
02039             LOG_DP << "scrolling to speaker..\n";
02040             screen->highlight_hex(speaker->first);
02041             const int offset_from_center = maximum<int>(0, speaker->first.y - 1);
02042             screen->scroll_to_tile(gamemap::location(speaker->first.x,offset_from_center));
02043             screen->highlight_hex(speaker->first);
02044 
02045             if(image.empty()) {
02046                 image = speaker->second.profile();
02047                 if(image == speaker->second.absolute_image()) {
02048                     std::stringstream ss;
02049 
02050 #ifdef LOW_MEM
02051                     ss  << image;
02052 #else
02053                     ss  << image << speaker->second.image_mods();
02054 #endif
02055 
02056                     image = ss.str();
02057                 }
02058             }
02059 
02060             if(caption.empty()) {
02061                 caption = speaker->second.name();
02062                 if(caption.empty()) {
02063                     caption = speaker->second.type_name();
02064                 }
02065             }
02066             LOG_DP << "done scrolling to speaker...\n";
02067         } else {
02068             screen->highlight_hex(gamemap::location::null_location);
02069         }
02070         screen->draw(false);
02071 
02072         std::vector<std::string> options;
02073         std::vector<vconfig::child_list> option_events;
02074 
02075         for(vconfig::child_list::const_iterator mi = menu_items.begin();
02076                 mi != menu_items.end(); ++mi) {
02077             std::string msg_str = (*mi)["message"];
02078             if(!(*mi).has_child("show_if")
02079                 || game_events::conditional_passed(units,(*mi).child("show_if"))) {
02080                 options.push_back(msg_str);
02081                 option_events.push_back((*mi).get_children("command"));
02082             }
02083         }
02084 
02085         if(text_input_elements.size()>1) {
02086             lg::wml_error << "too many text_input tags, only one accepted\n";
02087         }
02088 
02089         const vconfig text_input_element = has_text_input ?
02090             text_input_elements.front() : vconfig();
02091 
02092         surface surface(NULL);
02093         if(image.empty() == false) {
02094             surface.assign(image::get_image(image));
02095         }
02096 
02097         int option_chosen = -1;
02098         std::string text_input_result;
02099 
02100         DBG_DP << "showing dialog...\n";
02101 
02102         // If we're not replaying, or if we are replaying
02103         // and there is no input to be made, show the dialog.
02104         if(get_replay_source().at_end() || (options.empty() && !has_text_input) ) {
02105 
02106             if (side_for_show && !get_replay_source().is_skipping())
02107             {
02108                 const t_string msg = cfg["message"];
02109                 const std::string duration_str = cfg["duration"];
02110                 const unsigned int lifetime = average_frame_time * lexical_cast_default<unsigned int>(duration_str, prevent_misclick_duration);
02111                 const SDL_Rect& map_area = screen->map_outside_area();
02112 
02113                 try {
02114                     wml_event_dialog to_show(*screen, ((surface.null())? caption : ""),
02115                         msg, ((options.empty()&& !has_text_input)? gui::MESSAGE : gui::OK_ONLY));
02116                     if(!surface.null()) {
02117                         to_show.set_image(surface, caption);
02118                     }
02119                     if(!options.empty()) {
02120                         to_show.set_menu(options);
02121                     }
02122                     if(has_text_input) {
02123                         std::string text_input_label=text_input_element["label"];
02124                         std::string text_input_content=text_input_element["text"];
02125                         std::string max_size_str=text_input_element["max_length"];
02126                         int input_max_size=lexical_cast_default<int>(max_size_str, 256);
02127                         if(input_max_size>1024||input_max_size<1){
02128                             lg::wml_error << "invalid maximum size for input "<<input_max_size<<"\n";
02129                             input_max_size=256;
02130                         }
02131                         to_show.set_textbox(text_input_label, text_input_content, input_max_size);
02132                     }
02133                     gui::dialog::dimension_measurements dim = to_show.layout();
02134                     to_show.get_menu().set_width( dim.menu_width );
02135                     to_show.get_menu().set_max_width( dim.menu_width );
02136                     to_show.get_menu().wrap_words();
02137                     static const int dialog_top_offset = 26;
02138                     to_show.layout(-1, map_area.y + dialog_top_offset);
02139                     option_chosen = to_show.show(lifetime);
02140                     if(has_text_input) {
02141                         text_input_result=to_show.textbox_text();
02142                     }
02143                     LOG_DP << "showed dialog...\n";
02144 
02145                     if (option_chosen == gui::ESCAPE_DIALOG) {
02146                         skip_messages = true;
02147                     }
02148 
02149                     if(!options.empty()) {
02150                         recorder.choose_option(option_chosen);
02151                     }
02152                     if(has_text_input) {
02153                         recorder.text_input(text_input_result);
02154                     }
02155                 } catch(utils::invalid_utf8_exception&) {
02156                     // we already had a warning so do nothing.
02157                 }
02158             }
02159 
02160         // Otherwise if an input has to be made, get it from the replay data
02161         } else {
02162                     //! @todo FIXME: get player_number_ from the play_controller, not from the WML vars.
02163             const t_string& side_str = state_of_game->get_variable("side_number");
02164             const int side = lexical_cast_default<int>(side_str.base_str(), -1);
02165 
02166 
02167 
02168             if(!options.empty()) {
02169                 do_replay_handle(*screen,*game_map,*units,*teams,
02170                            side ,*status_ptr,*state_of_game,std::string("choose"));
02171                 const config* action = get_replay_source().get_next_action();
02172                 if(action == NULL || action->get_children("choose").empty()) {
02173                     replay::throw_error("choice expected but none found\n");
02174                 }
02175                 const std::string& val = (*(action->get_children("choose").front()))["value"];
02176                 option_chosen = atol(val.c_str());
02177             }
02178             if(has_text_input) {
02179                 do_replay_handle(*screen,*game_map,*units,*teams,
02180                            side ,*status_ptr,*state_of_game,std::string("input"));
02181                 const config* action = get_replay_source().get_next_action();
02182                 if(action == NULL || action->get_children("input").empty()) {
02183                     replay::throw_error("input expected but none found\n");
02184                 }
02185                 text_input_result = (*(action->get_children("input").front()))["text"];
02186             }
02187         }
02188 
02189         // Implement the consequences of the choice
02190         if(options.empty() == false) {
02191             if(size_t(option_chosen) >= menu_items.size()) {
02192                 std::stringstream errbuf;
02193                 errbuf << "invalid choice (" << option_chosen
02194                        << ") was specified, choice 0 to " << (menu_items.size() - 1)
02195                        << " was expected.\n";
02196                 replay::throw_error(errbuf.str());
02197             }
02198 
02199             vconfig::child_list events = option_events[option_chosen];
02200             for(vconfig::child_list::const_iterator itor = events.begin();
02201             itor != events.end(); ++itor) {
02202                 if(!handle_event(event_info, *itor)) {
02203                     mutated = false;
02204                 }
02205             }
02206         }
02207         if(has_text_input) {
02208             std::string variable_name=text_input_element["variable"];
02209             if(variable_name.empty())
02210                 variable_name="input";
02211             state_of_game->set_variable(variable_name, text_input_result);
02212         }
02213     }
02214 
02215     else if(cmd == "kill") {
02216         // Use (x,y) iteration, because firing events ruins unit_map iteration
02217         for(gamemap::location loc(0,0); loc.x < game_map->w(); ++loc.x) {
02218             for(loc.y = 0; loc.y < game_map->h(); ++loc.y) {
02219                 unit_map::iterator un = units->find(loc);
02220                 if(un != units->end() && game_events::unit_matches_filter(un,cfg)) {
02221                     if(utils::string_bool(cfg["animate"])) {
02222                         screen->scroll_to_tile(loc);
02223                         unit_display::unit_die(loc, un->second);
02224                     }
02225                     if(utils::string_bool(cfg["fire_event"])) {
02226                         game_events::entity_location death_loc(un);
02227                         game_events::fire("die", death_loc, death_loc);
02228                         un = units->find(death_loc);
02229                         if(un != units->end() && death_loc.matches_unit(un->second)) {
02230                             units->erase(un);
02231                             unit_mutations++;
02232                         }
02233                     } else {
02234                         units->erase(un);
02235                         unit_mutations++;
02236                     }
02237                 }
02238             }
02239         }
02240 
02241         // If the filter doesn't contain positional information,
02242         // then it may match units on all recall lists.
02243         if(cfg["x"].empty() && cfg["y"].empty()) {
02244             std::map<std::string, player_info>& players=state_of_game->players;
02245 
02246             for(std::map<std::string, player_info>::iterator pi = players.begin();
02247                     pi!=players.end(); ++pi)
02248             {
02249                 std::vector<unit>& avail_units = pi->second.available_units;
02250                 for(std::vector<unit>::iterator j = avail_units.begin(); j != avail_units.end();) {
02251                     j->set_game_context(units,game_map,status_ptr,teams);
02252                     scoped_recall_unit auto_store("this_unit", pi->first, j - avail_units.begin());
02253                     if(game_events::unit_matches_filter(*j, cfg,gamemap::location())) {
02254                         j = avail_units.erase(j);
02255                     } else {
02256                         ++j;
02257                     }
02258                 }
02259             }
02260         }
02261     }
02262 
02263     // Adding of new events
02264     else if(cmd == "event") {
02265         new_handlers.push_back(event_handler(cfg));
02266     }
02267 
02268     // Fire any events
02269     else if(cmd == "fire_event") {
02270         gamemap::location loc1,loc2;
02271         config data;
02272         if (cfg.has_child("primary_unit")) {
02273             vconfig u = cfg.child("primary_unit");
02274             if (u.has_attribute("x") && u.has_attribute("y"))
02275                 loc1 = cfg_to_loc(u);
02276             if (u.has_attribute("weapon")) {
02277                 config& f = data.add_child("first");
02278                 f["weapon"] = u.get_attribute("weapon");
02279             }
02280         }
02281         if (cfg.has_child("secondary_unit")) {
02282             vconfig u = cfg.child("secondary_unit");
02283             if (u.has_attribute("x") && u.has_attribute("y"))
02284                 loc2 = cfg_to_loc(u);
02285             if (u.has_attribute("weapon")) {
02286                 config& s = data.add_child("second");
02287                 s["weapon"] = u.get_attribute("weapon");
02288             }
02289         }
02290         game_events::fire(cfg["name"],loc1,loc2,data);
02291     }
02292 
02293     // Setting of menu items
02294     else if(cmd == "set_menu_item") {
02295         /*
02296         [set_menu_item]
02297             id=test1
02298             image="buttons/group_all.png"
02299             description="Summon Troll"
02300             [show_if]
02301                 [not]
02302                     [have_unit]
02303                         x,y=$x1,$y1
02304                     [/have_unit]
02305                 [/not]
02306             [/show_if]
02307             [filter_location]
02308             [/filter_location]
02309             [command]
02310                 {LOYAL_UNIT $side_number (Troll) $x1 $y1 (Myname) ( _ "Myname")}
02311             [/command]
02312         [/set_menu_item]
02313         */
02314         std::string id = cfg["id"];
02315         wml_menu_item*& mref = state_of_game->wml_menu_items[id];
02316         if(mref == NULL) {
02317             mref = new wml_menu_item(id);
02318         }
02319         if(cfg.has_attribute("image")) {
02320             mref->image = cfg["image"];
02321         }
02322         if(cfg.has_attribute("description")) {
02323             mref->description = cfg["description"];
02324         }
02325         if(cfg.has_attribute("needs_select")) {
02326             mref->needs_select = utils::string_bool(cfg["needs_select"], false);
02327         }
02328         if(cfg.has_child("show_if")) {
02329             mref->show_if = cfg.child("show_if").get_config();
02330         }
02331         if(cfg.has_child("filter_location")) {
02332             mref->filter_location = cfg.child("filter_location").get_config();
02333         }
02334         if(cfg.has_child("command")) {
02335             config* new_command = new config(cfg.child("command").get_config());
02336             wmi_command_changes.push_back(wmi_command_change(id, new_command));
02337         }
02338     }
02339     // Unit serialization to and from variables
02340     //! @todo FIXME: Check that store is automove bug safe
02341     else if(cmd == "store_unit") {
02342         const config empty_filter;
02343         vconfig filter = cfg.child("filter");
02344         if(filter.null())
02345             filter = &empty_filter;
02346 
02347         std::string variable = cfg["variable"];
02348         if(variable.empty()) {
02349             variable="unit";
02350         }
02351         const std::string mode = cfg["mode"];
02352         config to_store;
02353         variable_info varinfo(variable, true, variable_info::TYPE_ARRAY);
02354 
02355         const bool kill_units = utils::string_bool(cfg["kill"]);
02356 
02357         for(unit_map::iterator i = units->begin(); i != units->end();) {
02358             if(game_events::unit_matches_filter(i,filter) == false) {
02359                 ++i;
02360                 continue;
02361             }
02362 
02363             config& data = to_store.add_child(varinfo.key);
02364             i->first.write(data);
02365             i->second.write(data);
02366 
02367             if(kill_units) {
02368                 units->erase(i++);
02369                 unit_mutations++;
02370             } else {
02371                 ++i;
02372             }
02373         }
02374 
02375         if(filter["x"].empty() && filter["y"].empty()) {
02376             std::map<std::string, player_info>& players = state_of_game->players;
02377 
02378             for(std::map<std::string, player_info>::iterator pi = players.begin();
02379                     pi!=players.end(); ++pi) {
02380                 std::vector<unit>& avail_units = pi->second.available_units;
02381                 for(std::vector<unit>::iterator j = avail_units.begin(); j != avail_units.end();) {
02382                 j->set_game_context(units,game_map,status_ptr,teams);
02383                 scoped_recall_unit auto_store("this_unit", pi->first, j - avail_units.begin());
02384                 if(game_events::unit_matches_filter(*j, filter,gamemap::location()) == false) {
02385                         ++j;
02386                         continue;
02387                     }
02388                     config& data = to_store.add_child(varinfo.key);
02389                     j->write(data);
02390                     data["x"] = "recall";
02391                     data["y"] = "recall";
02392 
02393                     if(kill_units) {
02394                         j = avail_units.erase(j);
02395                     } else {
02396                         ++j;
02397                     }
02398                 }
02399             }
02400         }
02401         if(mode != "append") {
02402             varinfo.vars->clear_children(varinfo.key);
02403         }
02404         varinfo.vars->append(to_store);
02405     }
02406 
02407     else if(cmd == "unstore_unit") {
02408         assert(state_of_game != NULL);
02409         const config& var = state_of_game->get_variable_cfg(cfg["variable"]);
02410 
02411         try {
02412             assert(units != NULL);
02413             assert(game_map != NULL);
02414             assert(status_ptr != NULL);
02415             const unit u(units,game_map,status_ptr,teams,var, false);
02416 
02417             preferences::encountered_units().insert(u.type_id());
02418             gamemap::location loc(var, game_events::get_state_of_game());
02419             if(loc.valid()) {
02420                 if(utils::string_bool(cfg["find_vacant"])) {
02421                     loc = find_vacant_tile(*game_map,*units,loc);
02422                 }
02423 
02424                 units->erase(loc);
02425                 units->add(new std::pair<gamemap::location,unit>(loc,u));
02426                 unit_mutations++;
02427 
02428                 std::string text = cfg["text"];
02429                 if(!text.empty())
02430                 {
02431                     // Print floating label
02432                     std::string red_str = cfg["red"];
02433                     std::string green_str = cfg["green"];
02434                     std::string blue_str = cfg["blue"];
02435                     const int red = lexical_cast_default<int>(red_str,0);
02436                     const int green = lexical_cast_default<int>(green_str,0);
02437                     const int blue = lexical_cast_default<int>(blue_str,0);
02438                     {
02439                         screen->float_label(loc,text,red,green,blue);
02440                     }
02441                 }
02442 
02443                 if(utils::string_bool(cfg["advance"], true) && get_replay_source().at_end()) {
02444                     // Try to advance the unit
02445 
02446                     //! @todo FIXME: get player_number_ from the play_controller, not from the WML vars.
02447                     const t_string& side_str = state_of_game->get_variable("side_number");
02448                     const int side = lexical_cast_default<int>(side_str.base_str(), -1);
02449 
02450                     // Select advancement if it is on the playing side and the player is a human
02451                     const bool sel = (side == static_cast<int>(u.side())
02452                                      && (*teams)[side-1].is_human());
02453 
02454                     // The code in dialogs::advance_unit tests whether the unit can advance
02455                     dialogs::advance_unit(*game_map, *units, loc, *screen, !sel, true);
02456                 }
02457 
02458             } else {
02459                 player_info *player=state_of_game->get_player((*teams)[u.side()-1].save_id());
02460 
02461                 if(player) {
02462 
02463                     // Test whether the recall list has duplicates if so warn.
02464                     // This might be removed at some point but the uniqueness of
02465                     // the description is needed to avoid the recall duplication
02466                     // bugs. Duplicates here might cause the wrong unit being
02467                     // replaced by the wrong unit.
02468                     if(player->available_units.size() > 1) {
02469                         std::vector<std::string> desciptions;
02470                         for(std::vector<unit>::const_iterator citor =
02471                                 player->available_units.begin();
02472                                 citor != player->available_units.end(); ++citor) {
02473 
02474                             const std::string desciption =
02475                                 citor->underlying_id();
02476                             if(std::find(desciptions.begin(), desciptions.end(),
02477                                     desciption) != desciptions.end()) {
02478 
02479                                 lg::wml_error << "Recall list has duplicate unit "
02480                                     "description '" << desciption
02481                                     << "' unstore_unit may not work as expected.\n";
02482                             } else {
02483                                 desciptions.push_back(desciption);
02484                             }
02485                         }
02486                     }
02487 
02488                     // Avoid duplicates in the list.
02489                     //! @todo it would be better to change available_units from
02490                     //! a vector to a map and use the underlying_id
02491                     //! as key.
02492                     const std::string key = u.underlying_id();
02493                     for(std::vector<unit>::iterator itor =
02494                             player->available_units.begin();
02495                             itor != player->available_units.end(); ++itor) {
02496 
02497                         LOG_NG << "Replaced unit '"
02498                             << key << "' on the recall list\n";
02499                         if(itor->underlying_id() == key) {
02500                             player->available_units.erase(itor);
02501                             break;
02502                         }
02503                     }
02504                     player->available_units.push_back(u);
02505                 } else {
02506                     ERR_NG << "Cannot unstore unit: no recall list for player " << u.side()
02507                         << " and the map location is invalid.\n";
02508                 }
02509             }
02510 
02511             // If we unstore a leader make sure the team gets a leader if not the loading
02512             // in MP might abort since a side without a leader has a recall list.
02513             if(u.can_recruit()) {
02514                 (*teams)[u.side() - 1].no_leader() = false;
02515             }
02516 
02517         } catch(game::load_game_failed& e) {
02518             ERR_NG << "could not de-serialize unit: '" << e.message << "'\n";
02519         }
02520     }
02521 
02522     else if (cmd == "store_map_dimensions") {
02523         std::string variable = cfg["variable"];
02524         if (variable.empty()) {
02525             variable="map_size";
02526         }
02527         assert(state_of_game != NULL);
02528         state_of_game->get_variable(variable + ".width") = str_cast<int>(game_map->w());
02529         state_of_game->get_variable(variable + ".height") = str_cast<int>(game_map->h());
02530     }
02531 
02532     else if(cmd == "store_starting_location") {
02533         std::string side = cfg["side"];
02534         std::string variable = cfg["variable"];
02535         if (variable.empty()) {
02536             variable="location";
02537         }
02538         assert(state_of_game != NULL);
02539         const int side_num = lexical_cast_default<int>(side,1);
02540 
02541         const gamemap::location& loc = game_map->starting_position(side_num);
02542         assert(state_of_game != NULL);
02543         config &loc_store = state_of_game->get_variable_cfg(variable);
02544         loc_store.clear();
02545         loc.write(loc_store);
02546         game_map->write_terrain(loc, loc_store);
02547         if (game_map->is_village(loc)) {
02548             std::stringstream sd;
02549             int side = village_owner(loc,*teams) + 1;
02550             sd << side;
02551             loc_store["owner_side"]= sd.str();
02552         }
02553     }
02554 
02555     /* [store_villages] : store villages into an array
02556      * Keys:
02557      * - variable (mandatory): variable to store in
02558      * - side: if present, the village should be owned by this side (0=unowned villages)
02559      * - terrain: if present, filter the village types against this list of terrain types
02560      */
02561     else if(cmd == "store_villages" ) {
02562         log_scope("store_villages");
02563         std::string variable = cfg["variable"];
02564         if (variable.empty()) {
02565             variable="location";
02566         }
02567         config to_store;
02568         variable_info varinfo(variable, true, variable_info::TYPE_ARRAY);
02569 
02570         std::vector<gamemap::location> locs = game_map->villages();
02571 
02572         for(std::vector<gamemap::location>::const_iterator j = locs.begin(); j != locs.end(); ++j) {
02573             bool matches = false;
02574             if(cfg.has_attribute("side")) {     //! @deprecated, use owner_side instead
02575                 lg::wml_error << "side key is no longer accepted in [store_villages],"
02576                     << " use owner_side instead.\n";
02577                 config temp_cfg(cfg.get_config());
02578                 temp_cfg["owner_side"] = temp_cfg["side"];
02579                 temp_cfg["side"] = "";
02580                 matches = terrain_filter(&temp_cfg, *game_map, *status_ptr, *units).match(*j);
02581             } else {
02582                 matches = terrain_filter(cfg, *game_map, *status_ptr, *units).match(*j);
02583             }
02584             if(matches) {
02585                 config &loc_store = to_store.add_child(varinfo.key);
02586                 j->write(loc_store);
02587                 game_map->write_terrain(*j, loc_store);
02588                 std::stringstream sd;
02589                 int side = village_owner(*j,*teams) + 1;
02590                 sd << side;
02591                 loc_store["owner_side"]= sd.str();
02592             }
02593         }
02594         varinfo.vars->clear_children(varinfo.key);
02595         varinfo.vars->append(to_store);
02596     }
02597 
02598     else if(cmd == "store_locations" ) {
02599         log_scope("store_locations");
02600         std::string variable = cfg["variable"];
02601         if (variable.empty()) {
02602             variable="location";
02603         }
02604 
02605         std::set<gamemap::location> res;
02606         terrain_filter filter(cfg, *game_map, *status_ptr, *units);
02607         filter.restrict(game_config::max_loop);
02608         filter.get_locations(res);
02609 
02610         state_of_game->clear_variable_cfg(variable);
02611         for(std::set<gamemap::location>::const_iterator j = res.begin(); j != res.end(); ++j) {
02612             config &loc_store = state_of_game->add_variable_cfg(variable);
02613             j->write(loc_store);
02614             game_map->write_terrain(*j, loc_store);
02615             if (game_map->is_village(*j)) {
02616                 std::stringstream sd;
02617                 int side = village_owner(*j,*teams) + 1;
02618                 sd << side;
02619                 loc_store["owner_side"]= sd.str();
02620             }
02621         }
02622     }
02623 
02624     // Command to take control of a village for a certain side
02625     else if(cmd == "capture_village") {
02626         std::string side = cfg["side"];
02627         assert(state_of_game != NULL);
02628         const int side_num = lexical_cast_default<int>(side);
02629         // If 'side' is 0, then it will become an invalid index,
02630         // and so the village will become neutral.
02631         const size_t team_num = size_t(side_num-1);
02632 
02633         const std::vector<gamemap::location> locs(multiple_locs(cfg));
02634 
02635         for(std::vector<gamemap::location>::const_iterator i = locs.begin(); i != locs.end(); ++i) {
02636             if(game_map->is_village(*i)) {
02637                 get_village(*i,*screen,*teams,team_num,*units);
02638             }
02639         }
02640     }
02641 
02642     // Command to remove a variable
02643     else if(cmd == "clear_variable") {
02644         const std::string name = cfg["name"];
02645         std::vector<std::string> vars_to_clear = 
02646             utils::split(name, ',', utils::STRIP_SPACES | utils::REMOVE_EMPTY);
02647         foreach(const std::string& var, vars_to_clear) {
02648             state_of_game->clear_variable(var);
02649         }
02650     }
02651 
02652     else if(cmd == "endlevel") {
02653         // Remove 0-hp units from the unit map to avoid the following problem:
02654         // In case a die event triggers an endlevel the dead unit is still as a
02655         // 'ghost' in linger mode. After save loading in linger mode the unit
02656         // is fully visible again.
02657         unit_map::iterator u = units->begin();
02658         while(u != units->end()) {
02659             if(u->second.hitpoints() <= 0) {
02660                 units->erase(u++);
02661                 ++unit_mutations;
02662             } else {
02663                 ++u;
02664             }
02665         }
02666 
02667         const std::string next_scenario = cfg["next_scenario"];
02668         if(next_scenario.empty() == false) {
02669             state_of_game->next_scenario = next_scenario;
02670         }
02671 
02672         const std::string result = cfg["result"].base_str(); //do not translate
02673         if(result.empty() || result == "victory") {
02674             const bool bonus = utils::string_bool(cfg["bonus"],true);
02675             const int carry_over = lexical_cast_default<int>
02676                 (cfg["carryover_percentage"],
02677                 game_config::gold_carryover_percentage);
02678             const bool gold_add = utils::string_bool(cfg["carryover_add"],
02679                 game_config::gold_carryover_add);
02680 
02681             throw end_level_exception(VICTORY, carry_over, gold_add, bonus);
02682         } else if(result == "continue") {
02683             throw end_level_exception(LEVEL_CONTINUE);
02684         } else if(result == "continue_no_save") {
02685             throw end_level_exception(LEVEL_CONTINUE_NO_SAVE);
02686         } else {
02687             LOG_NG << "throwing event defeat...\n";
02688             throw end_level_exception(DEFEAT);
02689         }
02690     }
02691 
02692     else if(cmd == "redraw") {
02693         std::string side = cfg["side"];
02694         assert(state_of_game != NULL);
02695         if(side != "") {
02696             const int side_num = lexical_cast_default<int>(side);
02697             clear_shroud(*screen,*game_map,*units,*teams,side_num-1);
02698             screen->recalculate_minimap();
02699         }
02700         if(rebuild_screen_) {
02701             rebuild_screen_ = false;
02702             screen->recalculate_minimap();
02703             screen->rebuild_all();
02704         }
02705         screen->invalidate_all();
02706         screen->draw(true,true);
02707     }
02708 
02709     else if(cmd == "animate_unit") {
02710 
02711         unit_map::iterator u = units->find(event_info.loc1);
02712 
02713         // Search for a valid unit filter,
02714         // and if we have one, look for the matching unit
02715         vconfig filter = cfg.child("filter");
02716         if(!filter.null()) {
02717             for(u = units->begin(); u != units->end(); ++u){
02718                 if(game_events::unit_matches_filter(u, filter))
02719                     break;
02720             }
02721         }
02722 
02723         // We have found a unit that matches the filter
02724         if(u != units->end() && ! screen->fogged(u->first)) {
02725             attack_type *primary = NULL;
02726             attack_type *secondary = NULL;
02727             Uint32 text_color = 0;
02728             unit_animation::hit_type hits=  unit_animation::INVALID;
02729             std::vector<attack_type> attacks = u->second.attacks();
02730             std::vector<attack_type>::iterator itor;
02731 
02732             filter = cfg.child("primary_attack");
02733             if(!filter.null()) {
02734                 for(itor = attacks.begin(); itor != attacks.end(); ++itor){
02735                     if(itor->matches_filter(filter.get_parsed_config())) {
02736                         primary = &*itor;
02737                         break;
02738                     }
02739                 }
02740             }
02741 
02742             filter = cfg.child("secondary_attack");
02743             if(!filter.null()) {
02744                 for(itor = attacks.begin(); itor != attacks.end(); ++itor){
02745                     if(itor->matches_filter(filter.get_parsed_config())) {
02746                         secondary = &*itor;
02747                         break;
02748                     }
02749                 }
02750             }
02751 
02752             if(cfg["hit"] == "yes" || cfg["hit"] == "hit") {
02753                 hits = unit_animation::HIT;
02754             }
02755             if(cfg["hit"] == "no" || cfg["hit"] == "miss") {
02756                 hits = unit_animation::MISS;
02757             }
02758             if( cfg["hit"] == "kill" ) {
02759                 hits = unit_animation::KILL;
02760             }
02761             std::vector<std::string> tmp_string_vect=utils::split(cfg["text_color"]);
02762             if(tmp_string_vect.size() ==3) text_color = display::rgb(atoi(tmp_string_vect[0].c_str()),atoi(tmp_string_vect[1].c_str()),atoi(tmp_string_vect[2].c_str()));
02763             screen->scroll_to_tile(u->first);
02764             unit_animator animator;
02765             animator.add_animation(&u->second,cfg["flag"],u->first,lexical_cast_default<int>(cfg["value"]),utils::string_bool(cfg["with_bars"]),
02766             false,cfg["text"],text_color, hits,primary,secondary,0);
02767             animator.start_animations();
02768             animator.wait_for_end();
02769             u->second.set_standing(u->first);
02770             screen->invalidate(u->first);
02771             screen->draw();
02772             events::pump();
02773         }
02774     } else if(cmd == "label") {
02775 
02776         terrain_label label(screen->labels(),
02777                     cfg.get_config(),
02778                     game_events::get_state_of_game());
02779 
02780         screen->labels().set_label(label.location(),
02781                        label.text(),
02782                        label.team_name(),
02783                        label.colour());
02784     }
02785 
02786     else if(cmd== "heal_unit") {
02787         
02788         const bool animated = utils::string_bool(cfg["animate"],false);
02789         
02790         const vconfig healed_filter = cfg.child("filter");
02791         unit_map::iterator u;
02792         
02793         if (healed_filter.null()) {
02794             // Try to take the unit at loc1
02795             u = units->find(event_info.loc1);
02796         }
02797         else {
02798             for(u  = units->begin(); u != units->end(); ++u) {
02799                 if(game_events::unit_matches_filter(u, healed_filter))
02800                     break;
02801             }
02802         }
02803 
02804         const vconfig healers_filter = cfg.child("secondary_unit_filter");
02805         unit_map::iterator v;
02806         std::vector<unit_map::iterator> healers;
02807 
02808         if (!healers_filter.null()) {
02809             for(v  = units->begin(); v != units->end(); ++v) {
02810                 if(game_events::unit_matches_filter(v, healers_filter) &&
02811                    v->second.has_ability_type("heals")) {
02812                     healers.push_back(v);
02813                 }
02814             }
02815         }
02816 
02817         // We have found a unit
02818         if(u != units->end()) {
02819             int amount = lexical_cast_default<int>(cfg["amount"],0);
02820             int real_amount = u->second.hitpoints();
02821             u->second.heal(amount);
02822             real_amount = u->second.hitpoints() - real_amount;
02823 
02824             if (animated) {
02825                 unit_display::unit_healing(u->second,u->first,
02826                                healers,
02827                                real_amount);
02828             }
02829 
02830             state_of_game->set_variable("heal_amount",
02831                             str_cast<int>(real_amount));
02832         }
02833     }
02834 
02835     DBG_NG << "done handling command...\n";
02836 }
02837 
02838 static void commit_new_handlers() {
02839     // Commit any spawned events-within-events
02840     while(new_handlers.size() > 0) {
02841         event_handler& new_handler = new_handlers.back();
02842         std::vector<std::string> names = new_handler.names();
02843         std::vector<std::string>::iterator iter,end;
02844         for (iter = names.begin(),end = names.end();
02845              iter != end; ++iter) {
02846           events_map.insert(std::pair<std::string,event_handler>(*iter,new_handler));
02847           LOG_NG << "spawning new handler for event " << *iter << "\n";
02848         }
02849         //new_handler.cfg_->debug(lg::info(lg::engine));
02850         new_handlers.pop_back();
02851     }
02852 }
02853 
02854 static void commit_wmi_commands() {
02855     // Commit WML Menu Item command changes
02856     while(wmi_command_changes.size() > 0) {
02857         wmi_command_change wcc = wmi_command_changes.back();
02858         wml_menu_item*& mref = state_of_game->wml_menu_items[wcc.first];
02859         const bool no_current_handler = mref->command.empty();
02860         mref->command = *(wcc.second);
02861         if(no_current_handler) {
02862             if(!mref->command.empty()) {
02863                 mref->command["name"] = mref->name;
02864                 mref->command["first_time_only"] = "no";
02865                 event_handler new_handler(&mref->command);
02866                 std::vector<std::string> names = new_handler.names();
02867                 std::vector<std::string>::iterator iter,end;
02868                 for (iter = names.begin(),end = names.end();
02869                      iter != end; ++iter) {
02870                   events_map.insert(std::pair<std::string,event_handler>(*iter, new_handler));
02871                 }
02872             }
02873         } else if(mref->command.empty()) {
02874             mref->command["name"] = mref->name;
02875             mref->command["first_time_only"] = "no";
02876             mref->command.add_child("allow_undo");
02877         }
02878         LOG_NG << "setting command for " << mref->name << "\n";
02879         LOG_NG << *wcc.second;
02880         delete wcc.second;
02881         wmi_command_changes.pop_back();
02882     }
02883 }
02884 
02885 bool event_handler::handle_event(const queued_event& event_info, const vconfig conf)
02886 {
02887     if (first_time_only_)
02888     {
02889         disable();
02890     }
02891     bool mutated = true;
02892     bool skip_messages = false;
02893 
02894     vconfig cfg = conf;
02895     if(cfg.null()) {
02896         cfg = cfg_;
02897     }
02898     for(vconfig::all_children_iterator i = cfg.ordered_begin();
02899             i != cfg.ordered_end(); ++i) {
02900 
02901         //mutated and skip_messages will be modified
02902         handle_event_command(event_info, i.get_key(), i.get_child(), mutated, skip_messages);
02903     }
02904 
02905     // We do this once the event has completed any music alterations
02906     sound::commit_music_changes();
02907 
02908     return mutated;
02909 }
02910 
02911 } // end anonymous namespace (4)
02912 
02913 static bool process_event(event_handler& handler, const queued_event& ev)
02914 {
02915     if(handler.disabled())
02916         return false;
02917 
02918     unit_map::iterator unit1 = units->find(ev.loc1);
02919     unit_map::iterator unit2 = units->find(ev.loc2);
02920     bool filtered_unit1 = false, filtered_unit2 = false;
02921     scoped_xy_unit first_unit("unit", ev.loc1.x, ev.loc1.y, *units);
02922     scoped_xy_unit second_unit("second_unit", ev.loc2.x, ev.loc2.y, *units);
02923 
02924     const vconfig::child_list first_filters = handler.first_arg_filters();
02925     vconfig::child_list::const_iterator ffi;
02926     for(ffi = first_filters.begin();
02927             ffi != first_filters.end(); ++ffi) {
02928 
02929         if(unit1 == units->end() || !game_events::unit_matches_filter(unit1,*ffi)) {
02930             return false;
02931         }
02932         if(!ffi->empty()) {
02933             filtered_unit1 = true;
02934         }
02935     }
02936     bool special_matches = false;
02937     const vconfig::child_list first_special_filters = handler.first_special_filters();
02938     special_matches = first_special_filters.size() ? false : true;
02939     for(ffi = first_special_filters.begin();
02940             ffi != first_special_filters.end(); ++ffi) {
02941 
02942         if(unit1 != units->end() && game_events::matches_special_filter(ev.data.child("first"),*ffi)) {
02943             special_matches = true;
02944         }
02945         if(!ffi->empty()) {
02946             filtered_unit1 = true;
02947         }
02948     }
02949     if(!special_matches) {
02950         return false;
02951     }
02952 
02953     const vconfig::child_list second_filters = handler.second_arg_filters();
02954     for(vconfig::child_list::const_iterator sfi = second_filters.begin();
02955             sfi != second_filters.end(); ++sfi) {
02956         if(unit2 == units->end() || !game_events::unit_matches_filter(unit2,*sfi)) {
02957             return false;
02958         }
02959         if(!sfi->empty()) {
02960             filtered_unit2 = true;
02961         }
02962     }
02963     const vconfig::child_list second_special_filters = handler.second_special_filters();
02964     special_matches = second_special_filters.size() ? false : true;
02965     for(ffi = second_special_filters.begin();
02966             ffi != second_special_filters.end(); ++ffi) {
02967 
02968         if(unit2 != units->end() && game_events::matches_special_filter(ev.data.child("second"),*ffi)) {
02969             special_matches = true;
02970         }
02971         if(!ffi->empty()) {
02972             filtered_unit2 = true;
02973         }
02974     }
02975     if(!special_matches) {
02976         return false;
02977     }
02978     if(ev.loc1.requires_unit() && filtered_unit1
02979     && (unit1 == units->end() || !ev.loc1.matches_unit(unit1->second))) {
02980         // Wrong or missing entity at src location
02981         return false;
02982     }
02983     if(ev.loc2.requires_unit()  && filtered_unit2
02984     && (unit2 == units->end() || !ev.loc2.matches_unit(unit2->second))) {
02985         // Wrong or missing entity at dst location
02986         return false;
02987     }
02988 
02989     // The event hasn't been filtered out, so execute the handler
02990     const bool res = handler.handle_event(ev);
02991     if(ev.name == "select") {
02992         state_of_game->last_selected = ev.loc1;
02993     }
02994 
02995     if(handler.rebuild_screen()) {
02996         handler.rebuild_screen() = false;
02997         screen->recalculate_minimap();
02998         screen->invalidate_all();
02999         screen->rebuild_all();
03000     }
03001 
03002 
03003     return res;
03004 }
03005 
03006 namespace game_events {
03007 
03008 bool matches_special_filter(const config* cfg, const vconfig filter)
03009 {
03010     //! @todo FIXME: This filter should be deprecated and removed,
03011     // instead we should just auto-store $attacker_weapon and check it in a conditional
03012 
03013     if(!cfg) {
03014         return false;
03015     }
03016     bool matches = true;
03017     if(filter["weapon"] != "") {
03018         if(filter["weapon"] != (*cfg)["weapon"]) {
03019             matches = false;
03020         }
03021     }
03022 
03023     // Handle [and], [or], and [not] with in-order precedence
03024     vconfig::all_children_iterator cond_i = filter.ordered_begin();
03025     vconfig::all_children_iterator cond_end = filter.ordered_end();
03026     while(cond_i != cond_end)
03027     {
03028         const std::string& cond_name = cond_i.get_key();
03029         const vconfig& cond_filter = cond_i.get_child();
03030 
03031         // Handle [and]
03032         if(cond_name == "and")
03033         {
03034             matches = matches && matches_special_filter(cfg, cond_filter);
03035         }
03036         // Handle [or]
03037         else if(cond_name == "or")
03038         {
03039             matches = matches || matches_special_filter(cfg, cond_filter);
03040         }
03041         // Handle [not]
03042         else if(cond_name == "not")
03043         {
03044             matches = matches && !matches_special_filter(cfg, cond_filter);
03045         }
03046         ++cond_i;
03047     }
03048     return matches;
03049 }
03050 
03051 bool unit_matches_filter(const unit& u, const vconfig filter,const gamemap::location& loc)
03052 {
03053     return u.matches_filter(filter,loc);
03054 }
03055 
03056 bool unit_matches_filter(unit_map::const_iterator itor, const vconfig filter)
03057 {
03058     return itor->second.matches_filter(filter,itor->first);
03059 }
03060 
03061 static config::child_list unit_wml_configs;
03062 static std::set<std::string> unit_wml_ids;
03063 
03064 manager::manager(const config& cfg, game_display& gui_, gamemap& map_,
03065          soundsource::manager& sndsources_,
03066                  unit_map& units_,
03067                  std::vector<team>& teams_,
03068                  game_state& state_of_game_, gamestatus& status) :
03069     variable_manager(&state_of_game_)
03070 {
03071     const config::child_list& events_list = cfg.get_children("event");
03072     for(config::child_list::const_iterator i = events_list.begin();
03073         i != events_list.end(); ++i) {
03074         event_handler new_handler(*i);
03075         std::vector<std::string> names = new_handler.names();
03076         std::vector<std::string>::iterator iter,end;
03077         for (iter = names.begin(),end = names.end();
03078              iter != end; ++iter) {
03079           events_map.insert(std::pair<std::string,event_handler>(*iter, new_handler));
03080         }
03081     }
03082     std::vector<std::string> unit_ids = utils::split(cfg["unit_wml_ids"]);
03083     for(std::vector<std::string>::const_iterator id_it = unit_ids.begin(); id_it != unit_ids.end(); ++id_it) {
03084         unit_wml_ids.insert(*id_it);
03085     }
03086 
03087     teams = &teams_;
03088     screen = &gui_;
03089     soundsources = &sndsources_;
03090     game_map = &map_;
03091     units = &units_;
03092     state_of_game = &state_of_game_;
03093     status_ptr = &status;
03094 
03095     used_items.clear();
03096 
03097     const std::string used = cfg["used_items"];
03098     if(!used.empty()) {
03099         const std::vector<std::string>& v = utils::split(used);
03100         for(std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i) {
03101             used_items.insert(*i);
03102         }
03103     }
03104     int wmi_count = 0;
03105     std::map<std::string, wml_menu_item *>::iterator itor = state_of_game->wml_menu_items.begin();
03106     while(itor != state_of_game->wml_menu_items.end()) {
03107         if(!itor->second->command.empty()) {
03108             event_handler new_handler(&itor->second->command);
03109             std::vector<std::string> names = new_handler.names();
03110             std::vector<std::string>::iterator iter,end;
03111             for (iter = names.begin(),end = names.end();
03112                  iter != end; ++iter) {
03113               events_map.insert(std::pair<std::string,event_handler>(*iter, new_handler));
03114             }
03115         }
03116         ++itor;
03117         ++wmi_count;
03118     }
03119     if(wmi_count > 0) {
03120         LOG_NG << wmi_count << " WML menu items found, loaded." << std::endl;
03121     }
03122 }
03123 
03124 void write_events(config& cfg)
03125 {
03126     for(std::multimap<std::string,event_handler>::const_iterator i = events_map.begin(); i != events_map.end(); ++i) {
03127         if(!i->second.disabled() && !i->second.is_menu_item()) {
03128             i->second.write(cfg.add_child("event"));
03129         }
03130     }
03131 
03132     std::stringstream used;
03133     std::set<std::string>::const_iterator u;
03134     for(u = used_items.begin(); u != used_items.end(); ++u) {
03135         if(u != used_items.begin())
03136             used << ",";
03137 
03138         used << *u;
03139     }
03140 
03141     cfg["used_items"] = used.str();
03142     std::stringstream ids;
03143     for(u = unit_wml_ids.begin(); u != unit_wml_ids.end(); ++u) {
03144         if(u != unit_wml_ids.begin())
03145             ids << ",";
03146 
03147         ids << *u;
03148     }
03149 
03150     cfg["unit_wml_ids"] = ids.str();
03151 
03152     if(screen != NULL)
03153         screen->write_overlays(cfg);
03154 }
03155 
03156 manager::~manager() {
03157     events_queue.clear();
03158     events_map.clear();
03159     screen = NULL;
03160     game_map = NULL;
03161     units = NULL;
03162     state_of_game = NULL;
03163     status_ptr = NULL;
03164     for(config::child_list::iterator d = unit_wml_configs.begin(); d != unit_wml_configs.end(); ++d) {
03165         delete *d;
03166     }
03167     unit_wml_configs.clear();
03168     unit_wml_ids.clear();
03169 }
03170 
03171 void raise(const std::string& event,
03172            const entity_location& loc1,
03173            const entity_location& loc2,
03174            const config& data)
03175 {
03176     if(!events_init())
03177         return;
03178 
03179     events_queue.push_back(queued_event(event,loc1,loc2,data));
03180 }
03181 
03182 bool fire(const std::string& event,
03183           const entity_location& loc1,
03184           const entity_location& loc2,
03185           const config& data)
03186 {
03187     raise(event,loc1,loc2,data);
03188     return pump();
03189 }
03190 
03191 void add_events(const config::child_list& cfgs,const std::string& id)
03192 {
03193     if(std::find(unit_wml_ids.begin(),unit_wml_ids.end(),id) == unit_wml_ids.end()) {
03194         unit_wml_ids.insert(id);
03195         for(config::child_list::const_iterator new_ev = cfgs.begin(); new_ev != cfgs.end(); ++ new_ev) {
03196             unit_wml_configs.push_back(new config(**new_ev));
03197             event_handler new_handler(&(*unit_wml_configs.back()));
03198             std::vector<std::string> names = new_handler.names();
03199             std::vector<std::string>::iterator iter,end;
03200             for (iter = names.begin(),end = names.end();
03201                  iter != end; ++iter) {
03202               events_map.insert(std::pair<std::string,event_handler>(*iter, new_handler));
03203             }
03204         }
03205     }
03206 }
03207 
03208 bool pump()
03209 {
03210     if(!events_init())
03211         return false;
03212 
03213     bool result = false;
03214 
03215     while(events_queue.empty() == false) {
03216         queued_event ev = events_queue.front();
03217         events_queue.pop_front();   // pop now for exception safety
03218         const std::string& event_name = ev.name;
03219         typedef std::multimap<std::string,event_handler>::iterator itor;
03220 
03221         // Clear the unit cache, since the best clearing time is hard to figure out
03222         // due to status changes by WML. Every event will flush the cache.
03223         unit::clear_status_caches();
03224 
03225         // Find all handlers for this event in the map
03226         std::pair<itor,itor> i = events_map.equal_range(event_name);
03227 
03228         // Set the variables for the event
03229         if(i.first != i.second && state_of_game != NULL) {
03230             char buf[50];
03231             snprintf(buf,sizeof(buf),"%d",ev.loc1.x+1);
03232             state_of_game->set_variable("x1", buf);
03233 
03234             snprintf(buf,sizeof(buf),"%d",ev.loc1.y+1);
03235             state_of_game->set_variable("y1", buf);
03236 
03237             snprintf(buf,sizeof(buf),"%d",ev.loc2.x+1);
03238             state_of_game->set_variable("x2", buf);
03239 
03240             snprintf(buf,sizeof(buf),"%d",ev.loc2.y+1);
03241             state_of_game->set_variable("y2", buf);
03242         }
03243 
03244         while(i.first != i.second) {
03245             LOG_NG << "processing event '" << event_name << "'\n";
03246             event_handler& handler = i.first->second;
03247             if(process_event(handler, ev))
03248                 result = true;
03249             ++i.first;
03250         }
03251 
03252         commit_wmi_commands();
03253         commit_new_handlers();
03254 
03255         // Dialogs can only be shown if the display is not locked
03256         if(! screen->video().update_locked()) {
03257             show_wml_errors();
03258         }
03259     }
03260 
03261     return result;
03262 }
03263 
03264 Uint32 mutations() {
03265     return unit_mutations;
03266 }
03267 
03268 entity_location::entity_location(gamemap::location loc, const std::string& id)
03269     : location(loc), id_(id)
03270 {}
03271 
03272 entity_location::entity_location(unit_map::iterator itor)
03273     : location(itor->first), id_(itor->second.underlying_id())
03274 {}
03275 
03276 bool entity_location::matches_unit(const unit& u) const
03277 {
03278     return id_ == u.underlying_id();
03279 }
03280 
03281 bool entity_location::requires_unit() const
03282 {
03283     return !id_.empty();
03284 }
03285 
03286 } // end namespace game_events (2)
03287 
03288 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs