The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
action_wml.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Implementations of action WML tags, other than those implemented in Lua, and
18  * excluding conditional action WML.
19  */
20 
21 #include "global.hpp"
22 #include "action_wml.hpp"
23 #include "conditional_wml.hpp"
24 #include "manager.hpp"
25 #include "pump.hpp"
26 
27 #include "actions/attack.hpp"
28 #include "actions/create.hpp"
29 #include "actions/move.hpp"
30 #include "actions/vision.hpp"
31 #include "ai/manager.hpp"
32 #include "config_assign.hpp"
33 #include "fake_unit_ptr.hpp"
34 #include "filesystem.hpp"
35 #include "game_classification.hpp"
36 #include "game_display.hpp"
37 #include "game_errors.hpp"
38 #include "game_preferences.hpp"
39 #include "gettext.hpp"
41 #include "gui/widgets/window.hpp"
42 #include "log.hpp"
43 #include "map/map.hpp"
44 #include "map/exception.hpp"
45 #include "map/label.hpp"
46 #include "pathfind/teleport.hpp"
47 #include "pathfind/pathfind.hpp"
48 #include "persist_var.hpp"
49 #include "play_controller.hpp"
50 #include "recall_list_manager.hpp"
51 #include "replay.hpp"
52 #include "random_new.hpp"
53 #include "resources.hpp"
55 #include "side_filter.hpp"
56 #include "sound.hpp"
57 #include "soundsource.hpp"
58 #include "synced_context.hpp"
59 #include "synced_user_choice.hpp"
60 #include "team.hpp"
61 #include "terrain/filter.hpp"
62 #include "units/unit.hpp"
64 #include "units/udisplay.hpp"
65 #include "units/filter.hpp"
66 #include "wml_exception.hpp"
67 #include "whiteboard/manager.hpp"
68 
69 #include <boost/assign/list_of.hpp>
70 #include <boost/scoped_ptr.hpp>
71 #include <boost/regex.hpp>
72 
73 static lg::log_domain log_engine("engine");
74 #define DBG_NG LOG_STREAM(debug, log_engine)
75 #define LOG_NG LOG_STREAM(info, log_engine)
76 #define WRN_NG LOG_STREAM(warn, log_engine)
77 #define ERR_NG LOG_STREAM(err, log_engine)
78 
79 static lg::log_domain log_display("display");
80 #define DBG_DP LOG_STREAM(debug, log_display)
81 #define LOG_DP LOG_STREAM(info, log_display)
82 
83 static lg::log_domain log_wml("wml");
84 #define LOG_WML LOG_STREAM(info, log_wml)
85 #define WRN_WML LOG_STREAM(warn, log_wml)
86 #define ERR_WML LOG_STREAM(err, log_wml)
87 
88 static lg::log_domain log_config("config");
89 #define ERR_CF LOG_STREAM(err, log_config)
90 
91 
92 // This file is in the game_events namespace.
93 namespace game_events
94 {
95 
96 // This must be defined before any WML actions are.
97 // (So keep it at the rop of this file?)
99 
100 namespace { // Support functions
101 
102  /**
103  * Converts a vconfig to a location (based on x,y=).
104  * The default parameter values cause the default return value (if neither
105  * x nor y is specified) to equal map_location::null_location().
106  */
107  map_location cfg_to_loc(const vconfig& cfg, int defaultx = -999, int defaulty = -999)
108  {
109  int x = cfg["x"].to_int(defaultx) - 1;
110  int y = cfg["y"].to_int(defaulty) - 1;
111 
112  return map_location(x, y);
113  }
114 
115  fake_unit_ptr create_fake_unit(const vconfig& cfg)
116  {
117  std::string type = cfg["type"];
118  std::string variation = cfg["variation"];
119  std::string img_mods = cfg["image_mods"];
120 
121  size_t side_num = cfg["side"].to_int(1);
122  if ( side_num == 0 || side_num > resources::teams->size() )
123  side_num = 1;
124 
125  unit_race::GENDER gender = string_gender(cfg["gender"]);
126  const unit_type *ut = unit_types.find(type);
127  if (!ut) return fake_unit_ptr();
128  fake_unit_ptr fake = fake_unit_ptr(unit_ptr(new unit(*ut, side_num, false, gender)));
129 
130  if(!variation.empty()) {
131  config mod;
132  config &effect = mod.add_child("effect");
133  effect["apply_to"] = "variation";
134  effect["name"] = variation;
135  fake->add_modification("variation",mod);
136  }
137 
138  if(!img_mods.empty()) {
139  config mod;
140  config &effect = mod.add_child("effect");
141  effect["apply_to"] = "image_mod";
142  effect["add"] = img_mods;
143  fake->add_modification("image_mod",mod);
144  }
145 
146  return fake;
147  }
148 
149  std::vector<map_location> fake_unit_path(const unit& fake_unit, const std::vector<std::string>& xvals, const std::vector<std::string>& yvals)
150  {
151  const gamemap *game_map = & resources::gameboard->map();
152  std::vector<map_location> path;
155  for(size_t i = 0; i != std::min(xvals.size(),yvals.size()); ++i) {
156  if(i==0){
157  src.x = std::stoi(xvals[i])-1;
158  src.y = std::stoi(yvals[i])-1;
159  if (!game_map->on_board(src)) {
160  ERR_CF << "invalid move_unit_fake source: " << src << '\n';
161  break;
162  }
163  path.push_back(src);
164  continue;
165  }
166  pathfind::shortest_path_calculator calc(fake_unit,
167  (*resources::teams)[fake_unit.side()-1],
169  *game_map);
170 
171  dst.x = std::stoi(xvals[i])-1;
172  dst.y = std::stoi(yvals[i])-1;
173  if (!game_map->on_board(dst)) {
174  ERR_CF << "invalid move_unit_fake destination: " << dst << '\n';
175  break;
176  }
177 
178  pathfind::plain_route route = pathfind::a_star_search(src, dst, 10000, &calc,
179  game_map->w(), game_map->h());
180 
181  if (route.steps.empty()) {
182  WRN_NG << "Could not find move_unit_fake route from " << src << " to " << dst << ": ignoring complexities" << std::endl;
183  pathfind::emergency_path_calculator calc(fake_unit, *game_map);
184 
185  route = pathfind::a_star_search(src, dst, 10000, &calc,
186  game_map->w(), game_map->h());
187  if(route.steps.empty()) {
188  // This would occur when trying to do a MUF of a unit
189  // over locations which are unreachable to it (infinite movement
190  // costs). This really cannot fail.
191  WRN_NG << "Could not find move_unit_fake route from " << src << " to " << dst << ": ignoring terrain" << std::endl;
192  pathfind::dummy_path_calculator calc(fake_unit, *game_map);
193  route = a_star_search(src, dst, 10000, &calc, game_map->w(), game_map->h());
194  assert(!route.steps.empty());
195  }
196  }
197  // we add this section to the end of the complete path
198  // skipping section's head because already included
199  // by the previous iteration
200  path.insert(path.end(),
201  route.steps.begin()+1, route.steps.end());
202 
203  src = dst;
204  }
205  return path;
206  }
207 } // end anonymous namespace (support functions)
208 
209 /**
210  * Using this constructor for a static object outside action_wml.cpp
211  * will likely lead to a static initialization fiasco.
212  * @param[in] tag The WML tag for this action.
213  * @param[in] function The callback for this action.
214  */
216 {
217  registry_[tag] = function;
218 }
219 
220 
221 /**
222  * WML_HANDLER_FUNCTION macro handles auto registration for wml handlers
223  *
224  * @param pname wml tag name
225  * @param pei the variable name of the queued_event object inside the function
226  * @param pcfg the variable name of the config object inside the function
227  *
228  * You are warned! This is evil macro magic!
229  *
230  * The following code registers a [foo] tag:
231  * \code
232  * // comment out unused parameters to prevent compiler warnings
233  * WML_HANDLER_FUNCTION(foo, event_info, cfg)
234  * {
235  * // code for foo
236  * }
237  * \endcode
238  *
239  * Generated code looks like this:
240  * \code
241  * void wml_func_foo(...);
242  * static wml_action wml_action_foo("foo", &wml_func_foo);
243  * void wml_func_foo(...)
244  * {
245  * // code for foo
246  * }
247  * \endcode
248  */
249 #define WML_HANDLER_FUNCTION(pname, pei, pcfg) \
250  static void wml_func_##pname(const queued_event &pei, const vconfig &pcfg); \
251  static wml_action wml_action_##pname(#pname, &wml_func_##pname); \
252  static void wml_func_##pname(const queued_event& pei, const vconfig& pcfg)
253 
254 
255 /// Experimental data persistence
256 /// @todo Finish experimenting.
258 {
259  if (!resources::controller->is_replay())
261 }
262 
263 static void on_replay_error(const std::string& message, bool /*b*/)
264 {
265  ERR_NG << "Error via [do_command]:" << std::endl;
266  ERR_NG << message << std::endl;
267 }
268 
269 // This tag exposes part of the code path used to handle [command]'s in replays
270 // This allows to perform scripting in WML that will use the same code path as player actions, for example.
271 WML_HANDLER_FUNCTION(do_command,, cfg)
272 {
273  // Doing this in a whiteboard applied context will cause bugs
274  // Note that even though game_events::pump() will always apply the real unit map
275  // It is still possible get a wml commands to run in a whiteboard applied context
276  // With the theme_items lua callbacks
277  if(resources::whiteboard->has_planned_unit_map())
278  {
279  ERR_NG << "[do_command] called while whiteboard is applied, ignoring" << std::endl;
280  return;
281  }
282 
283  static const std::set<std::string> allowed_tags = {"attack", "move", "recruit", "recall", "disband", "fire_event", "lua_ai"};
284 
285  const bool is_too_early = resources::gamedata->phase() != game_data::START && resources::gamedata->phase() != game_data::PLAY;
286  if(is_too_early)
287  {
288  ERR_NG << "[do_command] called too early, only allowed at START or later" << std::endl;
289  return;
290  }
291  for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++i)
292  {
293  if(allowed_tags.find( i.get_key()) == allowed_tags.end()) {
294  ERR_NG << "unsupported tag [" << i.get_key() << "] in [do_command]" << std::endl;
295  std::stringstream o;
296  std::copy(allowed_tags.begin(), allowed_tags.end(), std::ostream_iterator<std::string>(o, " "));
297  ERR_NG << "allowed tags: " << o.str() << std::endl;
298  continue;
299  }
300  //Note that this fires related events and everthing else that also happen normally.
301  //have to watch out with the undo stack, therefore forbid [auto_shroud] and [update_shroud] here...
303  /*commandname*/ i.get_key(),
304  /*data*/ i.get_child().get_parsed_config(),
305  /*use_undo*/ true,
306  /*show*/ true,
307  /*error_handler*/ &on_replay_error
308  );
309  }
310 }
311 
312 /// Experimental data persistence
313 /// @todo Finish experimenting.
315 {
317 }
318 
319 WML_HANDLER_FUNCTION(modify_turns,, cfg)
320 {
321  config::attribute_value value = cfg["value"];
322  std::string add = cfg["add"];
323  config::attribute_value current = cfg["current"];
325  if(!add.empty()) {
326  tod_man.modify_turns_by_wml(add);
327  } else if(!value.empty()) {
328  tod_man.set_number_of_turns_by_wml(value.to_int(-1));
329  }
330  // change current turn only after applying mods
331  if(!current.empty()) {
332  const unsigned int current_turn_number = tod_man.turn();
333  int new_turn_number = current.to_int(current_turn_number);
334  const unsigned int new_turn_number_u = static_cast<unsigned int>(new_turn_number);
335  if(new_turn_number_u < 1 || (new_turn_number > tod_man.number_of_turns() && tod_man.number_of_turns() != -1)) {
336  ERR_NG << "attempted to change current turn number to one out of range (" << new_turn_number << ")" << std::endl;
337  } else if(new_turn_number_u != current_turn_number) {
338  tod_man.set_turn_by_wml(new_turn_number_u, resources::gamedata);
340  }
341  }
342 }
343 
344 /// Moving a 'unit' - i.e. a dummy unit
345 /// that is just moving for the visual effect
346 WML_HANDLER_FUNCTION(move_unit_fake,, cfg)
347 {
348  fake_unit_ptr dummy_unit(create_fake_unit(cfg));
349  if(!dummy_unit.get())
350  return;
351 
352  const bool force_scroll = cfg["force_scroll"].to_bool(true);
353 
354  const std::string x = cfg["x"];
355  const std::string y = cfg["y"];
356 
357  const std::vector<std::string> xvals = utils::split(x);
358  const std::vector<std::string> yvals = utils::split(y);
359 
360  const std::vector<map_location>& path = fake_unit_path(*dummy_unit, xvals, yvals);
361  if (!path.empty()) {
362  // Always scroll.
363  unit_display::move_unit(path, dummy_unit.get_unit_ptr(), true, map_location::NDIRECTIONS, force_scroll);
364  }
365 }
366 
367 WML_HANDLER_FUNCTION(move_units_fake,, cfg)
368 {
369  LOG_NG << "Processing [move_units_fake]\n";
370 
371  const vconfig::child_list unit_cfgs = cfg.get_children("fake_unit");
372  size_t num_units = unit_cfgs.size();
373  std::vector<fake_unit_ptr > units;
374  units.reserve(num_units);
375  std::vector<std::vector<map_location> > paths;
376  paths.reserve(num_units);
377 
378  LOG_NG << "Moving " << num_units << " units\n";
379 
380  size_t longest_path = 0;
381 
382  for (const vconfig& config : unit_cfgs) {
383  const std::vector<std::string> xvals = utils::split(config["x"]);
384  const std::vector<std::string> yvals = utils::split(config["y"]);
385  int skip_steps = config["skip_steps"];
386  fake_unit_ptr u = create_fake_unit(config);
387  units.push_back(u);
388  paths.push_back(fake_unit_path(*u, xvals, yvals));
389  if(skip_steps > 0)
390  paths.back().insert(paths.back().begin(), skip_steps, paths.back().front());
391  longest_path = std::max(longest_path, paths.back().size());
392  DBG_NG << "Path " << paths.size() - 1 << " has length " << paths.back().size() << '\n';
393 
394  u->set_location(paths.back().front());
396  }
397 
398  LOG_NG << "Units placed, longest path is " << longest_path << " long\n";
399 
400  std::vector<map_location> path_step(2);
401  path_step.resize(2);
402  for(size_t step = 1; step < longest_path; ++step) {
403  DBG_NG << "Doing step " << step << "...\n";
404  for(size_t un = 0; un < num_units; ++un) {
405  if(step >= paths[un].size() || paths[un][step - 1] == paths[un][step])
406  continue;
407  DBG_NG << "Moving unit " << un << ", doing step " << step << '\n';
408  path_step[0] = paths[un][step - 1];
409  path_step[1] = paths[un][step];
410  unit_display::move_unit(path_step, units[un].get_unit_ptr());
411  units[un]->set_location(path_step[1]);
412  units[un]->anim_comp().set_standing();
413  }
414  }
415 
416  LOG_NG << "Units moved\n";
417 }
418 
419 /// If we should recall units that match a certain description.
420 WML_HANDLER_FUNCTION(recall,, cfg)
421 {
422  LOG_NG << "recalling unit...\n";
423  config temp_config(cfg.get_config());
424  // Prevent the recall unit filter from using the location as a criterion
425 
426  /**
427  * @todo FIXME: we should design the WML to avoid these types of
428  * collisions; filters should be named consistently and always have a
429  * distinct scope.
430  */
431  temp_config["x"] = "recall";
432  temp_config["y"] = "recall";
433  vconfig unit_filter_cfg(temp_config);
434  const vconfig & leader_filter = cfg.child("secondary_unit");
435 
436  for(int index = 0; index < int(resources::teams->size()); ++index) {
437  LOG_NG << "for side " << index + 1 << "...\n";
438  const std::string player_id = (*resources::teams)[index].save_id();
439 
440  if((*resources::teams)[index].recall_list().size() < 1) {
441  DBG_NG << "recall list is empty when trying to recall!\n"
442  << "player_id: " << player_id << " side: " << index+1 << "\n";
443  continue;
444  }
445 
446  recall_list_manager & avail = (*resources::teams)[index].recall_list();
447  std::vector<unit_map::unit_iterator> leaders = resources::units->find_leaders(index + 1);
448 
449  const unit_filter ufilt(unit_filter_cfg, resources::filter_con);
450  const unit_filter lfilt(leader_filter, resources::filter_con); // Note that if leader_filter is null, this correctly gives a null filter that matches all units.
451  for(std::vector<unit_ptr>::iterator u = avail.begin(); u != avail.end(); ++u) {
452  DBG_NG << "checking unit against filter...\n";
453  scoped_recall_unit auto_store("this_unit", player_id, u - avail.begin());
454  if (ufilt(*(*u), map_location())) {
455  DBG_NG << (*u)->id() << " matched the filter...\n";
456  const unit_ptr to_recruit = *u;
457  const unit* pass_check = to_recruit.get();
458  if(!cfg["check_passability"].to_bool(true)) pass_check = nullptr;
459  const map_location cfg_loc = cfg_to_loc(cfg);
460 
461  /// @todo fendrin: comment this monster
462  for (unit_map::const_unit_iterator leader : leaders) {
463  DBG_NG << "...considering " + leader->id() + " as the recalling leader...\n";
464  map_location loc = cfg_loc;
465  if ( lfilt(*leader) &&
466  unit_filter(vconfig(leader->recall_filter()), resources::filter_con).matches( *(*u),map_location() ) ) {
467  DBG_NG << "...matched the leader filter and is able to recall the unit.\n";
468  if(!resources::gameboard->map().on_board(loc))
469  loc = leader->get_location();
470  if(pass_check || (resources::units->count(loc) > 0))
471  loc = pathfind::find_vacant_tile(loc, pathfind::VACANT_ANY, pass_check);
472  if(resources::gameboard->map().on_board(loc)) {
473  DBG_NG << "...valid location for the recall found. Recalling.\n";
474  avail.erase(u); // Erase before recruiting, since recruiting can fire more events
475  actions::place_recruit(to_recruit, loc, leader->get_location(), 0, true,
476  cfg["show"].to_bool(true), cfg["fire_event"].to_bool(false),
477  true, true);
478  return;
479  }
480  }
481  }
482  if (resources::gameboard->map().on_board(cfg_loc)) {
483  map_location loc = cfg_loc;
484  if(pass_check || (resources::units->count(loc) > 0))
485  loc = pathfind::find_vacant_tile(loc, pathfind::VACANT_ANY, pass_check);
486  // Check if we still have a valid location
487  if (resources::gameboard->map().on_board(loc)) {
488  DBG_NG << "No usable leader found, but found usable location. Recalling.\n";
489  avail.erase(u); // Erase before recruiting, since recruiting can fire more events
490  map_location null_location = map_location::null_location();
491  actions::place_recruit(to_recruit, loc, null_location, 0, true, cfg["show"].to_bool(true),
492  cfg["fire_event"].to_bool(false), true, true);
493  return;
494  }
495  }
496  }
497  }
498  }
499  LOG_WML << "A [recall] tag with the following content failed:\n" << cfg.get_config().debug();
500 }
501 
502 namespace {
503  struct map_choice : public mp_sync::user_choice
504  {
505  map_choice(const std::string& filename) : filename_(filename) {}
507  virtual config query_user(int /*side*/) const
508  {
509  //Do a regex check for the file format to prevent sending aribitary files to other clients.
510  //Note: this allows only the new format.
511  static const std::string s_simple_terrain = "[A-Za-z\\\\\\|\\/]{1,4}";
512  static const std::string s_terrain = s_simple_terrain + "(\\^" + s_simple_terrain + ")?";
513  static const std::string s_sep = "(, |\\n)";
514  static const std::string s_prefix = "(\\d+ )?";
515  static const std::string s_all = "(" + s_prefix + s_terrain + s_sep + ")+";
516  static const boost::regex r_all(s_all);
517 
518  const std::string& mapfile = filesystem::get_wml_location(filename_);
519  std::string res = "";
520  if(filesystem::file_exists(mapfile)) {
521  res = filesystem::read_file(mapfile);
522  }
523  config retv;
524  if(boost::regex_match(res, r_all))
525  {
526  retv["map_data"] = res;
527  }
528  return retv;
529  }
530  virtual config random_choice(int /*side*/) const
531  {
532  return config();
533  }
534  virtual std::string description() const
535  {
536  return "Map Data";
537  }
538 
539  };
540 }
541 
542 /// Experimental map replace
543 /// @todo Finish experimenting.
544 WML_HANDLER_FUNCTION(replace_map,, cfg)
545 {
546  /*
547  * When a hex changes from a village terrain to a non-village terrain, and
548  * a team owned that village it loses that village. When a hex changes from
549  * a non-village terrain to a village terrain and there is a unit on that
550  * hex it does not automatically capture the village. The reason for not
551  * capturing villages it that there are too many choices to make; should a
552  * unit loose its movement points, should capture events be fired. It is
553  * easier to do this as wanted by the author in WML.
554  */
555 
556  const gamemap * game_map = & resources::gameboard->map();
557  gamemap map(*game_map);
558 
559  try {
560  if(!cfg["map_file"].empty()) {
561  config file_cfg = mp_sync::get_user_choice("map_data", map_choice(cfg["map_file"].str()));
562  map.read(file_cfg["map_data"].str(), false);
563  } else {
564  map.read(cfg["map"], false);
565  }
566  } catch(incorrect_map_format_error&) {
567  const std::string log_map_name = cfg["map"].empty() ? cfg["file"] : std::string("from inline data");
568  lg::wml_error() << "replace_map: Unable to load map " << log_map_name << std::endl;
569  return;
570  } catch(twml_exception& e) {
571  e.show(resources::screen->video());
572  return;
573  }
574 
575  if (map.total_width() > game_map->total_width()
576  || map.total_height() > game_map->total_height()) {
577  if (!cfg["expand"].to_bool()) {
578  lg::wml_error() << "replace_map: Map dimension(s) increase but expand is not set" << std::endl;
579  return;
580  }
581  }
582 
583  if (map.total_width() < game_map->total_width()
584  || map.total_height() < game_map->total_height()) {
585  if (!cfg["shrink"].to_bool()) {
586  lg::wml_error() << "replace_map: Map dimension(s) decrease but shrink is not set" << std::endl;
587  return;
588  }
589  }
590 
591  boost::optional<std::string> errmsg = resources::gameboard->replace_map(map);
592 
593  if (errmsg) {
594  lg::wml_error() << *errmsg << std::endl;
595  }
596 
600 }
601 
602 /// Experimental data persistence
603 /// @todo Finish experimenting.
605 {
606  if (!resources::controller->is_replay())
608 }
609 
610 WML_HANDLER_FUNCTION(set_variables,, cfg)
611 {
612  const t_string& name = cfg["name"];
614  if(name.empty()) {
615  ERR_NG << "trying to set a variable with an empty name:\n" << cfg.get_config().debug();
616  return;
617  }
618 
619  std::vector<config> data;
620  if(cfg.has_attribute("to_variable"))
621  {
622  try
623  {
625  for (const config& c : tovar.as_array())
626  {
627  data.push_back(c);
628  }
629  }
630  catch(const invalid_variablename_exception&)
631  {
632  ERR_NG << "Cannot do [set_variables] with invalid to_variable variable: " << cfg["to_variable"] << " with " << cfg.get_config().debug() << std::endl;
633  }
634  } else {
635  typedef std::pair<std::string, vconfig> vchild;
636  for (const vchild& p : cfg.all_ordered()) {
637  if(p.first == "value") {
638  data.push_back(p.second.get_parsed_config());
639  } else if(p.first == "literal") {
640  data.push_back(p.second.get_config());
641  } else if(p.first == "split") {
642  const vconfig & split_element = p.second;
643 
644  std::string split_string=split_element["list"];
645  std::string separator_string=split_element["separator"];
646  std::string key_name=split_element["key"];
647  if(key_name.empty())
648  {
649  key_name="value";
650  }
651 
652  bool remove_empty = split_element["remove_empty"].to_bool();
653 
654  char* separator = separator_string.empty() ? nullptr : &separator_string[0];
655 
656  std::vector<std::string> split_vector;
657 
658  //if no separator is specified, explode the string
659  if(separator == nullptr)
660  {
661  for(std::string::iterator i=split_string.begin(); i!=split_string.end(); ++i)
662  {
663  split_vector.push_back(std::string(1, *i));
664  }
665  }
666  else {
667  split_vector=utils::split(split_string, *separator, remove_empty ? utils::REMOVE_EMPTY | utils::STRIP_SPACES : utils::STRIP_SPACES);
668  }
669 
670  for(std::vector<std::string>::iterator i=split_vector.begin(); i!=split_vector.end(); ++i)
671  {
672  data.push_back(config_of(key_name, *i));
673  }
674  }
675  }
676  }
677  try
678  {
679  const std::string& mode = cfg["mode"];
680  if(mode == "merge")
681  {
682  if(dest.explicit_index() && data.size() > 1)
683  {
684  //merge children into one
685  config merged_children;
686  for (const config &cfg : data) {
687  merged_children.append(cfg);
688  }
689  data = boost::assign::list_of(merged_children).convert_to_container<std::vector<config> >();
690  }
691  dest.merge_array(data);
692  }
693  else if(mode == "insert")
694  {
695  dest.insert_array(data);
696  }
697  else if(mode == "append")
698  {
699  dest.append_array(data);
700  }
701  else /*default if(mode == "replace")*/
702  {
703  dest.replace_array(data);
704  }
705  }
706  catch(const invalid_variablename_exception&)
707  {
708  ERR_NG << "Cannot do [set_variables] with invalid destination variable: " << name << " with " << cfg.get_config().debug() << std::endl;
709  }
710 }
711 
712 /// Store the relative direction from one hex to another in a WML variable.
713 /// This is mainly useful as a diagnostic tool, but could be useful
714 /// for some kind of scenario.
715 WML_HANDLER_FUNCTION(store_relative_direction,, cfg)
716 {
717  if (!cfg.child("source")) {
718  WRN_NG << "No source in [store_relative_direction]" << std::endl;
719  return;
720  }
721  if (!cfg.child("destination")) {
722  WRN_NG << "No destination in [store_relative_direction]" << std::endl;
723  return;
724  }
725  if (!cfg.has_attribute("variable")) {
726  WRN_NG << "No variable in [store_relative_direction]" << std::endl;
727  return;
728  }
729 
730  const map_location src = cfg_to_loc(cfg.child("source"));
731  const map_location dst = cfg_to_loc(cfg.child("destination"));
732 
733  std::string variable = cfg["variable"];
734  map_location::RELATIVE_DIR_MODE mode = static_cast<map_location::RELATIVE_DIR_MODE> (cfg["mode"].to_int(0));
735  try
736  {
738 
740  }
741  catch(const invalid_variablename_exception&)
742  {
743  ERR_NG << "Cannot do [store_relative_direction] with invalid destination variable: " << variable << " with " << cfg.get_config().debug() << std::endl;
744  }
745 }
746 
747 /// Store the rotation of one hex around another in a WML variable.
748 /// In increments of 60 degrees, clockwise.
749 /// This is mainly useful as a diagnostic tool, but could be useful
750 /// for some kind of scenario.
751 WML_HANDLER_FUNCTION(store_rotate_map_location,, cfg)
752 {
753  if (!cfg.child("source")) {
754  WRN_NG << "No source in [store_rotate_map_location]" << std::endl;
755  return;
756  }
757  if (!cfg.child("destination")) {
758  WRN_NG << "No destination in [store_rotate_map_location]" << std::endl;
759  return;
760  }
761  if (!cfg.has_attribute("variable")) {
762  WRN_NG << "No variable in [store_rotate_map_location]" << std::endl;
763  return;
764  }
765 
766  const map_location src = cfg_to_loc(cfg.child("source"));
767  const map_location dst = cfg_to_loc(cfg.child("destination"));
768 
769  std::string variable = cfg["variable"];
770  int angle = cfg["angle"].to_int(1);
771 
772  try
773  {
775 
776  dst.rotate_right_around_center(src,angle).write(store.as_container());
777  }
778  catch(const invalid_variablename_exception&)
779  {
780  ERR_NG << "Cannot do [store_rotate_map_location] with invalid destination variable: " << variable << " with " << cfg.get_config().debug() << std::endl;
781  }
782 }
783 
784 
785 /// Store time of day config in a WML variable. This is useful for those who
786 /// are too lazy to calculate the corresponding time of day for a given turn,
787 /// or if the turn / time-of-day sequence mutates in a scenario.
788 WML_HANDLER_FUNCTION(store_time_of_day,, cfg)
789 {
790  const map_location loc = cfg_to_loc(cfg);
791  int turn = cfg["turn"];
792  // using 0 will use the current turn
793  const time_of_day& tod = resources::tod_manager->get_time_of_day(loc,turn);
794 
795  std::string variable = cfg["variable"];
796  if(variable.empty()) {
797  variable = "time_of_day";
798  }
799  try
800  {
802  tod.write(store.as_container());
803  }
804  catch(const invalid_variablename_exception&)
805  {
806  ERR_NG << "Found invalid variablename " << variable << " in [store_time_of_day] with " << cfg.get_config().debug() << "\n";
807  }
808 }
809 
810 /// Creating a mask of the terrain
812 {
813  map_location loc = cfg_to_loc(cfg, 1, 1);
814 
815  gamemap mask_map(resources::gameboard->map());
816 
817  bool border = cfg["border"].to_bool(true);
818 
819  try {
820  if(!cfg["mask_file"].empty()) {
821  const std::string& maskfile = filesystem::get_wml_location(cfg["mask_file"].str());
822 
823  if(filesystem::file_exists(maskfile)) {
824  mask_map.read(filesystem::read_file(maskfile), false, border);
825  } else {
826  throw incorrect_map_format_error("Invalid file path");
827  }
828  } else {
829  mask_map.read(cfg["mask"], false, border);
830  }
831  } catch(incorrect_map_format_error&) {
832  ERR_NG << "terrain mask is in the incorrect format, and couldn't be applied" << std::endl;
833  return;
834  } catch(twml_exception& e) {
835  e.show(resources::screen->video());
836  return;
837  }
838  resources::gameboard->overlay_map(mask_map, cfg.get_parsed_config(), loc, border);
840 }
841 
842 WML_HANDLER_FUNCTION(tunnel,, cfg)
843 {
844  const bool remove = cfg["remove"].to_bool(false);
845  if (remove) {
846  const std::vector<std::string> ids = utils::split(cfg["id"]);
847  for (const std::string &id : ids) {
849  }
850  } else if (cfg.get_children("source").empty() ||
851  cfg.get_children("target").empty() ||
852  cfg.get_children("filter").empty()) {
853  ERR_WML << "[tunnel] is missing a mandatory tag:\n"
854  << cfg.get_config().debug();
855  } else {
856  pathfind::teleport_group tunnel(cfg, false);
857  resources::tunnels->add(tunnel);
858 
859  if(cfg["bidirectional"].to_bool(true)) {
860  tunnel = pathfind::teleport_group(cfg, true);
861  resources::tunnels->add(tunnel);
862  }
863  }
864 }
865 
866 /// If we should spawn a new unit on the map somewhere
868 {
869  config parsed_cfg = cfg.get_parsed_config();
870 
871  config::attribute_value to_variable = cfg["to_variable"];
872  if (!to_variable.blank())
873  {
874  parsed_cfg.remove_attribute("to_variable");
875  unit new_unit(parsed_cfg, true);
876  try
877  {
878  config &var = resources::gamedata->get_variable_cfg(to_variable);
879  var.clear();
880  new_unit.write(var);
881  if (const config::attribute_value *v = parsed_cfg.get("x")) var["x"] = *v;
882  if (const config::attribute_value *v = parsed_cfg.get("y")) var["y"] = *v;
883  }
884  catch(const invalid_variablename_exception&)
885  {
886  ERR_NG << "Cannot do [unit] with invalid to_variable: " << to_variable << " with " << cfg.get_config().debug() << std::endl;
887  }
888  return;
889 
890  }
891 
892  int side = parsed_cfg["side"].to_int(1);
893 
894 
895  if ((side<1)||(side > static_cast<int>(resources::teams->size()))) {
896  ERR_NG << "wrong side in [unit] tag - no such side: "<<side<<" ( number of teams :"<<resources::teams->size()<<")"<<std::endl;
897  DBG_NG << parsed_cfg.debug();
898  return;
899  }
900  team &tm = resources::teams->at(side-1);
901 
902  unit_creator uc(tm,resources::gameboard->map().starting_position(side));
903 
904  uc
905  .allow_add_to_recall(true)
906  .allow_discover(true)
907  .allow_get_village(true)
908  .allow_invalidate(true)
909  .allow_rename_side(true)
910  .allow_show(true);
911 
912  uc.add_unit(parsed_cfg, &cfg);
913 
914 }
915 
916 WML_HANDLER_FUNCTION(volume,, cfg)
917 {
918 
919  int vol;
920  float rel;
921  std::string music = cfg["music"];
922  std::string sound = cfg["sound"];
923 
924  if(!music.empty()) {
926  rel = atof(music.c_str());
927  if (rel >= 0.0f && rel < 100.0f) {
928  vol = static_cast<int>(rel*vol/100.0f);
929  }
931  }
932 
933  if(!sound.empty()) {
935  rel = atof(sound.c_str());
936  if (rel >= 0.0f && rel < 100.0f) {
937  vol = static_cast<int>(rel*vol/100.0f);
938  }
940  }
941 
942 }
943 
944 WML_HANDLER_FUNCTION(on_undo, event_info, cfg)
945 {
946  if(cfg["delayed_variable_substitution"].to_bool(false)) {
947  synced_context::add_undo_commands(cfg.get_config(), event_info);
948  } else {
949  synced_context::add_undo_commands(cfg.get_parsed_config(), event_info);
950  }
951 }
952 
953 WML_HANDLER_FUNCTION(on_redo, event_info, cfg)
954 {
955  if(cfg["delayed_variable_substitution"].to_bool(false)) {
956  synced_context::add_redo_commands(cfg.get_config(), event_info);
957  } else {
958  synced_context::add_redo_commands(cfg.get_parsed_config(), event_info);
959  }
960 }
961 
962 } // end namespace game_events
963 
int total_width() const
Real width of the map, including borders.
Definition: map.hpp:114
void verify_and_get_global_variable(const vconfig &pcfg)
play_controller * controller
Definition: resources.cpp:21
static void add_undo_commands(const config &commands, const game_events::queued_event &ctx)
bool explicit_index() const
Doesn't throw.
std::map< std::string, handler > map
Definition: action_wml.hpp:52
unit_creator & allow_rename_side(bool b)
config get_user_choice(const std::string &name, const user_choice &uch, int side=0)
void remove_attribute(const std::string &key)
Definition: config.cpp:534
variable_info_detail::maybe_const< vit, config::attribute_value >::type & as_scalar() const
might throw invalid_variablename_exception NOTE: If vit == vit_const, then the lifime of the returned...
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator *calc, const size_t width, const size_t height, const teleport_map *teleports, bool border)
#define ERR_NG
Definition: action_wml.cpp:77
static void get_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:62
place_recruit_result place_recruit(unit_ptr u, const map_location &recruit_location, const map_location &recruited_from, int cost, bool is_recall, bool show, bool fire_event, bool full_movement, bool wml_triggered)
Definition: create.cpp:610
static thandler * handler
Definition: handler.cpp:60
::tod_manager * tod_manager
Definition: resources.cpp:31
PHASE phase() const
Definition: game_data.hpp:78
std::vector< unit_iterator > find_leaders(int side)
Definition: map.cpp:319
Function which doesn't take anything into account.
Definition: pathfind.hpp:255
#define LOG_NG
Definition: action_wml.cpp:75
variable_info_detail::maybe_const< vit, config::child_itors >::type as_array() const
might throw invalid_variablename_exception
Definition: unit.hpp:95
void merge_array(std::vector< config > childs) const
merges might throw invalid_variablename_exception
wml_action(const std::string &tag, handler function)
Using this constructor for a static object outside action_wml.cpp will likely lead to a static initia...
Definition: action_wml.cpp:215
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:566
Various functions implementing vision (through fog of war and shroud).
const GLfloat * c
Definition: glew.h:12741
unit_creator & allow_invalidate(bool b)
static void set_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:97
unit_creator & allow_show(bool b)
Various functions that implement attacks and attack calculations.
void remove(const std::string &id)
Definition: teleport.cpp:272
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
internal_ptr get_unit_ptr()
Get a copy of the internal unit pointer.
config & get_variable_cfg(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:87
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:243
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
static void raise_map_changed()
Notifies all observers of 'ai_map_changed' event.
Definition: manager.cpp:474
void place_on_fake_unit_manager(fake_unit_manager *d)
Place this on manager's fake_units_ dequeue.
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:56
game_display * screen
Definition: resources.cpp:27
Extends variable_info with methods that can only be applied if vit != vit_const.
#define LOG_WML
Definition: action_wml.cpp:84
map_location find_vacant_tile(const map_location &loc, VACANT_TILE_TYPE vacancy, const unit *pass_check, const team *shroud_check, const game_board *board)
Function that will find a location on the board that is as near to loc as possible, but which is unoccupied by any units.
Definition: pathfind.cpp:56
This file contains the window object, this object is a top level container which has the event manage...
config::child_itors replace_array(std::vector< config > childs) const
void show(CVideo &video)
Shows the error in a dialog.
unit_creator & allow_discover(bool b)
REMOVE_EMPTY : remove empty elements.
static void on_replay_error(const std::string &message, bool)
Definition: action_wml.cpp:263
WML_HANDLER_FUNCTION(clear_global_variable,, pcfg)
Experimental data persistence.
Definition: action_wml.cpp:257
void modify_turns_by_wml(const std::string &mod)
unit_type_data unit_types
Definition: types.cpp:1314
Define actions for the game's events mechanism.
static void add_redo_commands(const config &commands, const game_events::queued_event &ctx)
void overlay_map(const gamemap &o, const config &cfg, map_location loc, bool border)
Definition: game_board.cpp:294
Replay control code.
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:220
int side() const
Definition: unit.hpp:201
static lg::log_domain log_config("config")
void verify_and_set_global_variable(const vconfig &pcfg)
void write(config &cfg) const
Definition: unit.cpp:1379
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
Audio output for sound and music.
Definition: sound.cpp:43
GLenum src
Definition: glew.h:2392
void clear()
Definition: config.cpp:1055
std::string debug() const
Definition: config.cpp:1438
-file sdl_utils.hpp
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
GLenum mode
Definition: glew.h:2390
bool empty() const
Tests for an attribute that either was never set or was set to "".
Definition: config.cpp:375
unit_creator & allow_add_to_recall(bool b)
void new_turn()
Update lighting settings.
Variant for storing WML attributes.
Definition: config.hpp:223
std::string terrain_mask
Definition: game_config.cpp:84
game_data * gamedata
Definition: resources.cpp:22
int total_height() const
Real height of the map, including borders.
Definition: map.hpp:117
std::string filename_
Definition: action_wml.cpp:506
bool blank() const
Tests for an attribute that was never set.
Definition: config.cpp:367
#define WRN_NG
Definition: action_wml.cpp:76
STRIP_SPACES : strips leading and trailing blank spaces.
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:48
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
GLsizei const char ** path
Definition: glew.h:4654
#define ERR_WML
Definition: action_wml.cpp:86
std::vector< map_location > steps
Definition: pathfind.hpp:135
#define DBG_NG
Definition: action_wml.cpp:74
void set_number_of_turns_by_wml(int num)
std::vector< team > * teams
Definition: resources.cpp:29
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:132
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
Definition: filter.hpp:67
filter_context * filter_con
Definition: resources.cpp:23
variable_access_create get_variable_access_write(const std::string &varname)
returns a variable_access that can be used to change the game variables
Definition: game_data.hpp:55
const GLdouble * v
Definition: glew.h:1359
GLsizei const GLfloat * value
Definition: glew.h:1817
GLenum GLenum dst
Definition: glew.h:2392
int w() const
Effective map width.
Definition: map.hpp:105
game_board * gameboard
Definition: resources.cpp:20
Interface for querying local choices.
iterator begin()
begin iterator
Encapsulates the map of the game.
Definition: map.hpp:37
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:161
config & add_child(const std::string &key)
Definition: config.cpp:743
fake_unit_manager * fake_units
Definition: resources.cpp:32
Managing the AIs lifecycle - headers.
Function which only uses terrain, ignoring shroud, enemies, etc.
Definition: pathfind.hpp:241
iterator end()
end iterator
static const map_location & null_location()
Definition: location.hpp:195
void needs_rebuild(bool b)
Sets whether the screen (map visuals) needs to be rebuilt. This is typically after the map has been c...
GLfloat GLfloat p
Definition: glew.h:12766
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
void set_sound_volume(int vol)
Definition: sound.cpp:859
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
void read(const std::string &data, const bool allow_invalid=true, const int border_size=1)
Definition: map.cpp:154
GLuint GLuint GLsizei count
Definition: glew.h:1221
void set_music_volume(int vol)
Definition: sound.cpp:849
void set_turn_by_wml(const int num, game_data *vars=nullptr, const bool increase_limit_if_needed=true)
Dynamically change the current turn number.
Encapsulates the map of the game.
Definition: location.hpp:38
#define ERR_CF
Definition: action_wml.cpp:89
Various functions related to moving units.
Domain specific events.
Definition: action_wml.cpp:93
static map registry_
Tracks the known action handlers.
Definition: action_wml.hpp:65
GLenum GLenum variable
Definition: glew.h:10668
Various functions related to the creation of units (recruits, recalls, and placed units)...
Define conditionals for the game's events mechanism, a.k.a.
GLuint res
Definition: glew.h:9258
unit * get()
Get a raw pointer to the underlying unit.
variable_info_detail::maybe_const< vit, config >::type & as_container() const
might throw invalid_variablename_exception
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:262
int h() const
Effective map height.
Definition: map.hpp:108
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
unit_creator & allow_get_village(bool b)
GLuint index
Definition: glew.h:1782
bool empty() const
Definition: variable.hpp:93
int turn() const
Define the game's event mechanism.
Information on a WML variable.
size_t i
Definition: function.cpp:1057
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
variable_access_const get_variable_access_read(const std::string &varname) const
returns a variable_access that cannot be used to change the game variables
Definition: game_data.hpp:49
static lg::log_domain log_display("display")
Declarations for File-IO.
GLuint const GLchar * name
Definition: glew.h:1782
const attribute_value * get(const std::string &key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:935
virtual const gamemap & map() const
Definition: game_board.hpp:98
iterator erase(iterator it)
Erase an iterator to this object.
GLint GLint GLsizei GLsizei GLsizei GLint border
Definition: glew.h:1222
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:467
GLsizeiptr size
Definition: glew.h:1649
void add_unit(const config &cfg, const vconfig *vcfg=nullptr)
adds a unit on map without firing any events (so, usable during team construction in gamestatus) ...
int sound_volume()
void add(const teleport_group &group)
Definition: teleport.cpp:268
boost::optional< std::string > replace_map(const gamemap &r)
Definition: game_board.cpp:258
boost::intrusive_ptr< unit > unit_ptr
Definition: ptr.hpp:29
static bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
checks whether we are currently running in a synced context, and if not we enters it...
void reload_map()
Updates internals that cache map size.
Definition: display.cpp:487
A variable-expanding proxy for the config class.
Definition: variable.hpp:36
GLdouble angle
Definition: glew.h:6979
Standard logging facilities (interface).
config::child_itors insert_array(std::vector< config > childs) const
static lg::log_domain log_engine("engine")
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
int to_int(int def=0) const
Definition: config.cpp:308
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1155
boost::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:36
config::child_itors append_array(std::vector< config > childs) const
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
#define e
static void clear_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:91
std::vector< vconfig > child_list
Definition: variable.hpp:71
static lg::log_domain log_wml("wml")
void write(config &cfg) const
Definition: time_of_day.cpp:54
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
Helper class, don't construct this directly.
const GLuint * ids
Definition: glew.h:1652
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
This module contains various pathfinding functions and utilities.
static std::string write_direction(DIRECTION dir)
Definition: location.cpp:144
pathfind::manager * tunnels
Definition: resources.cpp:33
GLsizei const GLcharARB ** string
Definition: glew.h:4503
Holds a temporary unit that can be drawn on the map without being placed in the unit_map.
unit_map * units
Definition: resources.cpp:35
bool empty() const
Definition: tstring.hpp:166
Display units performing various actions: moving, attacking, and dying.
void move_unit(const std::vector< map_location > &path, unit_ptr u, bool animate, map_location::DIRECTION dir, bool force_scroll)
Display a unit moving along a given path.
Definition: udisplay.cpp:486
int music_volume()
void verify_and_clear_global_variable(const vconfig &pcfg)
int number_of_turns() const
GLclampf f
Definition: glew.h:3024