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