The Battle for Wesnoth  1.15.0+dev
game_lua_kernel.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
3  Part of the Battle for Wesnoth Project https://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  * Provides a Lua interpreter, to be embedded in WML.
18  *
19  * @note Naming conventions:
20  * - intf_ functions are exported in the wesnoth domain,
21  * - impl_ functions are hidden inside metatables,
22  * - cfun_ functions are closures,
23  * - luaW_ functions are helpers in Lua style.
24  */
25 
27 
28 #include "actions/attack.hpp" // for battle_context_unit_stats, etc
29 #include "actions/advancement.hpp" // for advance_unit_at, etc
30 #include "actions/move.hpp" // for clear_shroud
31 #include "actions/vision.hpp" // for clear_shroud
32 #include "ai/composite/ai.hpp" // for ai_composite
33 #include "ai/composite/component.hpp" // for component, etc
34 #include "ai/composite/contexts.hpp" // for ai_context
35 #include "ai/lua/engine_lua.hpp" // for engine_lua
36 #include "ai/composite/rca.hpp" // for candidate_action
37 #include "ai/composite/stage.hpp" // for stage
38 #include "ai/configuration.hpp" // for configuration
39 #include "ai/lua/core.hpp" // for lua_ai_context, etc
40 #include "ai/manager.hpp" // for manager, holder
41 #include "attack_prediction.hpp" // for combatant
42 #include "chat_events.hpp" // for chat_handler, etc
43 #include "config.hpp" // for config, etc
44 #include "display_chat_manager.hpp" // for clear_chat_messages
45 #include "formatter.hpp"
46 #include "game_board.hpp" // for game_board
47 #include "game_classification.hpp" // for game_classification, etc
48 #include "game_config.hpp" // for debug, base_income, etc
49 #include "game_config_manager.hpp" // for game_config_manager
50 #include "game_data.hpp" // for game_data, etc
51 #include "game_display.hpp" // for game_display
52 #include "game_errors.hpp" // for game_error
53 #include "game_events/conditional_wml.hpp" // for conditional_passed
55 #include "game_events/pump.hpp" // for queued_event
56 #include "preferences/game.hpp" // for encountered_units
57 #include "help/help.hpp"
58 #include "log.hpp" // for LOG_STREAM, logger, etc
59 #include "utils/make_enum.hpp" // for operator<<
60 #include "map/map.hpp" // for gamemap
61 #include "map/label.hpp"
62 #include "map/location.hpp" // for map_location
63 #include "mouse_events.hpp" // for mouse_handler
64 #include "mp_game_settings.hpp" // for mp_game_settings
65 #include "pathfind/pathfind.hpp" // for full_cost_map, plain_route, etc
66 #include "pathfind/teleport.hpp" // for get_teleport_locations, etc
67 #include "play_controller.hpp" // for play_controller
68 #include "recall_list_manager.hpp" // for recall_list_manager
69 #include "replay.hpp" // for get_user_choice, etc
70 #include "reports.hpp" // for register_generator, etc
71 #include "resources.hpp" // for whiteboard
72 #include "scripting/lua_audio.hpp"
73 #include "scripting/lua_unit.hpp"
75 #include "scripting/lua_common.hpp"
77 #include "scripting/lua_gui2.hpp" // for show_gamestate_inspector
79 #include "scripting/lua_race.hpp"
80 #include "scripting/lua_team.hpp"
82 #include "scripting/push_check.hpp"
83 #include "synced_commands.hpp"
84 #include "color.hpp" // for surface
85 #include "sdl/surface.hpp" // for surface
86 #include "side_filter.hpp" // for side_filter
87 #include "sound.hpp" // for commit_music_changes, etc
88 #include "soundsource.hpp"
89 #include "synced_context.hpp" // for synced_context, etc
90 #include "synced_user_choice.hpp"
91 #include "team.hpp" // for team, village_owner
92 #include "terrain/terrain.hpp" // for terrain_type
93 #include "terrain/filter.hpp" // for terrain_filter
94 #include "terrain/translation.hpp" // for read_terrain_code, etc
95 #include "terrain/type_data.hpp"
96 #include "time_of_day.hpp" // for time_of_day
97 #include "tod_manager.hpp" // for tod_manager
98 #include "tstring.hpp" // for t_string, operator+
99 #include "units/unit.hpp" // for unit
100 #include "units/animation_component.hpp" // for unit_animation_component
101 #include "units/udisplay.hpp"
102 #include "units/filter.hpp"
103 #include "units/map.hpp" // for unit_map, etc
104 #include "units/ptr.hpp" // for unit_const_ptr, unit_ptr
105 #include "units/types.hpp" // for unit_type_data, unit_types, etc
106 #include "variable.hpp" // for vconfig, etc
107 #include "variable_info.hpp"
108 #include "whiteboard/manager.hpp" // for whiteboard
109 #include "wml_exception.hpp"
110 #include "deprecation.hpp"
111 
112 #include "utils/functional.hpp" // for bind_t, bind
113 #include <boost/range/algorithm/copy.hpp> // boost::copy
114 #include <boost/range/adaptors.hpp> // boost::adaptors::filtered
115 #include <cassert> // for assert
116 #include <cstring> // for strcmp
117 #include <iterator> // for distance, advance
118 #include <map> // for map, map<>::value_type, etc
119 #include <new> // for operator new
120 #include <set> // for set
121 #include <sstream> // for operator<<, basic_ostream, etc
122 #include <utility> // for pair
123 #include <algorithm>
124 #include <vector> // for vector, etc
125 #include <SDL2/SDL_timer.h> // for SDL_GetTicks
126 #include "lua/lauxlib.h" // for luaL_checkinteger, etc
127 #include "lua/lua.h" // for lua_setfield, etc
128 
129 class CVideo;
130 
131 #ifdef DEBUG_LUA
132 #include "scripting/debug_lua.hpp"
133 #endif
134 
135 static lg::log_domain log_scripting_lua("scripting/lua");
136 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
137 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
138 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
139 
140 std::vector<config> game_lua_kernel::preload_scripts;
142 
143 // Template which allows to push member functions to the lua kernel base into lua as C functions, using a shim
145 
146 template <member_callback method>
148  return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L);
149 }
150 
151 // Pass a const bool also...
152 typedef int (game_lua_kernel::*member_callback2)(lua_State *, bool);
153 
154 template <member_callback2 method, bool b>
156  return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L, b);
157 }
158 
160 {
161  map_locker(game_lua_kernel* kernel) : kernel_(kernel)
162  {
163  ++kernel_->map_locked_;
164  }
166  {
167  --kernel_->map_locked_;
168  }
170 };
171 
172 
174 {
175  game_lua_kernel::preload_scripts.clear();
176  for (const config& cfg : game_config.child_range("lua")) {
177  game_lua_kernel::preload_scripts.push_back(cfg);
178  }
179  game_lua_kernel::preload_config = game_config.child("game_config");
180 }
181 
182 void game_lua_kernel::log_error(char const * msg, char const * context)
183 {
184  lua_kernel_base::log_error(msg, context);
185  lua_chat(context, msg);
186 }
187 
188 void game_lua_kernel::lua_chat(const std::string& caption, const std::string& msg)
189 {
190  if (game_display_) {
191  game_display_->get_chat_manager().add_chat_message(std::time(nullptr), caption, 0, msg,
193  }
194 }
195 
196 /**
197  * Gets a vector of sides from side= attribute in a given config node.
198  * Promotes consistent behavior.
199  */
200 std::vector<int> game_lua_kernel::get_sides_vector(const vconfig& cfg)
201 {
202  const config::attribute_value sides = cfg["side"];
203  const vconfig &ssf = cfg.child("filter_side");
204 
205  if (!ssf.null()) {
206  if(!sides.empty()) { WRN_LUA << "ignoring duplicate side filter information (inline side=)" << std::endl; }
207  side_filter filter(ssf, &game_state_);
208  return filter.get_teams();
209  }
210 
211  side_filter filter(sides.str(), &game_state_);
212  return filter.get_teams();
213 }
214 
215 
216 
218 {
219  lua_pushnumber(L, lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().size());
220  return 1;
221 }
222 
224 {
225  const t_translation::starting_positions::left_map& left = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().left;
226 
227  t_translation::starting_positions::left_const_iterator it;
228  if (lua_isnoneornil(L, 2)) {
229  it = left.begin();
230  }
231  else {
232  it = left.find(luaL_checkstring(L, 2));
233  if (it == left.end()) {
234  return 0;
235  }
236  ++it;
237  }
238  if (it == left.end()) {
239  return 0;
240  }
241  lua_pushstring(L, it->first.c_str());
242  luaW_pushlocation(L, it->second);
243  return 2;
244 }
245 
247 {
249  lua_pushvalue(L, -2);
250  lua_pushnil(L);
251  return 3;
252 }
253 
255 {
256  const t_translation::starting_positions::left_map& left = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().left;
257  auto it = left.find(luaL_checkstring(L, 2));
258  if (it == left.end()) {
259  return 0;
260  }
261  else {
262  luaW_pushlocation(L, it->second);
263  return 1;
264  }
265 }
266 
268 {
269  lua_pushstring(L, "special locations cannot be modified using wesnoth.special_locations");
270  return lua_error(L);
271 }
272 
274 {
275  lua_newtable(L); // The functor table
276  lua_newtable(L); // The functor metatable
277  lua_pushstring(L, "__len");
279  lua_rawset(L, -3);
280  lua_pushstring(L, "__index");
282  lua_rawset(L, -3);
283  lua_pushstring(L, "__newindex");
285  lua_rawset(L, -3);
286  lua_pushstring(L, "__pairs");
288  lua_rawset(L, -3);
289  lua_setmetatable(L, -2); // Apply the metatable to the functor table
290 }
291 
292 namespace {
293  /**
294  * Temporary entry to a queued_event stack
295  */
296  struct queued_event_context
297  {
298  typedef game_events::queued_event qe;
299  std::stack<qe const *> & stack_;
300 
301  queued_event_context(qe const *new_qe, std::stack<qe const*> & stack)
302  : stack_(stack)
303  {
304  stack_.push(new_qe);
305  }
306 
307  ~queued_event_context()
308  {
309  stack_.pop();
310  }
311  };
312 }//unnamed namespace for queued_event_context
313 
314 /**
315  * Gets currently viewing side.
316  * - Ret 1: integer specifying the currently viewing side
317  * - Ret 2: Bool whether the vision is not limited to that team, this can for example be true during replays.
318  */
320 {
321  if(const display* disp = display::get_singleton()) {
322  lua_pushinteger(L, disp->viewing_side());
323  lua_pushboolean(L, disp->show_everything());
324  return 2;
325  }
326  else {
327  return 0;
328  }
329 }
330 
331 static const char animatorKey[] = "unit animator";
332 
334  unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
335  anim.~unit_animator();
336  return 0;
337 }
338 
340 {
341  unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
342  unit& u = luaW_checkunit(L, 2);
343  std::string which = luaL_checkstring(L, 3);
344 
345  using hit_type = unit_animation::hit_type;
346  std::string hits_str = luaL_checkstring(L, 4);
347  hit_type hits = hit_type::string_to_enum(hits_str, hit_type::INVALID);
348 
349  map_location dest;
350  int v1 = 0, v2 = 0;
351  bool bars = false;
352  t_string text;
353  color_t color{255, 255, 255};
354  const_attack_ptr primary, secondary;
355 
356  if(lua_istable(L, 5)) {
357  lua_getfield(L, 5, "target");
358  if(luaW_tolocation(L, -1, dest)) {
359  if(dest == u.get_location()) {
360  return luaL_argerror(L, 5, "target location must be different from animated unit's location");
361  } else if(!tiles_adjacent(dest, u.get_location())) {
362  return luaL_argerror(L, 5, "target location must be adjacent to the animated unit");
363  }
364  } else {
365  // luaW_tolocation may set the location to (0,0) if it fails
366  dest = map_location();
367  if(!lua_isnoneornil(L, -1)) {
368  return luaW_type_error(L, 5, "target", "location table");
369  }
370  }
371  lua_pop(L, 1);
372 
373  lua_getfield(L, 5, "value");
374  if(lua_isnumber(L, -1)) {
375  v1 = lua_tonumber(L, -1);
376  } else if(lua_istable(L, -1)) {
377  lua_rawgeti(L, -1, 1);
378  v1 = lua_tonumber(L, -1);
379  lua_pop(L, 1);
380  lua_rawgeti(L, -1, 2);
381  v2 = lua_tonumber(L, -1);
382  lua_pop(L, 1);
383  } else if(!lua_isnoneornil(L, -1)) {
384  return luaW_type_error(L, 5, "value", "number or array of two numbers");
385  }
386  lua_pop(L, 1);
387 
388  lua_getfield(L, 5, "with_bars");
389  if(lua_isboolean(L, -1)) {
390  bars = luaW_toboolean(L, -1);
391  } else if(!lua_isnoneornil(L, -1)) {
392  return luaW_type_error(L, 5, "with_bars", lua_typename(L, LUA_TBOOLEAN));
393  }
394  lua_pop(L, 1);
395 
396  lua_getfield(L, 5, "text");
397  if(lua_isstring(L, -1)) {
398  text = lua_tostring(L, -1);
399  } else if(luaW_totstring(L, -1, text)) {
400  // Do nothing; luaW_totstring already assigned the value
401  } else if(!lua_isnoneornil(L, -1)) {
402  return luaW_type_error(L, 5, "text", lua_typename(L, LUA_TSTRING));
403  }
404  lua_pop(L, 1);
405 
406  lua_getfield(L, 5, "color");
407  if(lua_istable(L, -1) && lua_rawlen(L, -1) == 3) {
408  int idx = lua_absindex(L, -1);
409  lua_rawgeti(L, idx, 1); // red @ -3
410  lua_rawgeti(L, idx, 2); // green @ -2
411  lua_rawgeti(L, idx, 3); // blue @ -1
412  color = color_t(lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1));
413  lua_pop(L, 3);
414  } else if(!lua_isnoneornil(L, -1)) {
415  return luaW_type_error(L, 5, "color", "array of three numbers");
416  }
417  lua_pop(L, 1);
418 
419  lua_getfield(L, 5, "primary");
420  primary = luaW_toweapon(L, -1);
421  if(!primary && !lua_isnoneornil(L, -1)) {
422  return luaW_type_error(L, 5, "primary", "weapon");
423  }
424  lua_pop(L, 1);
425 
426  lua_getfield(L, 5, "secondary");
427  secondary = luaW_toweapon(L, -1);
428  if(!secondary && !lua_isnoneornil(L, -1)) {
429  return luaW_type_error(L, 5, "secondary", "weapon");
430  }
431  lua_pop(L, 1);
432  } else if(!lua_isnoneornil(L, 5)) {
433  return luaW_type_error(L, 5, "table of options");
434  }
435 
436  anim.add_animation(&u, which, u.get_location(), dest, v1, bars, text, color, hits, primary, secondary, v2);
437  return 0;
438 }
439 
441 {
443  if(v.update_locked() || v.faked()) {
444  return 0;
445  }
446  events::command_disabler command_disabler;
447  unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
448  play_controller_.play_slice(false);
449  anim.start_animations();
450  anim.wait_for_end();
451  anim.set_all_standing();
452  anim.clear();
453  return 0;
454 }
455 
457 {
458  unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
459  anim.clear();
460  return 0;
461 }
462 
464 {
465  const char* m = lua_tostring(L, 2);
466  return luaW_getmetafield(L, 1, m);
467 }
468 
470 {
471  new(L) unit_animator;
473  luaL_Reg metafuncs[] {
474  {"__gc", impl_animator_collect},
475  {"__index", impl_animator_get},
476  {"add", impl_add_animation},
477  {"run", &dispatch<&game_lua_kernel::impl_run_animation>},
478  {"clear", impl_clear_animation},
479  {nullptr, nullptr},
480  };
481  luaL_setfuncs(L, metafuncs, 0);
482  lua_pushstring(L, "__metatable");
483  lua_setfield(L, -2, animatorKey);
484  }
485  lua_setmetatable(L, -2);
486  return 1;
487 }
488 
490 {
491  if (game_display_) {
492  return lua_gui2::show_gamestate_inspector(luaW_checkvconfig(L, 1), gamedata(), game_state_);
493  }
494  return 0;
495 }
496 
497 /**
498  * Gets the unit at the given location or with the given id.
499  * - Arg 1: location
500  * OR
501  * - Arg 1: string ID
502  * - Ret 1: full userdata with __index pointing to impl_unit_get and
503  * __newindex pointing to impl_unit_set.
504  */
506 {
507  map_location loc;
508  if(lua_isstring(L, 1) && !lua_isnumber(L, 1)) {
509  std::string id = luaL_checkstring(L, 1);
510  for(const unit& u : units()) {
511  if(u.id() == id) {
512  luaW_pushunit(L, u.underlying_id());
513  return 1;
514  }
515  }
516  return 0;
517  }
518  if(!luaW_tolocation(L, 1, loc)) {
519  return luaL_argerror(L, 1, "expected string or location");
520  }
521  unit_map::const_iterator ui = units().find(loc);
522 
523  if (!ui.valid()) return 0;
524 
525  luaW_pushunit(L, ui->underlying_id());
526  return 1;
527 }
528 
529 /**
530  * Gets the unit displayed in the sidebar.
531  * - Ret 1: full userdata with __index pointing to impl_unit_get and
532  * __newindex pointing to impl_unit_set.
533  */
535 {
536  if (!game_display_) {
537  return 0;
538  }
539 
540  unit_map::const_iterator ui = board().find_visible_unit(
541  game_display_->displayed_unit_hex(),
542  teams()[game_display_->viewing_team()],
543  game_display_->show_everything());
544  if (!ui.valid()) return 0;
545 
546  luaW_pushunit(L, ui->underlying_id());
547  return 1;
548 }
549 
550 /**
551  * Gets all the units matching a given filter.
552  * - Arg 1: optional table containing a filter
553  * - Arg 2: optional location (to find all units that would match on that location)
554  OR unit (to find all units that would match adjacent to that unit)
555  * - Ret 1: table containing full userdata with __index pointing to
556  * impl_unit_get and __newindex pointing to impl_unit_set.
557  */
559 {
560  vconfig filter = luaW_checkvconfig(L, 1, true);
561 
562  unit_filter filt(filter);
563  std::vector<const unit*> units;
564 
565  if(unit* u_adj = luaW_tounit(L, 2)) {
566  if(!u_adj) {
567  return luaL_argerror(L, 2, "unit not found");
568  }
569  units = filt.all_matches_with_unit(*u_adj);
570  } else if(!lua_isnoneornil(L, 2)) {
571  map_location loc;
572  luaW_tolocation(L, 2, loc);
573  if(!loc.valid()) {
574  return luaL_argerror(L, 2, "invalid location");
575  }
576  units = filt.all_matches_at(loc);
577  } else {
578  units = filt.all_matches_on_map();
579  }
580 
581  // Go through all the units while keeping the following stack:
582  // 1: return table, 2: userdata
583  lua_settop(L, 0);
584  lua_newtable(L);
585  int i = 1;
586 
587  for (const unit * ui : units) {
588  luaW_pushunit(L, ui->underlying_id());
589  lua_rawseti(L, 1, i);
590  ++i;
591  }
592  return 1;
593 }
594 
595 /**
596  * Matches a unit against the given filter.
597  * - Arg 1: full userdata.
598  * - Arg 2: table containing a filter
599  * - Arg 3: optional location OR optional "adjacent" unit
600  * - Ret 1: boolean.
601  */
603 {
604  lua_unit& u = *luaW_checkunit_ref(L, 1);
605 
606  vconfig filter = luaW_checkvconfig(L, 2, true);
607 
608  if (filter.null()) {
609  lua_pushboolean(L, true);
610  return 1;
611  }
612 
613  if(unit* u_adj = luaW_tounit(L, 3)) {
614  if(int side = u.on_recall_list()) {
615  WRN_LUA << "wesnoth.match_unit called with a secondary unit (3rd argument), ";
616  WRN_LUA << "but unit to match was on recall list. ";
617  WRN_LUA << "Thus the 3rd argument is ignored.\n";
618  team &t = board().get_team(side);
619  scoped_recall_unit auto_store("this_unit", t.save_id_or_number(), t.recall_list().find_index(u->id()));
620  lua_pushboolean(L, unit_filter(filter).matches(*u, map_location()));
621  return 1;
622  }
623  if (!u_adj) {
624  return luaL_argerror(L, 3, "unit not found");
625  }
626  lua_pushboolean(L, unit_filter(filter).matches(*u, *u_adj));
627  } else if(int side = u.on_recall_list()) {
628  map_location loc;
629  luaW_tolocation(L, 3, loc); // If argument 3 isn't a location, loc is unchanged
630  team &t = board().get_team(side);
631  scoped_recall_unit auto_store("this_unit", t.save_id_or_number(), t.recall_list().find_index(u->id()));
632  lua_pushboolean(L, unit_filter(filter).matches(*u, loc));
633  return 1;
634  } else {
635  map_location loc = u->get_location();
636  luaW_tolocation(L, 3, loc); // If argument 3 isn't a location, loc is unchanged
637  lua_pushboolean(L, unit_filter(filter).matches(*u, loc));
638  }
639  return 1;
640 }
641 
642 /**
643  * Gets the numeric ids of all the units matching a given filter on the recall lists.
644  * - Arg 1: optional table containing a filter
645  * - Ret 1: table containing full userdata with __index pointing to
646  * impl_unit_get and __newindex pointing to impl_unit_set.
647  */
649 {
650  vconfig filter = luaW_checkvconfig(L, 1, true);
651 
652  // Go through all the units while keeping the following stack:
653  // 1: return table, 2: userdata
654  lua_settop(L, 0);
655  lua_newtable(L);
656  int i = 1, s = 1;
657  const unit_filter ufilt(filter);
658  for (team &t : teams())
659  {
660  for (unit_ptr & u : t.recall_list())
661  {
662  if (!filter.null()) {
663  scoped_recall_unit auto_store("this_unit",
664  t.save_id_or_number(), t.recall_list().find_index(u->id()));
665  if (!ufilt( *u, map_location() ))
666  continue;
667  }
668  luaW_pushunit(L, s, u->underlying_id());
669  lua_rawseti(L, 1, i);
670  ++i;
671  }
672  ++s;
673  }
674  return 1;
675 }
676 
677 /**
678  * Fires an event.
679  * - Arg 1: string containing the event name or id.
680  * - Arg 2: optional first location.
681  * - Arg 3: optional second location.
682  * - Arg 4: optional WML table used as the [weapon] tag.
683  * - Arg 5: optional WML table used as the [second_weapon] tag.
684  * - Ret 1: boolean indicating whether the event was processed or not.
685  */
687 {
688  char const *m = luaL_checkstring(L, 1);
689 
690  int pos = 2;
691  map_location l1, l2;
692  config data;
693 
694  if (luaW_tolocation(L, 2, l1)) {
695  if (luaW_tolocation(L, 3, l2)) {
696  pos = 4;
697  } else {
698  pos = 3;
699  }
700  }
701 
702  if (!lua_isnoneornil(L, pos)) {
703  data.add_child("first", luaW_checkconfig(L, pos));
704  }
705  ++pos;
706  if (!lua_isnoneornil(L, pos)) {
707  data.add_child("second", luaW_checkconfig(L, pos));
708  }
709 
710  bool b = false;
711 
712  if (by_id) {
713  b = std::get<0>(play_controller_.pump().fire("", m, l1, l2, data));
714  }
715  else {
716  b = std::get<0>(play_controller_.pump().fire(m, l1, l2, data));
717  }
718  lua_pushboolean(L, b);
719  return 1;
720 }
721 
722 
723 /**
724  * Fires a wml menu item.
725  * - Arg 1: id of the item. it is not possible to fire items that don't have ids with this function.
726  * - Arg 2: optional first location.
727  * - Ret 1: boolean, true indicating that the event was fired successfully
728  *
729  * NOTE: This is not an "official" feature, it may currently cause assertion failures if used with
730  * menu items which have "needs_select". It is not supported right now to use it this way.
731  * The purpose of this function right now is to make it possible to have automated sanity tests for
732  * the wml menu items system.
733  */
735 {
736  char const *m = luaL_checkstring(L, 1);
737 
738  map_location l1 = luaW_checklocation(L, 2);
739 
740  bool b = game_state_.get_wml_menu_items().fire_item(m, l1, gamedata(), game_state_, units());
741  lua_pushboolean(L, b);
742  return 1;
743 }
744 
745 /**
746  * Gets a WML variable.
747  * - Arg 1: string containing the variable name.
748  * - Arg 2: optional bool indicating if tables for containers should be left empty.
749  * - Ret 1: value of the variable, if any.
750  */
752 {
753  char const *m = luaL_checkstring(L, 1);
755  return luaW_pushvariable(L, v) ? 1 : 0;
756 }
757 
758 /**
759  * Gets a side specific WML variable.
760  * - Arg 1: integer side number.
761  * - Arg 2: string containing the variable name.
762  * - Ret 1: value of the variable, if any.
763  */
765 {
766 
767  unsigned side_index = luaL_checkinteger(L, 1) - 1;
768  if(side_index >= teams().size()) {
769  return luaL_argerror(L, 1, "invalid side number");
770  }
771  char const *m = luaL_checkstring(L, 2);
772  variable_access_const v(m, teams()[side_index].variables());
773  return luaW_pushvariable(L, v) ? 1 : 0;
774 }
775 
776 /**
777  * Gets a side specific WML variable.
778  * - Arg 1: integer side number.
779  * - Arg 2: string containing the variable name.
780  * - Arg 3: boolean/integer/string/table containing the value.
781  */
783 {
784  unsigned side = luaL_checkinteger(L, 1);
785  if(side > teams().size() || side == 0) {
786  return luaL_argerror(L, 1, "invalid side number");
787  }
788  char const *m = luaL_checkstring(L, 2);
789  config& vars = game_state_.board_.get_team(side).variables();
790  if(lua_isnoneornil(L, 3)) {
791  try {
792  variable_access_throw(m, vars).clear(false);
793  } catch(const invalid_variablename_exception&) {
794  }
795  return 0;
796  }
797  variable_access_create v(m, vars);
798  luaW_checkvariable(L, v, 3);
799  return 0;
800 }
801 
802 /**
803  * Sets a WML variable.
804  * - Arg 1: string containing the variable name.
805  * - Arg 2: boolean/integer/string/table containing the value.
806  */
808 {
809  const std::string m = luaL_checkstring(L, 1);
810  if(m.empty()) return luaL_argerror(L, 1, "empty variable name");
811  if (lua_isnoneornil(L, 2)) {
813  return 0;
814  }
816  luaW_checkvariable(L, v, 2);
817  return 0;
818 }
819 
821 {
822  game_state_.get_wml_menu_items().set_item(luaL_checkstring(L, 1), luaW_checkvconfig(L,2));
823  return 0;
824 }
825 
827 {
828  std::string ids(luaL_checkstring(L, 1));
829  for(const std::string& id : utils::split(ids, ',', utils::STRIP_SPACES)) {
830  if(id.empty()) {
831  WRN_LUA << "[clear_menu_item] has been given an empty id=, ignoring" << std::endl;
832  continue;
833  }
834  game_state_.get_wml_menu_items().erase(id);
835  }
836  return 0;
837 }
838 
840 {
841  game_classification &classification = play_controller_.get_classification();
842  classification.end_credits = luaW_toboolean(L, 1);
843  return 0;
844 }
845 
847 {
848  game_classification &classification = play_controller_.get_classification();
849  classification.end_text = luaW_checktstring(L, 1);
850  if (lua_isnumber(L, 2)) {
851  classification.end_text_duration = static_cast<int> (lua_tonumber(L, 2));
852  }
853 
854  return 0;
855 }
856 
858 {
859  deprecated_message("wesnoth.set_next_scenario", DEP_LEVEL::INDEFINITE, "");
861  return 0;
862 }
863 
864 int game_lua_kernel::intf_shroud_op(lua_State *L, bool place_shroud)
865 {
866 
867  int side_num = luaL_checkinteger(L, 1);
868 
869  if(lua_isstring(L, 2)) {
870  std::string data = lua_tostring(L, 2);
871  // Special case - using a shroud_data string, or "all"
872  team& side = board().get_team(side_num);
873  if(place_shroud) {
874  side.reshroud();
875  }
876  if(data != "all") {
877  side.merge_shroud_map_data(data);
878  } else if(!place_shroud) {
879  bool was_shrouded = side.uses_shroud();
880  side.set_shroud(false);
881  actions::clear_shroud(side.side());
882  side.set_shroud(was_shrouded);
883  }
884  return 0;
885  } else if(lua_istable(L, 2)) {
886  std::vector<map_location> locs_v = lua_check<std::vector<map_location>>(L, 2);
887  std::set<map_location> locs(locs_v.begin(), locs_v.end());
888  team &t = board().get_team(side_num);
889 
890  for (const map_location& loc : locs)
891  {
892  if (place_shroud) {
893  t.place_shroud(loc);
894  } else {
895  t.clear_shroud(loc);
896  }
897  }
898  } else {
899  return luaL_argerror(L, 2, "expected list of locations or shroud data string");
900  }
901 
902  game_display_->labels().recalculate_shroud();
903  game_display_->recalculate_minimap();
904  game_display_->invalidate_all();
905 
906  return 0;
907 }
908 
909 
910 /**
911  * Highlights the given location on the map.
912  * - Arg 1: location.
913  */
915 {
916  if (!game_display_) {
917  return 0;
918  }
919 
920  const map_location loc = luaW_checklocation(L, 1);
921  if(!map().on_board(loc)) return luaL_argerror(L, 1, "not on board");
922  game_display_->highlight_hex(loc);
923  game_display_->display_unit_hex(loc);
924 
925  return 0;
926 }
927 
928 /**
929  * Returns whether the first side is an enemy of the second one.
930  * - Args 1,2: side numbers.
931  * - Ret 1: boolean.
932  */
934 {
935  unsigned side_1 = luaL_checkinteger(L, 1) - 1;
936  unsigned side_2 = luaL_checkinteger(L, 2) - 1;
937  if (side_1 >= teams().size() || side_2 >= teams().size()) return 0;
938  lua_pushboolean(L, teams()[side_1].is_enemy(side_2 + 1));
939  return 1;
940 }
941 
942 /**
943  * Gets whether gamemap scrolling is disabled for the user.
944  * - Ret 1: boolean.
945  */
947 {
948  if (!game_display_) {
949  return 0;
950  }
951 
952  lua_pushboolean(L, game_display_->view_locked());
953  return 1;
954 }
955 
956 /**
957  * Sets whether gamemap scrolling is disabled for the user.
958  * - Arg 1: boolean, specifying the new locked/unlocked status.
959  */
961 {
962  bool lock = luaW_toboolean(L, 1);
963  if (game_display_) {
964  game_display_->set_view_locked(lock);
965  }
966  return 0;
967 }
968 
969 /**
970  * Gets a terrain code.
971  * - Arg 1: map location.
972  * - Ret 1: string.
973  */
975 {
976  map_location loc = luaW_checklocation(L, 1);
977 
978  const t_translation::terrain_code& t = board().map().
979  get_terrain(loc);
981  return 1;
982 }
983 
984 /**
985  * Sets a terrain code.
986  * - Arg 1: map location.
987  * - Arg 2: terrain code string.
988  * - Arg 3: layer: (overlay|base|both, default=both)
989  * - Arg 4: replace_if_failed, default = no
990  */
992 {
993  map_location loc = luaW_checklocation(L, 1);
994  std::string t_str(luaL_checkstring(L, 2));
995 
996  std::string mode_str = "both";
997  bool replace_if_failed = false;
998  if (!lua_isnone(L, 3)) {
999  if (!lua_isnil(L, 3)) {
1000  mode_str = luaL_checkstring(L, 3);
1001  }
1002 
1003  if(!lua_isnoneornil(L, 4)) {
1004  replace_if_failed = luaW_toboolean(L, 4);
1005  }
1006  }
1007 
1008  bool result = board().change_terrain(loc, t_str, mode_str, replace_if_failed);
1009 
1010  if (game_display_) {
1011  game_display_->needs_rebuild(result);
1012  }
1013 
1014  return 0;
1015 }
1016 
1017 /**
1018  * Reaplces part of the map.
1019  * - Arg 1: map location.
1020  * - Arg 2: map data string.
1021  * - Arg 3: table for optional named arguments
1022  * - is_odd: boolen, if Arg2 has the odd mapo format (as if it was cut from a odd map location)
1023  * - ignore_special_locations: boolean
1024  * - rules: table of tables
1025 */
1027 {
1028  map_location loc = luaW_checklocation(L, 1);
1029  std::string t_str(luaL_checkstring(L, 2));
1030  bool is_odd = false;
1031  bool ignore_special_locations = false;
1032  std::vector<gamemap::overlay_rule> rules;
1033 
1034  if(lua_istable(L, 3)) {
1035  if(luaW_tableget(L, 3, "is_odd")) {
1036  is_odd = luaW_toboolean(L, -1);
1037  lua_pop(L, 1);
1038  }
1039  if(luaW_tableget(L, 3, "ignore_special_locations")) {
1040  ignore_special_locations = luaW_toboolean(L, -1);
1041  lua_pop(L, 1);
1042  }
1043  if(luaW_tableget(L, 3, "rules")) {
1044  //todo: reduce code dublication by using read_rules_vector.
1045  if(!lua_istable(L, -1)) {
1046  return luaL_argerror(L, 3, "rules must be a table");
1047  }
1048 
1049  for (int i = 1, i_end = lua_rawlen(L, -1); i <= i_end; ++i)
1050  {
1051  lua_rawgeti(L, -1, i);
1052  if(!lua_istable(L, -1)) {
1053  return luaL_argerror(L, 3, "rules must be a table of tables");
1054  }
1055  rules.push_back(gamemap::overlay_rule());
1056  auto& rule = rules.back();
1057  if(luaW_tableget(L, -1, "old")) {
1058  rule.old_ = t_translation::read_list(luaW_tostring(L, -1));
1059  lua_pop(L, 1);
1060  }
1061 
1062  if(luaW_tableget(L, -1, "new")) {
1063  rule.new_ = t_translation::read_list(luaW_tostring(L, -1));
1064  lua_pop(L, 1);
1065  }
1066 
1067  if(luaW_tableget(L, -1, "mode")) {
1068  auto str = luaW_tostring(L, -1);
1069  rule.mode_ = str == "base" ? terrain_type_data::BASE : (str == "overlay" ? terrain_type_data::OVERLAY : terrain_type_data::BOTH);
1070  lua_pop(L, 1);
1071  }
1072 
1073  if(luaW_tableget(L, -1, "terrain")) {
1075  if(!terrain.empty()) {
1076  rule.terrain_ = terrain[0];
1077  }
1078  lua_pop(L, 1);
1079  }
1080 
1081  if(luaW_tableget(L, -1, "use_old")) {
1082  rule.use_old_ = luaW_toboolean(L, -1);
1083  lua_pop(L, 1);
1084  }
1085 
1086  if(luaW_tableget(L, -1, "replace_if_failed")) {
1087  rule.replace_if_failed_ = luaW_toboolean(L, -1);
1088  lua_pop(L, 1);
1089  }
1090 
1091  lua_pop(L, 1);
1092  }
1093  lua_pop(L, 1);
1094  }
1095  }
1096 
1097 
1098  gamemap mask_map(board().map().tdata(), "");
1099  mask_map.read(t_str, false);
1100  board().map_->overlay(mask_map, loc, rules, is_odd, ignore_special_locations);
1101 
1102  for(team& t : board().teams()) {
1103  t.fix_villages(board().map());
1104  }
1105 
1106  if (game_display_) {
1107  game_display_->needs_rebuild(true);
1108  }
1109 
1110  return 0;
1111 }
1112 
1113 /**
1114  * Gets details about a terrain.
1115  * - Arg 1: terrain code string.
1116  * - Ret 1: table.
1117  */
1119 {
1120  char const *m = luaL_checkstring(L, 1);
1122  if (t == t_translation::NONE_TERRAIN) return 0;
1123  const terrain_type& info = board().map().tdata()->get_terrain_info(t);
1124 
1125  lua_newtable(L);
1126  lua_pushstring(L, info.id().c_str());
1127  lua_setfield(L, -2, "id");
1128  luaW_pushtstring(L, info.name());
1129  lua_setfield(L, -2, "name");
1130  luaW_pushtstring(L, info.editor_name());
1131  lua_setfield(L, -2, "editor_name");
1132  luaW_pushtstring(L, info.description());
1133  lua_setfield(L, -2, "description");
1134  lua_push(L, info.icon_image());
1135  lua_setfield(L, -2, "icon");
1136  lua_push(L, info.editor_image());
1137  lua_setfield(L, -2, "editor_image");
1138  lua_pushinteger(L, info.light_bonus(0));
1139  lua_setfield(L, -2, "light");
1140  lua_pushboolean(L, info.is_village());
1141  lua_setfield(L, -2, "village");
1142  lua_pushboolean(L, info.is_castle());
1143  lua_setfield(L, -2, "castle");
1144  lua_pushboolean(L, info.is_keep());
1145  lua_setfield(L, -2, "keep");
1146  lua_pushinteger(L, info.gives_healing());
1147  lua_setfield(L, -2, "healing");
1148 
1149  return 1;
1150 }
1151 
1152 /**
1153  * Gets time of day information.
1154  * - Arg 1: optional turn number
1155  * - Arg 2: optional location
1156  * - Arg 3: optional boolean (consider_illuminates)
1157  * - Ret 1: table.
1158  */
1160 {
1161  unsigned arg = 1;
1162 
1163  int for_turn = tod_man().turn();
1164  map_location loc = map_location();
1165  bool consider_illuminates = false;
1166 
1167  if(lua_isnumber(L, arg)) {
1168  ++arg;
1169  for_turn = luaL_checkinteger(L, 1);
1170  int number_of_turns = tod_man().number_of_turns();
1171  if(for_turn < 1 || (number_of_turns != -1 && for_turn > number_of_turns)) {
1172  return luaL_argerror(L, 1, "turn number out of range");
1173  }
1174  }
1175  else if(lua_isnil(L, arg)) ++arg;
1176 
1177  if(luaW_tolocation(L, arg, loc)) {
1178  if(!board().map().on_board(loc)) return luaL_argerror(L, arg, "coordinates are not on board");
1179 
1180  if(lua_istable(L, arg)) {
1181  lua_rawgeti(L, arg, 3);
1182  consider_illuminates = luaW_toboolean(L, -1);
1183  lua_pop(L, 1);
1184  } else if(lua_isboolean(L, arg + 1)) {
1185  consider_illuminates = luaW_toboolean(L, arg + 1);
1186  }
1187  }
1188 
1189  const time_of_day& tod = consider_illuminates ?
1190  tod_man().get_illuminated_time_of_day(board().units(), board().map(), loc, for_turn) :
1191  tod_man().get_time_of_day(loc, for_turn);
1192 
1193  lua_newtable(L);
1194  lua_pushstring(L, tod.id.c_str());
1195  lua_setfield(L, -2, "id");
1196  lua_pushinteger(L, tod.lawful_bonus);
1197  lua_setfield(L, -2, "lawful_bonus");
1199  lua_setfield(L, -2, "bonus_modified");
1200  lua_pushstring(L, tod.image.c_str());
1201  lua_setfield(L, -2, "image");
1202  luaW_pushtstring(L, tod.name);
1203  lua_setfield(L, -2, "name");
1204 
1205  lua_pushinteger(L, tod.color.r);
1206  lua_setfield(L, -2, "red");
1207  lua_pushinteger(L, tod.color.g);
1208  lua_setfield(L, -2, "green");
1209  lua_pushinteger(L, tod.color.b);
1210  lua_setfield(L, -2, "blue");
1211 
1212  return 1;
1213 }
1214 
1215 /**
1216  * Gets the side of a village owner.
1217  * - Arg 1: map location.
1218  * - Ret 1: integer.
1219  */
1221 {
1222  map_location loc = luaW_checklocation(L, 1);
1223  if (!board().map().is_village(loc))
1224  return 0;
1225 
1226  int side = board().village_owner(loc) + 1;
1227  if (!side) return 0;
1228  lua_pushinteger(L, side);
1229  return 1;
1230 }
1231 
1232 /**
1233  * Sets the owner of a village.
1234  * - Arg 1: map location.
1235  * - Arg 2: integer for the side or empty to remove ownership.
1236  */
1238 {
1239  map_location loc = luaW_checklocation(L, 1);
1240  if(!board().map().is_village(loc)) {
1241  return 0;
1242  }
1243 
1244  const int old_side_num = board().village_owner(loc) + 1;
1245  const int new_side_num = lua_isnoneornil(L, 2) ? 0 : luaL_checkinteger(L, 2);
1246 
1247  team* old_side = nullptr;
1248  team* new_side = nullptr;
1249 
1250  if(old_side_num == new_side_num) {
1251  return 0;
1252  }
1253 
1254  try {
1255  old_side = &board().get_team(old_side_num);
1256  } catch(const std::out_of_range&) {
1257  // old_side_num is invalid, most likely because the village wasn't captured.
1258  old_side = nullptr;
1259  }
1260 
1261  try {
1262  new_side = &board().get_team(new_side_num);
1263  } catch(const std::out_of_range&) {
1264  // new_side_num is invalid.
1265  new_side = nullptr;
1266  }
1267 
1268  // The new side was valid, but already defeated. Do nothing.
1269  if(new_side && board().team_is_defeated(*new_side)) {
1270  return 0;
1271  }
1272 
1273  // Even if the new side is not valid, we still want to remove the village from the old side.
1274  // This covers the case where new_side_num equals 0. The behavior in that case is to simply
1275  // un-assign the village from the old side, which of course we also want to happen if the new
1276  // side IS valid. If the village in question hadn't been captured, this won't fire (old_side
1277  // will be a nullptr).
1278  if(old_side) {
1279  old_side->lose_village(loc);
1280  }
1281 
1282  // If the new side was valid, re-assign the village.
1283  if(new_side) {
1284  new_side->get_village(loc, old_side_num, (luaW_toboolean(L, 3) ? &gamedata() : nullptr));
1285  }
1286 
1287  return 0;
1288 }
1289 
1290 
1291 /**
1292  * Returns the map size.
1293  * - Ret 1: width.
1294  * - Ret 2: height.
1295  * - Ret 3: border size.
1296  */
1298 {
1299  const gamemap &map = board().map();
1300  lua_pushinteger(L, map.w());
1301  lua_pushinteger(L, map.h());
1302  lua_pushinteger(L, map.border_size());
1303  return 3;
1304 }
1305 
1306 /**
1307  * Returns the currently overed tile.
1308  * - Ret 1: x.
1309  * - Ret 2: y.
1310  */
1312 {
1313  if (!game_display_) {
1314  return 0;
1315  }
1316 
1317  const map_location &loc = game_display_->mouseover_hex();
1318  if (!board().map().on_board(loc)) return 0;
1319  lua_pushinteger(L, loc.wml_x());
1320  lua_pushinteger(L, loc.wml_y());
1321  return 2;
1322 }
1323 
1324 /**
1325  * Returns the currently selected tile.
1326  * - Ret 1: x.
1327  * - Ret 2: y.
1328  */
1330 {
1331  if (!game_display_) {
1332  return 0;
1333  }
1334 
1335  const map_location &loc = game_display_->selected_hex();
1336  if (!board().map().on_board(loc)) return 0;
1337  lua_pushinteger(L, loc.wml_x());
1338  lua_pushinteger(L, loc.wml_y());
1339  return 2;
1340 }
1341 
1342 /**
1343  * Returns the starting position of a side.
1344  * Arg 1: side number
1345  * Ret 1: table with unnamed indices holding wml coordinates x and y
1346 */
1348 {
1349  const int side = luaL_checkinteger(L, 1);
1350  if(side < 1 || static_cast<int>(teams().size()) < side)
1351  return luaL_argerror(L, 1, "out of bounds");
1352  const map_location& starting_pos = board().map().starting_position(side);
1353  if(!board().map().on_board(starting_pos)) return 0;
1354 
1355  luaW_pushlocation(L, starting_pos);
1356  return 1;
1357 }
1358 
1359 /**
1360  * Gets a table for an era tag.
1361  * - Arg 1: userdata (ignored).
1362  * - Arg 2: string containing id of the desired era
1363  * - Ret 1: config for the era
1364  */
1365 static int intf_get_era(lua_State *L)
1366 {
1367  char const *m = luaL_checkstring(L, 1);
1368  luaW_pushconfig(L, game_config_manager::get()->game_config().find_child("era","id",m));
1369  return 1;
1370 }
1371 
1372 /**
1373  * Gets some game_config data (__index metamethod).
1374  * - Arg 1: userdata (ignored).
1375  * - Arg 2: string containing the name of the property.
1376  * - Ret 1: something containing the attribute.
1377  */
1379 {
1380  LOG_LUA << "impl_game_config_get\n";
1381  char const *m = luaL_checkstring(L, 2);
1382 
1383  // Find the corresponding attribute.
1384  return_int_attrib("last_turn", tod_man().number_of_turns());
1385  return_bool_attrib("do_healing", play_controller_.gamestate().do_healing_);
1386  return_string_attrib("next_scenario", gamedata().next_scenario());
1387  return_string_attrib("theme", gamedata().get_theme());
1388  return_string_attrib("scenario_id", gamedata().get_id());
1389  return_vector_string_attrib("defeat_music", gamedata().get_defeat_music());
1390  return_vector_string_attrib("victory_music", gamedata().get_victory_music());
1391 
1392  const mp_game_settings& mp_settings = play_controller_.get_mp_settings();
1393  const game_classification & classification = play_controller_.get_classification();
1394 
1395  return_string_attrib("campaign_type", classification.campaign_type.to_string());
1396  if(classification.campaign_type==game_classification::CAMPAIGN_TYPE::MULTIPLAYER) {
1397  return_cfgref_attrib("mp_settings", mp_settings.to_config());
1398  return_cfgref_attrib("era", game_config_manager::get()->game_config().find_child("era","id",mp_settings.mp_era));
1399  //^ finds the era with name matching mp_era, and creates a lua reference from the config of that era.
1400 
1401  //This code for SigurdFD, not the cleanest implementation but seems to work just fine.
1403  std::string eras_list(its.front()["id"]);
1404  its.pop_front();
1405  for(const auto& cfg : its) {
1406  eras_list = eras_list + "," + cfg["id"];
1407  }
1408  return_string_attrib("eras", eras_list);
1409  }
1411 }
1412 
1413 /**
1414  * Sets some game_config data (__newindex metamethod).
1415  * - Arg 1: userdata (ignored).
1416  * - Arg 2: string containing the name of the property.
1417  * - Arg 3: something containing the attribute.
1418  */
1420 {
1421  LOG_LUA << "impl_game_config_set\n";
1422  char const *m = luaL_checkstring(L, 2);
1423 
1424  // Find the corresponding attribute.
1425  modify_int_attrib("base_income", game_config::base_income = value);
1426  modify_int_attrib("village_income", game_config::village_income = value);
1427  modify_int_attrib("village_support", game_config::village_support = value);
1428  modify_int_attrib("poison_amount", game_config::poison_amount = value);
1429  modify_int_attrib("rest_heal_amount", game_config::rest_heal_amount = value);
1430  modify_int_attrib("recall_cost", game_config::recall_cost = value);
1431  modify_int_attrib("kill_experience", game_config::kill_experience = value);
1432  modify_int_attrib("combat_experience", game_config::combat_experience = value);
1433  modify_int_attrib("last_turn", tod_man().set_number_of_turns_by_wml(value));
1434  modify_bool_attrib("do_healing", play_controller_.gamestate().do_healing_ = value);
1435  modify_string_attrib("next_scenario", gamedata().set_next_scenario(value));
1436  modify_string_attrib("theme",
1437  gamedata().set_theme(value);
1439  game_display_->set_theme(play_controller_.get_theme(game_config, value));
1440  );
1441  modify_vector_string_attrib("defeat_music", gamedata().set_defeat_music(std::move(value)));
1442  modify_vector_string_attrib("victory_music", gamedata().set_victory_music(std::move(value)));
1444 }
1445 
1446 /**
1447  converts synced_context::get_synced_state() to a string.
1448 */
1450 {
1451  //maybe return "initial" for game_data::INITIAL?
1452  if(gamedata().phase() == game_data::PRELOAD || gamedata().phase() == game_data::INITIAL)
1453  {
1454  return "preload";
1455  }
1457  {
1459  return "local_choice";
1461  return "synced";
1463  return "unsynced";
1464  default:
1465  throw game::game_error("Found corrupt synced_context::synced_state");
1466  }
1467 }
1468 
1469 
1470 /**
1471  * Gets some data about current point of game (__index metamethod).
1472  * - Arg 1: userdata (ignored).
1473  * - Arg 2: string containing the name of the property.
1474  * - Ret 1: something containing the attribute.
1475  */
1477 {
1478  char const *m = luaL_checkstring(L, 2);
1479 
1480  // Find the corresponding attribute.
1481  return_int_attrib("side", play_controller_.current_side());
1482  return_int_attrib("turn", play_controller_.turn());
1483  return_string_attrib("synced_state", synced_state());
1484  return_bool_attrib("user_can_invoke_commands", !play_controller_.is_lingering() && play_controller_.gamestate().init_side_done() && !events::commands_disabled && gamedata().phase() == game_data::PLAY);
1485 
1486  if (strcmp(m, "event_context") == 0)
1487  {
1488  const game_events::queued_event &ev = get_event_info();
1489  config cfg;
1490  cfg["name"] = ev.name;
1491  cfg["id"] = ev.id;
1492  if (const config &weapon = ev.data.child("first")) {
1493  cfg.add_child("weapon", weapon);
1494  }
1495  if (const config &weapon = ev.data.child("second")) {
1496  cfg.add_child("second_weapon", weapon);
1497  }
1498 
1499  const config::attribute_value di = ev.data["damage_inflicted"];
1500  if(!di.empty()) {
1501  cfg["damage_inflicted"] = di;
1502  }
1503 
1504  if (ev.loc1.valid()) {
1505  cfg["x1"] = ev.loc1.filter_loc().wml_x();
1506  cfg["y1"] = ev.loc1.filter_loc().wml_y();
1507  // The position of the unit involved in this event, currently the only case where this is different from x1/y1 are enter/exit_hex events
1508  cfg["unit_x"] = ev.loc1.wml_x();
1509  cfg["unit_y"] = ev.loc1.wml_y();
1510  }
1511  if (ev.loc2.valid()) {
1512  cfg["x2"] = ev.loc2.filter_loc().wml_x();
1513  cfg["y2"] = ev.loc2.filter_loc().wml_y();
1514  }
1515  luaW_pushconfig(L, cfg);
1516  return 1;
1517  }
1518 
1519  return 0;
1520 }
1521 
1522 /**
1523  * Displays a message in the chat window and in the logs.
1524  * - Arg 1: optional message header.
1525  * - Arg 2 (or 1): message.
1526  */
1528 {
1529  t_string m = luaW_checktstring(L, 1);
1530  t_string h = m;
1531  if (lua_isnone(L, 2)) {
1532  h = "Lua";
1533  } else {
1534  m = luaW_checktstring(L, 2);
1535  }
1536  lua_chat(h, m);
1537  LOG_LUA << "Script says: \"" << m << "\"\n";
1538  return 0;
1539 }
1540 
1542 {
1543  if (game_display_) {
1545  }
1546  return 0;
1547 }
1548 
1550 {
1551  if(!game_display_) {
1552  return 0;
1553  }
1554  double factor = luaL_checknumber(L, 1);
1555  bool relative = luaW_toboolean(L, 2);
1556  if(relative) {
1557  factor *= game_display_->get_zoom_factor();
1558  }
1559  // Passing true explicitly to avoid casting to int.
1560  // Without doing one of the two, the call is ambiguous.
1561  game_display_->set_zoom(factor * game_config::tile_size, true);
1562  lua_pushnumber(L, game_display_->get_zoom_factor());
1563  return 1;
1564 }
1565 
1566 /**
1567  * Removes all messages from the chat window.
1568  */
1570 {
1571  if (game_display_) {
1572  game_display_->get_chat_manager().clear_chat_messages();
1573  }
1574  return 0;
1575 }
1576 
1578 {
1579  const end_level_data& data = *static_cast<end_level_data*>(lua_touserdata(L, 1));
1580  const char* m = luaL_checkstring(L, 2);
1581 
1582  return_bool_attrib("linger_mode", data.transient.linger_mode);
1583  return_bool_attrib("reveal_map", data.transient.reveal_map);
1584  return_bool_attrib("carryover_report", data.transient.carryover_report);
1585  return_bool_attrib("prescenario_save", data.prescenario_save);
1586  return_bool_attrib("replay_save", data.replay_save);
1587  return_bool_attrib("proceed_to_next_level", data.proceed_to_next_level);
1588  return_bool_attrib("is_victory", data.is_victory);
1589  return_bool_attrib("is_loss", !data.is_victory);
1590  return_cstring_attrib("result", data.is_victory ? "victory" : "loss"); // to match wesnoth.end_level()
1591  return_cfg_attrib("__cfg", data.to_config_full());
1592 
1593  return 0;
1594 }
1595 
1596 namespace {
1597  struct end_level_committer {
1598  end_level_committer(end_level_data& data, play_controller& pc) : data_(data), pc_(pc) {}
1599  ~end_level_committer() {
1600  pc_.set_end_level_data(data_);
1601  }
1602  private:
1603  end_level_data& data_;
1604  play_controller& pc_;
1605  };
1606 }
1607 
1609 {
1610  end_level_data& data = *static_cast<end_level_data*>(lua_touserdata(L, 1));
1611  const char* m = luaL_checkstring(L, 2);
1612  end_level_committer commit(data, play_controller_);
1613 
1614  modify_bool_attrib("linger_mode", data.transient.linger_mode = value);
1615  modify_bool_attrib("reveal_map", data.transient.reveal_map = value);
1616  modify_bool_attrib("carryover_report", data.transient.carryover_report = value);
1617  modify_bool_attrib("prescenario_save", data.prescenario_save = value);
1618  modify_bool_attrib("replay_save", data.replay_save = value);
1619 
1620  return 0;
1621 }
1622 
1624 {
1625  end_level_data* data = static_cast<end_level_data*>(lua_touserdata(L, 1));
1626  UNUSED(data); // Suppress an erroneous MSVC warning (a destructor call doesn't count as a reference)
1627  data->~end_level_data();
1628  return 0;
1629 }
1630 
1632 {
1633  if (!play_controller_.is_regular_game_end()) {
1634  return 0;
1635  }
1636  auto data = play_controller_.get_end_level_data_const();
1637  new(L) end_level_data(data);
1638  if(luaL_newmetatable(L, "end level data")) {
1639  static luaL_Reg const callbacks[] {
1640  { "__index", &impl_end_level_data_get},
1641  { "__newindex", &dispatch<&game_lua_kernel::impl_end_level_data_set>},
1642  { "__gc", &impl_end_level_data_collect},
1643  { nullptr, nullptr }
1644  };
1645  luaL_setfuncs(L, callbacks, 0);
1646  }
1647  lua_setmetatable(L, -2);
1648  return 1;
1649 }
1650 
1652 {
1653  vconfig cfg(luaW_checkvconfig(L, 1));
1654  end_level_data data;
1655 
1656  data.proceed_to_next_level = cfg["proceed_to_next_level"].to_bool(true);
1657  data.transient.carryover_report = cfg["carryover_report"].to_bool(true);
1658  data.prescenario_save = cfg["save"].to_bool(true);
1659  data.replay_save = cfg["replay_save"].to_bool(true);
1660  data.transient.linger_mode = cfg["linger_mode"].to_bool(true) && !teams().empty();
1661  data.transient.reveal_map = cfg["reveal_map"].to_bool(true);
1662  data.is_victory = cfg["result"] == "victory";
1663  play_controller_.set_end_level_data(data);
1664  return 0;
1665 }
1666 
1668 {
1669  //note that next_player_number = 1, next_player_number = nteams+1 both set the next team to be the first team
1670  //but the later will make the turn counter change aswell fire turn end events accoringly etc.
1671  if (!lua_isnoneornil(L, 1)) {
1672  int npn = luaL_checknumber(L, 1);
1673  if (npn <= 0 /*TODO: || npn > 2*nteams*/) {
1674  return luaL_argerror(L, 1, "side number out of range");
1675  }
1677  }
1678  play_controller_.force_end_turn();
1679  return 0;
1680 }
1681 
1682 /**
1683  * Evaluates a boolean WML conditional.
1684  * - Arg 1: WML table.
1685  * - Ret 1: boolean.
1686  */
1688 {
1689  vconfig cond = luaW_checkvconfig(L, 1);
1690  bool b = game_events::conditional_passed(cond);
1691  lua_pushboolean(L, b);
1692  return 1;
1693 }
1694 
1695 
1696 /**
1697  * Finds a path between two locations.
1698  * - Arg 1: source location. (Or Arg 1: unit.)
1699  * - Arg 2: destination.
1700  * - Arg 3: optional cost function or
1701  * table (optional fields: ignore_units, ignore_teleport, max_cost, viewing_side).
1702  * - Ret 1: array of pairs containing path steps.
1703  * - Ret 2: path cost.
1704  */
1706 {
1707  int arg = 1;
1708  map_location src, dst;
1709  const unit* u = nullptr;
1710 
1711  if (lua_isuserdata(L, arg))
1712  {
1713  u = &luaW_checkunit(L, arg);
1714  src = u->get_location();
1715  ++arg;
1716  }
1717  else
1718  {
1719  src = luaW_checklocation(L, arg);
1720  unit_map::const_unit_iterator ui = units().find(src);
1721  if (ui.valid()) {
1722  u = ui.get_shared_ptr().get();
1723  }
1724  ++arg;
1725  }
1726 
1727  dst = luaW_checklocation(L, arg);
1728  ++arg;
1729 
1730  if (!board().map().on_board(src))
1731  return luaL_argerror(L, 1, "invalid location");
1732  if (!board().map().on_board(dst))
1733  return luaL_argerror(L, arg - 2, "invalid location");
1734 
1735  const gamemap &map = board().map();
1736  int viewing_side = 0;
1737  bool ignore_units = false, see_all = false, ignore_teleport = false;
1738  double stop_at = 10000;
1739  std::unique_ptr<pathfind::cost_calculator> calc;
1740 
1741  if (lua_istable(L, arg))
1742  {
1743  ignore_units = luaW_table_get_def<bool>(L, arg, "ignore_units", false);
1744 
1745  ignore_teleport = luaW_table_get_def<bool>(L, arg, "ignore_teleport", false);
1746 
1747  stop_at = luaW_table_get_def<double>(L, arg, "stop_at", stop_at);
1748 
1749 
1750  lua_pushstring(L, "viewing_side");
1751  lua_rawget(L, arg);
1752  if (!lua_isnil(L, -1)) {
1753  int i = luaL_checkinteger(L, -1);
1754  if (i >= 1 && i <= static_cast<int>(teams().size())) viewing_side = i;
1755  else see_all = true;
1756  }
1757  lua_pop(L, 1);
1758 
1759  lua_pushstring(L, "calculate");
1760  lua_rawget(L, arg);
1761  if(lua_isfunction(L, -1)) {
1762  calc.reset(new lua_pathfind_cost_calculator(L, lua_gettop(L)));
1763  }
1764  // Don't pop, the lua_pathfind_cost_calculator requires it to stay on the stack.
1765  }
1766  else if (lua_isfunction(L, arg))
1767  {
1768  deprecated_message("wesnoth.find_path with cost_function as last argument", DEP_LEVEL::FOR_REMOVAL, {1, 17, 0}, "Use calculate=cost_function inside the path options table instead.");
1769  calc.reset(new lua_pathfind_cost_calculator(L, arg));
1770  }
1771 
1772  const team& viewing_team = viewing_side
1773  ? board().get_team(viewing_side)
1774  : board().get_team(u->side());
1775 
1776  pathfind::teleport_map teleport_locations;
1777 
1778  if(!ignore_teleport) {
1779  teleport_locations = pathfind::get_teleport_locations(*u, viewing_team, see_all, ignore_units);
1780  }
1781 
1782  if (!calc) {
1783  if (!u) return luaL_argerror(L, 1, "unit not found");
1784 
1785  calc.reset(new pathfind::shortest_path_calculator(*u, viewing_team,
1786  teams(), map, ignore_units, false, see_all));
1787  }
1788 
1789  pathfind::plain_route res = pathfind::a_star_search(src, dst, stop_at, *calc, map.w(), map.h(),
1790  &teleport_locations);
1791 
1792  int nb = res.steps.size();
1793  lua_createtable(L, nb, 0);
1794  for (int i = 0; i < nb; ++i)
1795  {
1796  lua_createtable(L, 2, 0);
1797  lua_pushinteger(L, res.steps[i].wml_x());
1798  lua_rawseti(L, -2, 1);
1799  lua_pushinteger(L, res.steps[i].wml_y());
1800  lua_rawseti(L, -2, 2);
1801  lua_rawseti(L, -2, i + 1);
1802  }
1803  lua_pushinteger(L, res.move_cost);
1804 
1805  return 2;
1806 }
1807 
1808 /**
1809  * Finds all the locations reachable by a unit.
1810  * - Arg 1: source location OR unit.
1811  * - Arg 2: optional table (optional fields: ignore_units, ignore_teleport, additional_turns, viewing_side).
1812  * - Ret 1: array of triples (coordinates + remaining movement).
1813  */
1815 {
1816  int arg = 1;
1817  const unit* u = nullptr;
1818 
1819  if (lua_isuserdata(L, arg))
1820  {
1821  u = &luaW_checkunit(L, arg);
1822  ++arg;
1823  }
1824  else
1825  {
1826  map_location src = luaW_checklocation(L, arg);
1827  unit_map::const_unit_iterator ui = units().find(src);
1828  if (!ui.valid())
1829  return luaL_argerror(L, 1, "unit not found");
1830  u = ui.get_shared_ptr().get();
1831  ++arg;
1832  }
1833 
1834  int viewing_side = 0;
1835  bool ignore_units = false, see_all = false, ignore_teleport = false;
1836  int additional_turns = 0;
1837 
1838  if (lua_istable(L, arg))
1839  {
1840  lua_pushstring(L, "ignore_units");
1841  lua_rawget(L, arg);
1842  ignore_units = luaW_toboolean(L, -1);
1843  lua_pop(L, 1);
1844 
1845  lua_pushstring(L, "ignore_teleport");
1846  lua_rawget(L, arg);
1847  ignore_teleport = luaW_toboolean(L, -1);
1848  lua_pop(L, 1);
1849 
1850  lua_pushstring(L, "additional_turns");
1851  lua_rawget(L, arg);
1852  additional_turns = lua_tointeger(L, -1);
1853  lua_pop(L, 1);
1854 
1855  lua_pushstring(L, "viewing_side");
1856  lua_rawget(L, arg);
1857  if (!lua_isnil(L, -1)) {
1858  int i = luaL_checkinteger(L, -1);
1859  if (i >= 1 && i <= static_cast<int>(teams().size())) viewing_side = i;
1860  else see_all = true;
1861  }
1862  lua_pop(L, 1);
1863  }
1864 
1865  const team& viewing_team = viewing_side
1866  ? board().get_team(viewing_side)
1867  : board().get_team(u->side());
1868 
1869  pathfind::paths res(*u, ignore_units, !ignore_teleport,
1870  viewing_team, additional_turns, see_all, ignore_units);
1871 
1872  int nb = res.destinations.size();
1873  lua_createtable(L, nb, 0);
1874  for (int i = 0; i < nb; ++i)
1875  {
1877  lua_createtable(L, 2, 0);
1878  lua_pushinteger(L, s.curr.wml_x());
1879  lua_rawseti(L, -2, 1);
1880  lua_pushinteger(L, s.curr.wml_y());
1881  lua_rawseti(L, -2, 2);
1882  lua_pushinteger(L, s.move_left);
1883  lua_rawseti(L, -2, 3);
1884  lua_rawseti(L, -2, i + 1);
1885  }
1886 
1887  return 1;
1888 }
1889 
1890 static bool intf_find_cost_map_helper(const unit * ptr) {
1891  return ptr->get_location().valid();
1892 }
1893 
1894 template<typename T> // This is only a template so I can avoid typing out the long typename. >_>
1895 static int load_fake_units(lua_State* L, int arg, T& fake_units)
1896 {
1897  for (int i = 1, i_end = lua_rawlen(L, arg); i <= i_end; ++i)
1898  {
1899  map_location src;
1900  lua_rawgeti(L, arg, i);
1901  int entry = lua_gettop(L);
1902  if (!lua_istable(L, entry)) {
1903  goto error;
1904  }
1905 
1906  if (!luaW_tolocation(L, entry, src)) {
1907  goto error;
1908  }
1909 
1910  lua_rawgeti(L, entry, 3);
1911  if (!lua_isnumber(L, -1)) {
1912  lua_getfield(L, entry, "side");
1913  if (!lua_isnumber(L, -1)) {
1914  goto error;
1915  }
1916  }
1917  int side = lua_tointeger(L, -1);
1918 
1919  lua_rawgeti(L, entry, 4);
1920  if (!lua_isstring(L, -1)) {
1921  lua_getfield(L, entry, "type");
1922  if (!lua_isstring(L, -1)) {
1923  goto error;
1924  }
1925  }
1926  std::string unit_type = lua_tostring(L, -1);
1927 
1928  fake_units.emplace_back(src, side, unit_type);
1929 
1930  lua_settop(L, entry - 1);
1931  }
1932  return 0;
1933 error:
1934  return luaL_argerror(L, arg, "unit type table malformed - each entry should be either array of 4 elements or table with keys x, y, side, type");
1935 }
1936 
1937 /**
1938  * Is called with one or more units and builds a cost map.
1939  * - Arg 1: source location. (Or Arg 1: unit. Or Arg 1: table containing a filter)
1940  * - Arg 2: optional array of tables with 4 elements (coordinates + side + unit type string)
1941  * - Arg 3: optional table (optional fields: ignore_units, ignore_teleport, viewing_side, debug).
1942  * - Arg 4: optional table: standard location filter.
1943  * - Ret 1: array of triples (coordinates + array of tuples(summed cost + reach counter)).
1944  */
1946 {
1947  int arg = 1;
1948  unit* unit = luaW_tounit(L, arg, true);
1950  luaW_tovconfig(L, arg, filter);
1951 
1952  std::vector<const ::unit*> real_units;
1953  typedef std::vector<std::tuple<map_location, int, std::string>> unit_type_vector;
1954  unit_type_vector fake_units;
1955 
1956 
1957  if (unit) // 1. arg - unit
1958  {
1959  real_units.push_back(unit);
1960  }
1961  else if (!filter.null()) // 1. arg - filter
1962  {
1963  boost::copy(unit_filter(filter).all_matches_on_map() | boost::adaptors::filtered(&intf_find_cost_map_helper), std::back_inserter(real_units));
1964  }
1965  else // 1. arg - coordinates
1966  {
1967  map_location src = luaW_checklocation(L, arg);
1968  unit_map::const_unit_iterator ui = units().find(src);
1969  if (ui.valid())
1970  {
1971  real_units.push_back(&(*ui));
1972  }
1973  }
1974  ++arg;
1975 
1976  if (lua_istable(L, arg)) // 2. arg - optional types
1977  {
1978  load_fake_units(L, arg, fake_units);
1979  ++arg;
1980  }
1981 
1982  if(real_units.empty() && fake_units.empty())
1983  {
1984  return luaL_argerror(L, 1, "unit(s) not found");
1985  }
1986 
1987  int viewing_side = 0;
1988  bool ignore_units = true, see_all = true, ignore_teleport = false, debug = false, use_max_moves = false;
1989 
1990  if (lua_istable(L, arg)) // 4. arg - options
1991  {
1992  lua_pushstring(L, "ignore_units");
1993  lua_rawget(L, arg);
1994  if (!lua_isnil(L, -1))
1995  {
1996  ignore_units = luaW_toboolean(L, -1);
1997  }
1998  lua_pop(L, 1);
1999 
2000  lua_pushstring(L, "ignore_teleport");
2001  lua_rawget(L, arg);
2002  if (!lua_isnil(L, -1))
2003  {
2004  ignore_teleport = luaW_toboolean(L, -1);
2005  }
2006  lua_pop(L, 1);
2007 
2008  lua_pushstring(L, "viewing_side");
2009  lua_rawget(L, arg);
2010  if (!lua_isnil(L, -1))
2011  {
2012  int i = luaL_checkinteger(L, -1);
2013  if (i >= 1 && i <= static_cast<int>(teams().size()))
2014  {
2015  viewing_side = i;
2016  see_all = false;
2017  }
2018  }
2019 
2020  lua_pushstring(L, "debug");
2021  lua_rawget(L, arg);
2022  if (!lua_isnil(L, -1))
2023  {
2024  debug = luaW_toboolean(L, -1);
2025  }
2026  lua_pop(L, 1);
2027 
2028  lua_pushstring(L, "use_max_moves");
2029  lua_rawget(L, arg);
2030  if (!lua_isnil(L, -1))
2031  {
2032  use_max_moves = luaW_toboolean(L, -1);
2033  }
2034  lua_pop(L, 1);
2035  ++arg;
2036  }
2037 
2038  // 5. arg - location filter
2039  filter = vconfig::unconstructed_vconfig();
2040  std::set<map_location> location_set;
2041  luaW_tovconfig(L, arg, filter);
2042  if (filter.null())
2043  {
2044  filter = vconfig(config(), true);
2045  }
2046  filter_context & fc = game_state_;
2047  const terrain_filter t_filter(filter, &fc);
2048  t_filter.get_locations(location_set, true);
2049  ++arg;
2050 
2051  // build cost_map
2052  const team& viewing_team = viewing_side
2053  ? board().get_team(viewing_side)
2054  : board().teams()[0];
2055 
2056  pathfind::full_cost_map cost_map(
2057  ignore_units, !ignore_teleport, viewing_team, see_all, ignore_units);
2058 
2059  for (const ::unit* const u : real_units)
2060  {
2061  cost_map.add_unit(*u, use_max_moves);
2062  }
2063  for (const unit_type_vector::value_type& fu : fake_units)
2064  {
2065  const unit_type* ut = unit_types.find(std::get<2>(fu));
2066  cost_map.add_unit(std::get<0>(fu), ut, std::get<1>(fu));
2067  }
2068 
2069  if (debug)
2070  {
2071  if (game_display_) {
2072  game_display_->labels().clear_all();
2073  for (const map_location& loc : location_set)
2074  {
2075  std::stringstream s;
2076  s << cost_map.get_pair_at(loc.x, loc.y).first;
2077  s << " / ";
2078  s << cost_map.get_pair_at(loc.x, loc.y).second;
2079  game_display_->labels().set_label(loc, s.str());
2080  }
2081  }
2082  }
2083 
2084  // create return value
2085  lua_createtable(L, location_set.size(), 0);
2086  int counter = 1;
2087  for (const map_location& loc : location_set)
2088  {
2089  lua_createtable(L, 4, 0);
2090 
2091  lua_pushinteger(L, loc.wml_x());
2092  lua_rawseti(L, -2, 1);
2093 
2094  lua_pushinteger(L, loc.wml_y());
2095  lua_rawseti(L, -2, 2);
2096 
2097  lua_pushinteger(L, cost_map.get_pair_at(loc.x, loc.y).first);
2098  lua_rawseti(L, -2, 3);
2099 
2100  lua_pushinteger(L, cost_map.get_pair_at(loc.x, loc.y).second);
2101  lua_rawseti(L, -2, 4);
2102 
2103  lua_rawseti(L, -2, counter);
2104  ++counter;
2105  }
2106  return 1;
2107 }
2108 
2110  vconfig cfg(luaW_checkvconfig(L, 1));
2111 
2112  // Remove any old message.
2113  static int floating_label = 0;
2114  if (floating_label)
2115  font::remove_floating_label(floating_label);
2116 
2117  // Display a message on-screen
2118  std::string text = cfg["text"];
2119  if(text.empty() || !game_display_)
2120  return 0;
2121 
2122  int size = cfg["size"].to_int(font::SIZE_SMALL);
2123  int lifetime = cfg["duration"].to_int(50);
2124 
2125  color_t color = font::LABEL_COLOR;
2126 
2127  if(!cfg["color"].empty()) {
2128  color = color_t::from_rgb_string(cfg["color"]);
2129  } else if(cfg.has_attribute("red") || cfg.has_attribute("green") || cfg.has_attribute("blue")) {
2130  color = color_t(cfg["red"], cfg["green"], cfg["blue"]);
2131  }
2132 
2133  const SDL_Rect& rect = game_display_->map_outside_area();
2134 
2135  font::floating_label flabel(text);
2136  flabel.set_font_size(size);
2137  flabel.set_color(color);
2138  flabel.set_position(rect.w/2,rect.h/2);
2139  flabel.set_lifetime(lifetime);
2140  flabel.set_clip_rect(rect);
2141 
2142  floating_label = font::add_floating_label(flabel);
2143 
2144  return 0;
2145 }
2146 
2148 {
2149  if(game_display_) {
2150  game_display_->invalidate(loc);
2151  }
2152 
2153  units().erase(loc);
2154  resources::whiteboard->on_kill_unit();
2155 }
2156 
2157 /**
2158  * Places a unit on the map.
2159  * - Arg 1: (optional) location.
2160  * - Arg 2: Unit (WML table or proxy), or nothing/nil to delete.
2161  * OR
2162  * - Arg 1: Unit (WML table or proxy)
2163  * - Arg 2: (optional) location
2164  * - Arg 3: (optional) boolean
2165  */
2167 {
2168  if(map_locked_) {
2169  return luaL_error(L, "Attempted to move a unit while the map is locked");
2170  }
2171  int unit_arg = 1;
2172 
2173  map_location loc;
2174  if (lua_isnumber(L, 1)) {
2175  // Since this form is deprecated, I didn't bother updating it to luaW_tolocation.
2176  unit_arg = 3;
2177  loc.set_wml_x(lua_tointeger(L, 1));
2178  loc.set_wml_y(luaL_checkinteger(L, 2));
2179  if (!map().on_board(loc)) {
2180  return luaL_argerror(L, 1, "invalid location");
2181  }
2182  } else if (luaW_tolocation(L, 2, loc)) {
2183  if (!map().on_board(loc)) {
2184  return luaL_argerror(L, 2, "invalid location");
2185  }
2186  }
2187 
2188  if((luaW_isunit(L, unit_arg))) {
2189  lua_unit& u = *luaW_checkunit_ref(L, unit_arg);
2190  if(u.on_map() && u->get_location() == loc) {
2191  return 0;
2192  }
2193  if (!loc.valid()) {
2194  loc = u->get_location();
2195  if (!map().on_board(loc))
2196  return luaL_argerror(L, 1, "invalid location");
2197  } else if (unit_arg != 1) {
2198  deprecated_message("wesnoth.put_unit(x, y, unit)", DEP_LEVEL::FOR_REMOVAL, {1, 15, 0}, "Use wesnoth.put_unit(unit, x, y) or unit:to_map(x, y) instead.");
2199  }
2200  put_unit_helper(loc);
2201  u.put_map(loc);
2202  u.get_shared()->anim_comp().set_standing();
2203  } else if(!lua_isnoneornil(L, unit_arg)) {
2204  const vconfig* vcfg = nullptr;
2205  config cfg = luaW_checkconfig(L, unit_arg, vcfg);
2206  if (unit_arg == 1 && !map().on_board(loc)) {
2207  loc.set_wml_x(cfg["x"]);
2208  loc.set_wml_y(cfg["y"]);
2209  if (!map().on_board(loc))
2210  return luaL_argerror(L, 2, "invalid location");
2211  } else if (unit_arg != 1) {
2212  deprecated_message("wesnoth.put_unit(x, y, unit)", DEP_LEVEL::FOR_REMOVAL, {1, 15, 0}, "Use wesnoth.put_unit(unit, x, y) or unit:to_map(x, y) instead.");
2213  }
2214  unit_ptr u = unit::create(cfg, true, vcfg);
2215  put_unit_helper(loc);
2216  u->set_location(loc);
2217  units().insert(u);
2218  } else {
2219  deprecated_message("wesnoth.put_unit(x, y)", DEP_LEVEL::FOR_REMOVAL, {1, 15, 0}, "Use wesnoth.erase_unit(x, y) or unit:erase() instead.");
2220  put_unit_helper(loc);
2221  return 0; // Don't fire event when unit is only erase
2222  }
2223 
2224  // Fire event if using the deprecated version or if the final argument is not false
2225  // If the final boolean argument is omitted, the actual final argument (the unit or location) will always yield true.
2226  if(unit_arg != 1 || luaW_toboolean(L, -1)) {
2227  play_controller_.pump().fire("unit_placed", loc);
2228  }
2229  return 0;
2230 }
2231 
2232 /**
2233  * Erases a unit from the map
2234  * - Arg 1: Unit to erase OR Location to erase unit
2235  */
2237 {
2238  if(map_locked_) {
2239  return luaL_error(L, "Attempted to remove a unit while the map is locked");
2240  }
2241  map_location loc;
2242 
2243  if(luaW_isunit(L, 1)) {
2244  lua_unit& u = *luaW_checkunit_ref(L, 1);
2245  if (u.on_map()) {
2246  loc = u->get_location();
2247  if (!map().on_board(loc)) {
2248  return luaL_argerror(L, 1, "invalid location");
2249  }
2250  } else if (int side = u.on_recall_list()) {
2251  team &t = board().get_team(side);
2252  // Should it use underlying ID instead?
2254  } else {
2255  return luaL_argerror(L, 1, "can't erase private units");
2256  }
2257  } else if (luaW_tolocation(L, 1, loc)) {
2258  if (!map().on_board(loc)) {
2259  return luaL_argerror(L, 1, "invalid location");
2260  }
2261  } else {
2262  return luaL_argerror(L, 1, "expected unit or location");
2263  }
2264 
2265  units().erase(loc);
2266  resources::whiteboard->on_kill_unit();
2267  return 0;
2268 }
2269 
2270 /**
2271  * Puts a unit on a recall list.
2272  * - Arg 1: WML table or unit.
2273  * - Arg 2: (optional) side.
2274  */
2276 {
2277  if(map_locked_) {
2278  return luaL_error(L, "Attempted to move a unit while the map is locked");
2279  }
2280  lua_unit *lu = nullptr;
2281  unit_ptr u = unit_ptr();
2282  int side = lua_tointeger(L, 2);
2283  if (static_cast<unsigned>(side) > teams().size()) side = 0;
2284 
2285  if(luaW_isunit(L, 1)) {
2286  lu = luaW_checkunit_ref(L, 1);
2287  u = lu->get_shared();
2288  if(lu->on_recall_list() && lu->on_recall_list() == side) {
2289  return luaL_argerror(L, 1, "unit already on recall list");
2290  }
2291  } else {
2292  const vconfig* vcfg = nullptr;
2293  config cfg = luaW_checkconfig(L, 1, vcfg);
2294  u = unit::create(cfg, true, vcfg);
2295  }
2296 
2297  if (!side) {
2298  side = u->side();
2299  } else {
2300  u->set_side(side);
2301  }
2302  team &t = board().get_team(side);
2303  // Avoid duplicates in the recall list.
2304  std::size_t uid = u->underlying_id();
2306  t.recall_list().add(u);
2307  if (lu) {
2308  if (lu->on_map()) {
2309  units().erase(u->get_location());
2310  resources::whiteboard->on_kill_unit();
2311  u->anim_comp().clear_haloes();
2312  }
2313  lu->lua_unit::~lua_unit();
2314  new(lu) lua_unit(side, uid);
2315  }
2316 
2317  return 0;
2318 }
2319 
2320 /**
2321  * Extracts a unit from the map or a recall list and gives it to Lua.
2322  * - Arg 1: unit userdata.
2323  */
2325 {
2326  if(map_locked_) {
2327  return luaL_error(L, "Attempted to remove a unit while the map is locked");
2328  }
2329  lua_unit* lu = luaW_checkunit_ref(L, 1);
2330  unit_ptr u = lu->get_shared();
2331 
2332  if (lu->on_map()) {
2333  u = units().extract(u->get_location());
2334  assert(u);
2335  u->anim_comp().clear_haloes();
2336  } else if (int side = lu->on_recall_list()) {
2337  team &t = board().get_team(side);
2338  unit_ptr v = u->clone();
2339  t.recall_list().erase_if_matches_id(u->id());
2340  u = v;
2341  } else {
2342  return 0;
2343  }
2344 
2345  lu->lua_unit::~lua_unit();
2346  new(lu) lua_unit(u);
2347  return 0;
2348 }
2349 
2350 /**
2351  * Finds a vacant tile.
2352  * - Arg 1: location.
2353  * - Arg 2: optional unit for checking movement type.
2354  * - Rets 1,2: location.
2355  */
2357 {
2358  map_location loc = luaW_checklocation(L, 1);
2359 
2360  unit_ptr u;
2361  if (!lua_isnoneornil(L, 2)) {
2362  if(luaW_isunit(L, 2)) {
2363  u = luaW_checkunit_ptr(L, 2, false);
2364  } else {
2365  const vconfig* vcfg = nullptr;
2366  config cfg = luaW_checkconfig(L, 2, vcfg);
2367  u = unit::create(cfg, false, vcfg);
2368  }
2369  }
2370 
2371  map_location res = find_vacant_tile(loc, pathfind::VACANT_ANY, u.get());
2372 
2373  if (!res.valid()) return 0;
2374  lua_pushinteger(L, res.wml_x());
2375  lua_pushinteger(L, res.wml_y());
2376  return 2;
2377 }
2378 
2379 /**
2380  * Floats some text on the map.
2381  * - Arg 1: location.
2382  * - Arg 2: string.
2383  * - Arg 3: color.
2384  */
2386 {
2387  map_location loc = luaW_checklocation(L, 1);
2388  color_t color = font::LABEL_COLOR;
2389 
2390  t_string text = luaW_checktstring(L, 2);
2391  if (!lua_isnoneornil(L, 3)) {
2393  }
2394 
2395  if (game_display_) {
2396  game_display_->float_label(loc, text, color);
2397  }
2398  return 0;
2399 }
2400 
2401 /**
2402  * Creates a unit from its WML description.
2403  * - Arg 1: WML table.
2404  * - Ret 1: unit userdata.
2405  */
2407 {
2408  const vconfig* vcfg = nullptr;
2409  config cfg = luaW_checkconfig(L, 1, vcfg);
2410  unit_ptr u = unit::create(cfg, true, vcfg);
2411  luaW_pushunit(L, u);
2412  return 1;
2413 }
2414 
2415 /**
2416  * Copies a unit.
2417  * - Arg 1: unit userdata.
2418  * - Ret 1: unit userdata.
2419  */
2421 {
2422  unit& u = luaW_checkunit(L, 1);
2423  luaW_pushunit(L, u.clone());
2424  return 1;
2425 }
2426 
2427 /**
2428  * Returns unit resistance against a given attack type.
2429  * - Arg 1: unit userdata.
2430  * - Arg 2: string containing the attack type.
2431  * - Arg 3: boolean indicating if attacker.
2432  * - Arg 4: optional location.
2433  * - Ret 1: integer.
2434  */
2436 {
2437  const unit& u = luaW_checkunit(L, 1);
2438  char const *m = luaL_checkstring(L, 2);
2439  bool a = luaW_toboolean(L, 3);
2440 
2441  map_location loc = u.get_location();
2442  if (!lua_isnoneornil(L, 4)) {
2443  loc = luaW_checklocation(L, 4);
2444  }
2445 
2446  lua_pushinteger(L, u.resistance_against(m, a, loc));
2447  return 1;
2448 }
2449 
2450 /**
2451  * Returns unit movement cost on a given terrain.
2452  * - Arg 1: unit userdata.
2453  * - Arg 2: string containing the terrain type.
2454  * - Ret 1: integer.
2455  */
2457 {
2458  const unit& u = luaW_checkunit(L, 1);
2459  char const *m = luaL_checkstring(L, 2);
2461  lua_pushinteger(L, u.movement_cost(t));
2462  return 1;
2463 }
2464 
2465 /**
2466  * Returns unit vision cost on a given terrain.
2467  * - Arg 1: unit userdata.
2468  * - Arg 2: string containing the terrain type.
2469  * - Ret 1: integer.
2470  */
2472 {
2473  const unit& u = luaW_checkunit(L, 1);
2474  char const *m = luaL_checkstring(L, 2);
2476  lua_pushinteger(L, u.vision_cost(t));
2477  return 1;
2478 }
2479 
2480 /**
2481  * Returns unit jamming cost on a given terrain.
2482  * - Arg 1: unit userdata.
2483  * - Arg 2: string containing the terrain type.
2484  * - Ret 1: integer.
2485  */
2487 {
2488  const unit& u = luaW_checkunit(L, 1);
2489  char const *m = luaL_checkstring(L, 2);
2491  lua_pushinteger(L, u.jamming_cost(t));
2492  return 1;
2493 }
2494 
2495 /**
2496  * Returns unit defense on a given terrain.
2497  * - Arg 1: unit userdata.
2498  * - Arg 2: string containing the terrain type.
2499  * - Ret 1: integer.
2500  */
2502 {
2503  const unit& u = luaW_checkunit(L, 1);
2504  char const *m = luaL_checkstring(L, 2);
2507  return 1;
2508 }
2509 
2510 /**
2511  * Returns true if the unit has the given ability enabled.
2512  * - Arg 1: unit userdata.
2513  * - Arg 2: string.
2514  * - Ret 1: boolean.
2515  */
2517 {
2518  const unit& u = luaW_checkunit(L, 1);
2519  char const *m = luaL_checkstring(L, 2);
2521  return 1;
2522 }
2523 
2524 /**
2525  * Changes a unit to the given unit type.
2526  * - Arg 1: unit userdata.
2527  * - Arg 2: string.
2528  */
2530 {
2531  unit& u = luaW_checkunit(L, 1);
2532  char const *m = luaL_checkstring(L, 2);
2533  const unit_type *utp = unit_types.find(m);
2534  if (!utp) return luaL_argerror(L, 2, "unknown unit type");
2535  u.advance_to(*utp);
2536 
2537  return 0;
2538 }
2539 
2540 /**
2541  * Puts a table at the top of the stack with some combat result.
2542  */
2543 static void luaW_pushsimdata(lua_State *L, const combatant &cmb)
2544 {
2545  int n = cmb.hp_dist.size();
2546  lua_createtable(L, 0, 4);
2547  lua_pushnumber(L, cmb.poisoned);
2548  lua_setfield(L, -2, "poisoned");
2549  lua_pushnumber(L, cmb.slowed);
2550  lua_setfield(L, -2, "slowed");
2551  lua_pushnumber(L, cmb.untouched);
2552  lua_setfield(L, -2, "untouched");
2553  lua_pushnumber(L, cmb.average_hp());
2554  lua_setfield(L, -2, "average_hp");
2555  lua_createtable(L, n, 0);
2556  for (int i = 0; i < n; ++i) {
2557  lua_pushnumber(L, cmb.hp_dist[i]);
2558  lua_rawseti(L, -2, i);
2559  }
2560  lua_setfield(L, -2, "hp_chance");
2561 }
2562 
2563 /**
2564  * Puts a table at the top of the stack with information about the combatants' weapons.
2565  */
2567 {
2568 
2569  lua_createtable(L, 0, 16);
2570 
2571  lua_pushnumber(L, bcustats.num_blows);
2572  lua_setfield(L, -2, "num_blows");
2573  lua_pushnumber(L, bcustats.damage);
2574  lua_setfield(L, -2, "damage");
2575  lua_pushnumber(L, bcustats.chance_to_hit);
2576  lua_setfield(L, -2, "chance_to_hit");
2577  lua_pushboolean(L, bcustats.poisons);
2578  lua_setfield(L, -2, "poisons");
2579  lua_pushboolean(L, bcustats.slows);
2580  lua_setfield(L, -2, "slows");
2581  lua_pushboolean(L, bcustats.petrifies);
2582  lua_setfield(L, -2, "petrifies");
2583  lua_pushboolean(L, bcustats.plagues);
2584  lua_setfield(L, -2, "plagues");
2585  lua_pushstring(L, bcustats.plague_type.c_str());
2586  lua_setfield(L, -2, "plague_type");
2587  lua_pushboolean(L, bcustats.backstab_pos);
2588  lua_setfield(L, -2, "backstabs");
2589  lua_pushnumber(L, bcustats.rounds);
2590  lua_setfield(L, -2, "rounds");
2591  lua_pushboolean(L, bcustats.firststrike);
2592  lua_setfield(L, -2, "firststrike");
2593  lua_pushboolean(L, bcustats.drains);
2594  lua_setfield(L, -2, "drains");
2595  lua_pushnumber(L, bcustats.drain_constant);
2596  lua_setfield(L, -2, "drain_constant");
2597  lua_pushnumber(L, bcustats.drain_percent);
2598  lua_setfield(L, -2, "drain_percent");
2599 
2600 
2601  //if we called simulate_combat without giving an explicit weapon this can be useful.
2602  lua_pushnumber(L, bcustats.attack_num);
2603  lua_setfield(L, -2, "attack_num"); // DEPRECATED
2604  lua_pushnumber(L, bcustats.attack_num + 1);
2605  lua_setfield(L, -2, "number");
2606  //this is nullptr when there is no counter weapon
2607  if(bcustats.weapon != nullptr)
2608  {
2609  lua_pushstring(L, bcustats.weapon->id().c_str());
2610  lua_setfield(L, -2, "name");
2611  luaW_pushweapon(L, bcustats.weapon);
2612  lua_setfield(L, -2, "weapon");
2613  }
2614 
2615 }
2616 
2617 /**
2618  * Simulates a combat between two units.
2619  * - Arg 1: attacker userdata.
2620  * - Arg 2: optional weapon index.
2621  * - Arg 3: defender userdata.
2622  * - Arg 4: optional weapon index.
2623  *
2624  * - Ret 1: attacker results.
2625  * - Ret 2: defender results.
2626  * - Ret 3: info about the attacker weapon.
2627  * - Ret 4: info about the defender weapon.
2628  */
2630 {
2631  int arg_num = 1, att_w = -1, def_w = -1;
2632 
2633  unit_const_ptr att = luaW_checkunit(L, arg_num).shared_from_this();
2634  ++arg_num;
2635  if (lua_isnumber(L, arg_num)) {
2636  att_w = lua_tointeger(L, arg_num) - 1;
2637  if (att_w < 0 || att_w >= static_cast<int>(att->attacks().size()))
2638  return luaL_argerror(L, arg_num, "weapon index out of bounds");
2639  ++arg_num;
2640  }
2641 
2642  unit_const_ptr def = luaW_checkunit(L, arg_num).shared_from_this();
2643  ++arg_num;
2644  if (lua_isnumber(L, arg_num)) {
2645  def_w = lua_tointeger(L, arg_num) - 1;
2646  if (def_w < 0 || def_w >= static_cast<int>(def->attacks().size()))
2647  return luaL_argerror(L, arg_num, "weapon index out of bounds");
2648  ++arg_num;
2649  }
2650 
2651  battle_context context(units(), att->get_location(),
2652  def->get_location(), att_w, def_w, 0.0, nullptr, att.get(), def.get());
2653 
2654  luaW_pushsimdata(L, context.get_attacker_combatant());
2655  luaW_pushsimdata(L, context.get_defender_combatant());
2656  luaW_pushsimweapon(L, context.get_attacker_stats());
2657  luaW_pushsimweapon(L, context.get_defender_stats());
2658  return 4;
2659 }
2660 
2661 /**
2662  * Modifies the music playlist.
2663  * - Arg 1: WML table, or nil to force changes.
2664  */
2666 {
2667  deprecated_message("wesnoth.set_music", DEP_LEVEL::INDEFINITE, "", "Use the wesnoth.playlist table instead!");
2668  if (lua_isnoneornil(L, 1)) {
2670  return 0;
2671  }
2672 
2673  config cfg = luaW_checkconfig(L, 1);
2675  return 0;
2676 }
2677 
2678 /**
2679  * Plays a sound, possibly repeated.
2680  * - Arg 1: string.
2681  * - Arg 2: optional integer.
2682  */
2684 {
2685  char const *m = luaL_checkstring(L, 1);
2686  if (play_controller_.is_skipping_replay()) return 0;
2687  int repeats = lua_tointeger(L, 2);
2688  sound::play_sound(m, sound::SOUND_FX, repeats);
2689  return 0;
2690 }
2691 
2692 /**
2693  * Gets/sets the current sound volume
2694  * - Arg 1: (optional) New volume to set
2695  * - Return: Original volume
2696  */
2698 {
2699  int vol = preferences::sound_volume();
2700  lua_pushnumber(L, sound::get_sound_volume() * 100.0 / vol);
2701  if(lua_isnumber(L, 1)) {
2702  float rel = lua_tonumber(L, 1);
2703  if(rel < 0.0f || rel > 100.0f) {
2704  return luaL_argerror(L, 1, "volume must be in range 0..100");
2705  }
2706  vol = static_cast<int>(rel*vol / 100.0f);
2708  }
2709  return 1;
2710 }
2711 
2712 /**
2713  * Scrolls to given tile.
2714  * - Arg 1: location.
2715  * - Arg 2: boolean preventing scroll to fog.
2716  * - Arg 3: boolean specifying whether to warp instantly.
2717  * - Arg 4: boolean specifying whether to skip if already onscreen
2718  */
2720 {
2721  map_location loc = luaW_checklocation(L, 1);
2722  bool check_fogged = luaW_toboolean(L, 2);
2724  ? luaW_toboolean(L, 3)
2727  : luaW_toboolean(L, 3)
2730  ;
2731  if (game_display_) {
2732  game_display_->scroll_to_tile(loc, scroll, check_fogged);
2733  }
2734  return 0;
2735 }
2736 
2738 {
2739  events::command_disabler command_disabler;
2740  deprecated_message("wesnoth.select_hex", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use wesnoth.select_unit and/or wesnoth.highlight_hex instead.");
2741 
2742  // Need this because check_location may change the stack
2743  // By doing this now, we ensure that it won't do so when
2744  // intf_select_unit and intf_highlight_hex call it.
2745  const map_location loc = luaW_checklocation(L, 1);
2746  luaW_pushlocation(L, loc);
2747  lua_replace(L, 1);
2748 
2749  intf_select_unit(L);
2750  if(!lua_isnoneornil(L, 2) && luaW_toboolean(L,2)) {
2751  intf_highlight_hex(L);
2752  }
2753  return 0;
2754 }
2755 
2756 /**
2757  * Selects and highlights the given location on the map.
2758  * - Arg 1: location.
2759  * - Args 2,3: booleans
2760  */
2762 {
2763  events::command_disabler command_disabler;
2764  if(lua_isnoneornil(L, 1)) {
2765  play_controller_.get_mouse_handler_base().select_hex(map_location::null_location(), false, false, false);
2766  return 0;
2767  }
2768  const map_location loc = luaW_checklocation(L, 1);
2769  if(!map().on_board(loc)) return luaL_argerror(L, 1, "not on board");
2770  bool highlight = true;
2771  if(!lua_isnoneornil(L, 2))
2772  highlight = luaW_toboolean(L, 2);
2773  const bool fire_event = luaW_toboolean(L, 3);
2774  play_controller_.get_mouse_handler_base().select_hex(
2775  loc, false, highlight, fire_event);
2776  return 0;
2777 }
2778 
2779 /**
2780  * Deselects any highlighted hex on the map.
2781  * No arguments or return values
2782  */
2784 {
2785  if(game_display_) {
2786  game_display_->highlight_hex(map_location::null_location());
2787  }
2788 
2789  return 0;
2790 }
2791 
2792 /**
2793  * Return true if a replay is in progress but the player has chosen to skip it
2794  */
2796 {
2797  bool skipping = play_controller_.is_skipping_replay() || play_controller_.is_skipping_story();
2798  if (!skipping) {
2799  skipping = game_state_.events_manager_->pump().context_skip_messages();
2800  }
2801  lua_pushboolean(L, skipping);
2802  return 1;
2803 }
2804 
2805 /**
2806  * Set whether to skip messages
2807  * Arg 1 (optional) - boolean
2808  */
2810 {
2811  bool skip = true;
2812  if (!lua_isnone(L, 1)) {
2813  skip = luaW_toboolean(L, 1);
2814  }
2815  game_state_.events_manager_->pump().context_skip_messages(skip);
2816  return 0;
2817 }
2818 
2819 namespace
2820 {
2821  struct lua_synchronize : mp_sync::user_choice
2822  {
2823  lua_State *L;
2824  int user_choice_index;
2825  int random_choice_index;
2826  int ai_choice_index;
2827  std::string desc;
2828  lua_synchronize(lua_State *l, const std::string& descr, int user_index, int random_index = 0, int ai_index = 0)
2829  : L(l)
2830  , user_choice_index(user_index)
2831  , random_choice_index(random_index)
2832  , ai_choice_index(ai_index != 0 ? ai_index : user_index)
2833  , desc(descr)
2834  {}
2835 
2836  virtual config query_user(int side) const override
2837  {
2838  bool is_local_ai = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).board().get_team(side).is_local_ai();
2839  config cfg;
2840  query_lua(side, is_local_ai ? ai_choice_index : user_choice_index, cfg);
2841  return cfg;
2842  }
2843 
2844  virtual config random_choice(int side) const override
2845  {
2846  config cfg;
2847  if(random_choice_index != 0 && lua_isfunction(L, random_choice_index)) {
2848  query_lua(side, random_choice_index, cfg);
2849  }
2850  return cfg;
2851  }
2852 
2853  virtual std::string description() const override
2854  {
2855  return desc;
2856  }
2857 
2858  void query_lua(int side, int function_index, config& cfg) const
2859  {
2860  assert(cfg.empty());
2861  lua_pushvalue(L, function_index);
2862  lua_pushnumber(L, side);
2863  if (luaW_pcall(L, 1, 1, false)) {
2864  if(!luaW_toconfig(L, -1, cfg)) {
2865  lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).log_error("function returned to wesnoth.synchronize_choice a table which was partially invalid");
2866  }
2867  }
2868  }
2869  //Although lua's sync_choice can show a dialog, (and will in most cases)
2870  //we return false to enable other possible things that do not contain UI things.
2871  //it's in the responsibility of the umc dev to not show dialogs during prestart events.
2872  virtual bool is_visible() const override { return false; }
2873  };
2874 }//unnamed namespace for lua_synchronize
2875 
2876 /**
2877  * Ensures a value is synchronized among all the clients.
2878  * - Arg 1: optional string specifying the type id of the choice.
2879  * - Arg 2: function to compute the value, called if the client is the master.
2880  * - Arg 3: optional function, called instead of the first function if the user is not human.
2881  * - Arg 4: optional integer specifying, on which side the function should be evaluated.
2882  * - Ret 1: WML table returned by the function.
2883  */
2885 {
2886  std::string tagname = "input";
2887  t_string desc = _("input");
2888  int human_func = 0;
2889  int ai_func = 0;
2890  int side_for;
2891 
2892  int nextarg = 1;
2893  if(!lua_isfunction(L, nextarg) && luaW_totstring(L, nextarg, desc) ) {
2894  ++nextarg;
2895  }
2896  if(lua_isfunction(L, nextarg)) {
2897  human_func = nextarg++;
2898  }
2899  else {
2900  return luaL_argerror(L, nextarg, "expected a function");
2901  }
2902  if(lua_isfunction(L, nextarg)) {
2903  ai_func = nextarg++;
2904  }
2905  side_for = lua_tointeger(L, nextarg);
2906 
2907  config cfg = mp_sync::get_user_choice(tagname, lua_synchronize(L, desc, human_func, 0, ai_func), side_for);
2908  luaW_pushconfig(L, cfg);
2909  return 1;
2910 }
2911 /**
2912  * Ensures a value is synchronized among all the clients.
2913  * - Arg 1: optional string the id of this type of user input, may only contain characters a-z and '_'
2914  * - Arg 2: function to compute the value, called if the client is the master.
2915  * - Arg 3: an optional function to compute the value, if the side was null/empty controlled.
2916  * - Arg 4: an array of integers specifying, on which side the function should be evaluated.
2917  * - Ret 1: a map int -> WML tabls.
2918  */
2920 {
2921  std::string tagname = "input";
2922  t_string desc = _("input");
2923  int human_func = 0;
2924  int null_func = 0;
2925  std::vector<int> sides_for;
2926 
2927  int nextarg = 1;
2928  if(!lua_isfunction(L, nextarg) && luaW_totstring(L, nextarg, desc) ) {
2929  ++nextarg;
2930  }
2931  if(lua_isfunction(L, nextarg)) {
2932  human_func = nextarg++;
2933  }
2934  else {
2935  return luaL_argerror(L, nextarg, "expected a function");
2936  }
2937  if(lua_isfunction(L, nextarg)) {
2938  null_func = nextarg++;
2939  };
2940  sides_for = lua_check<std::vector<int>>(L, nextarg++);
2941 
2942  lua_push(L, mp_sync::get_user_choice_multiple_sides(tagname, lua_synchronize(L, desc, human_func, null_func), std::set<int>(sides_for.begin(), sides_for.end())));
2943  return 1;
2944 }
2945 
2946 
2947 /**
2948  * Calls a function in an unsynced context (this specially means that all random calls used by that function will be unsynced).
2949  * This is usually used together with an unsynced if like 'if controller != network'
2950  * - Arg 1: function that will be called during the unsynced context.
2951  */
2953 {
2954  set_scontext_unsynced sync;
2955  lua_pushvalue(L, 1);
2956  luaW_pcall(L, 0, 0, false);
2957  return 0;
2958 }
2959 
2960 /**
2961  * Gets all the locations matching a given filter.
2962  * - Arg 1: WML table.
2963  * - Arg 2: Optional reference unit (teleport_unit)
2964  * - Ret 1: array of integer pairs.
2965  */
2967 {
2968  vconfig filter = luaW_checkvconfig(L, 1);
2969 
2970  std::set<map_location> res;
2971  filter_context & fc = game_state_;
2972  const terrain_filter t_filter(filter, &fc);
2973  if(luaW_isunit(L, 2)) {
2974  t_filter.get_locations(res, *luaW_tounit(L, 2), true);
2975  } else {
2976  t_filter.get_locations(res, true);
2977  }
2978 
2979  lua_createtable(L, res.size(), 0);
2980  int i = 1;
2981  for (const map_location& loc : res)
2982  {
2983  lua_createtable(L, 2, 0);
2984  lua_pushinteger(L, loc.wml_x());
2985  lua_rawseti(L, -2, 1);
2986  lua_pushinteger(L, loc.wml_y());
2987  lua_rawseti(L, -2, 2);
2988  lua_rawseti(L, -2, i);
2989  ++i;
2990  }
2991  return 1;
2992 }
2993 
2994 /**
2995  * Gets all the villages matching a given filter, or all the villages on the map if no filter is given.
2996  * - Arg 1: WML table (optional).
2997  * - Ret 1: array of integer pairs.
2998  */
3000 {
3001  std::vector<map_location> locs = map().villages();
3002  lua_newtable(L);
3003  int i = 1;
3004 
3005  vconfig filter = luaW_checkvconfig(L, 1);
3006 
3007  filter_context & fc = game_state_;
3008  for(std::vector<map_location>::const_iterator it = locs.begin(); it != locs.end(); ++it) {
3009  bool matches = terrain_filter(filter, &fc).match(*it);
3010  if (matches) {
3011  lua_createtable(L, 2, 0);
3012  lua_pushinteger(L, it->wml_x());
3013  lua_rawseti(L, -2, 1);
3014  lua_pushinteger(L, it->wml_y());
3015  lua_rawseti(L, -2, 2);
3016  lua_rawseti(L, -2, i);
3017  ++i;
3018  }
3019  }
3020  return 1;
3021 }
3022 
3023 /**
3024  * Matches a location against the given filter.
3025  * - Arg 1: location.
3026  * - Arg 2: WML table.
3027  * - Arg 3: Optional reference unit (teleport_unit)
3028  * - Ret 1: boolean.
3029  */
3031 {
3032  map_location loc = luaW_checklocation(L, 1);
3033  vconfig filter = luaW_checkvconfig(L, 2, true);
3034 
3035  if (filter.null()) {
3036  lua_pushboolean(L, true);
3037  return 1;
3038  }
3039 
3040  filter_context & fc = game_state_;
3041  const terrain_filter t_filter(filter, &fc);
3042  if(luaW_isunit(L, 3)) {
3043  lua_pushboolean(L, t_filter.match(loc, *luaW_tounit(L, 3)));
3044  } else {
3045  lua_pushboolean(L, t_filter.match(loc));
3046  }
3047  return 1;
3048 }
3049 
3050 
3051 
3052 /**
3053  * Matches a side against the given filter.
3054  * - Args 1: side number.
3055  * - Arg 2: WML table.
3056  * - Ret 1: boolean.
3057  */
3059 {
3060  vconfig filter = luaW_checkvconfig(L, 2, true);
3061 
3062  if (filter.null()) {
3063  lua_pushboolean(L, true);
3064  return 1;
3065  }
3066 
3067  filter_context & fc = game_state_;
3068  side_filter s_filter(filter, &fc);
3069 
3070  if(team* t = luaW_toteam(L, 1)) {
3071  lua_pushboolean(L, s_filter.match(*t));
3072  } else {
3073  unsigned side = luaL_checkinteger(L, 1) - 1;
3074  if (side >= teams().size()) return 0;
3075  lua_pushboolean(L, s_filter.match(side + 1));
3076  }
3077  return 1;
3078 }
3079 
3081 {
3082  int team_i = luaL_checkinteger(L, 1) - 1;
3083  std::string flag = luaL_optlstring(L, 2, "", nullptr);
3084  std::string color = luaL_optlstring(L, 3, "", nullptr);
3085 
3086  if(flag.empty() && color.empty()) {
3087  return 0;
3088  }
3089  if(team_i < 0 || static_cast<std::size_t>(team_i) >= teams().size()) {
3090  return luaL_error(L, "set_side_id: side number %d out of range", team_i);
3091  }
3092  team& side = teams()[team_i];
3093 
3094  if(!color.empty()) {
3095  side.set_color(color);
3096  }
3097  if(!flag.empty()) {
3098  side.set_flag(flag);
3099  }
3100 
3101  game_display_->reinit_flags_for_side(team_i);
3102  return 0;
3103 }
3104 
3105 static int intf_modify_ai(lua_State *L, const char* action)
3106 {
3107  int side_num = luaL_checkinteger(L, 1);
3108  std::string path = luaL_checkstring(L, 2);
3109  config cfg {
3110  "action", action,
3111  "path", path
3112  };
3113  if(strcmp(action, "delete") == 0) {
3115  return 0;
3116  }
3117  config component = luaW_checkconfig(L, 3);
3118  std::size_t len = std::string::npos, open_brak = path.find_last_of('[');
3119  std::size_t dot = path.find_last_of('.');
3120  if(open_brak != len) {
3121  len = open_brak - dot - 1;
3122  }
3123  cfg.add_child(path.substr(dot + 1, len), component);
3125  return 0;
3126 }
3127 
3129 {
3130  int side_num = luaL_checkinteger(L, 1);
3131  if(lua_isstring(L, 2)) {
3132  std::string file = luaL_checkstring(L, 2);
3133  if(!ai::manager::get_singleton().add_ai_for_side_from_file(side_num, file)) {
3134  std::string err = formatter() << "Could not load AI for side " << side_num << " from file " << file;
3135  lua_pushlstring(L, err.c_str(), err.length());
3136  return lua_error(L);
3137  }
3138  } else {
3140  }
3141  return 0;
3142 }
3143 
3145 {
3146  int side_num = luaL_checkinteger(L, 1);
3147  config cfg = luaW_checkconfig(L, 2);
3148  if(!cfg.has_child("ai")) {
3149  cfg = config {"ai", cfg};
3150  }
3151  bool added_dummy_stage = false;
3152  if(!cfg.child("ai").has_child("stage")) {
3153  added_dummy_stage = true;
3154  cfg.child("ai").add_child("stage", config {"name", "empty"});
3155  }
3157  if(added_dummy_stage) {
3158  for(auto iter = cfg.ordered_begin(); iter != cfg.ordered_end(); iter++) {
3159  if(iter->key == "stage" && iter->cfg["name"] == "empty") {
3160  iter = cfg.erase(iter);
3161  }
3162  }
3163  }
3165  return 0;
3166 }
3167 
3168 /**
3169  * Returns a proxy table array for all sides matching the given SSF.
3170  * - Arg 1: SSF
3171  * - Ret 1: proxy table array
3172  */
3174 {
3175  LOG_LUA << "intf_get_sides called: this = " << std::hex << this << std::dec << " myname = " << my_name() << std::endl;
3176  std::vector<int> sides;
3177  const vconfig ssf = luaW_checkvconfig(L, 1, true);
3178  if(ssf.null()) {
3179  for (unsigned side_number = 1; side_number <= teams().size(); ++side_number) {
3180  sides.push_back(side_number);
3181  }
3182  } else {
3183  filter_context & fc = game_state_;
3184 
3185  side_filter filter(ssf, &fc);
3186  sides = filter.get_teams();
3187  }
3188 
3189  lua_settop(L, 0);
3190  lua_createtable(L, sides.size(), 0);
3191  unsigned index = 1;
3192  for(int side : sides) {
3193  luaW_pushteam(L, board().get_team(side));
3194  lua_rawseti(L, -2, index);
3195  ++index;
3196  }
3197 
3198  return 1;
3199 }
3200 
3201 /**
3202  * .Returns information about the global traits known to the engine.
3203  * - Ret 1: Table with named fields holding wml tables describing the traits.
3204  */
3206 {
3207  lua_newtable(L);
3208  for(const config& trait : unit_types.traits()) {
3209  const std::string& id = trait["id"];
3210  //It seems the engine does nowhere check the id field for emptyness or duplicates
3211  //(also not later on).
3212  //However, the worst thing to happen is that the trait read later overwrites the older one,
3213  //and this is not the right place for such checks.
3214  lua_pushstring(L, id.c_str());
3215  luaW_pushconfig(L, trait);
3216  lua_rawset(L, -3);
3217  }
3218  return 1;
3219 }
3220 
3221 /**
3222  * Adds a modification to a unit.
3223  * - Arg 1: unit.
3224  * - Arg 2: string.
3225  * - Arg 3: WML table.
3226  * - Arg 4: (optional) Whether to add to [modifications] - default true
3227  */
3229 {
3230  unit& u = luaW_checkunit(L, 1);
3231  char const *m = luaL_checkstring(L, 2);
3232  std::string sm = m;
3233  if (sm == "advance") { // Maintain backwards compatibility
3234  sm = "advancement";
3235  deprecated_message("\"advance\" modification type", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use \"advancement\" instead.");
3236  }
3237  if (sm != "advancement" && sm != "object" && sm != "trait") {
3238  return luaL_argerror(L, 2, "unknown modification type");
3239  }
3240  bool write_to_mods = true;
3241  if (!lua_isnone(L, 4)) {
3242  write_to_mods = luaW_toboolean(L, 4);
3243  }
3244  if(sm.empty()) {
3245  write_to_mods = false;
3246  }
3247 
3248  config cfg = luaW_checkconfig(L, 3);
3249  u.add_modification(sm, cfg, !write_to_mods);
3250  return 0;
3251 }
3252 
3253 /**
3254  * Removes modifications from a unit
3255  * - Arg 1: unit
3256  * - Arg 2: table (filter as [filter_wml])
3257  * - Arg 3: type of modification (default "object")
3258  */
3260 {
3261  unit& u = luaW_checkunit(L, 1);
3262  config filter = luaW_checkconfig(L, 2);
3263  std::string tag = luaL_optstring(L, 3, "object");
3264  //TODO
3265  if(filter.attribute_count() == 1 && filter.all_children_count() == 0 && filter.attribute_range().front().first == "duration") {
3266  u.expire_modifications(filter["duration"]);
3267  } else {
3268  for(config& obj : u.get_modifications().child_range(tag)) {
3269  if(obj.matches(filter)) {
3270  obj["duration"] = "now";
3271  }
3272  }
3273  u.expire_modifications("now");
3274  }
3275  return 0;
3276 }
3277 
3278 /**
3279  * Advances a unit if the unit has enough xp.
3280  * - Arg 1: unit.
3281  * - Arg 2: optional boolean whether to animate the advancement.
3282  * - Arg 3: optional boolean whether to fire advancement events.
3283  */
3285 {
3286  events::command_disabler command_disabler;
3287  //TODO: check whether the unit is on the map.
3288  unit& u = luaW_checkunit(L, 1, true);
3290  if(lua_isboolean(L, 2)) {
3291  par.animate(luaW_toboolean(L, 2));
3292  }
3293  if(lua_isboolean(L, 3)) {
3294  par.fire_events(luaW_toboolean(L, 3));
3295  }
3296  advance_unit_at(par);
3297  return 0;
3298 }
3299 
3300 
3301 /**
3302  * Adds a new known unit type to the help system.
3303  * - Arg 1: string.
3304  */
3306 {
3307  char const *ty = luaL_checkstring(L, 1);
3308  if(!unit_types.find(ty))
3309  {
3310  std::stringstream ss;
3311  ss << "unknown unit type: '" << ty << "'";
3312  return luaL_argerror(L, 1, ss.str().c_str());
3313  }
3314  preferences::encountered_units().insert(ty);
3315  return 0;
3316 }
3317 
3318 /**
3319  * Adds an overlay on a tile.
3320  * - Arg 1: location.
3321  * - Arg 2: WML table.
3322  */
3324 {
3325  map_location loc = luaW_checklocation(L, 1);
3326  vconfig cfg = luaW_checkvconfig(L, 2);
3327  const vconfig &ssf = cfg.child("filter_team");
3328 
3329  std::string team_name;
3330  if (!ssf.null()) {
3331  const std::vector<int>& teams = side_filter(ssf, &game_state_).get_teams();
3332  std::vector<std::string> team_names;
3333  std::transform(teams.begin(), teams.end(), std::back_inserter(team_names),
3334  [&](int team) { return game_state_.get_disp_context().get_team(team).team_name(); });
3335  team_name = utils::join(team_names);
3336  } else {
3337  team_name = cfg["team_name"].str();
3338  }
3339 
3340  if (game_display_) {
3341  game_display_->add_overlay(loc, cfg["image"], cfg["halo"],
3342  team_name, cfg["name"], cfg["visible_in_fog"].to_bool(true), cfg["z_order"].to_double(0));
3343  }
3344  return 0;
3345 }
3346 
3347 /**
3348  * Removes an overlay from a tile.
3349  * - Arg 1: location.
3350  * - Arg 2: optional string.
3351  */
3353 {
3354  map_location loc = luaW_checklocation(L, 1);
3355  char const *m = lua_tostring(L, 2);
3356 
3357  if (m) {
3358  if (game_display_) {
3359  game_display_->remove_single_overlay(loc, m);
3360  }
3361  } else {
3362  if (game_display_) {
3363  game_display_->remove_overlay(loc);
3364  }
3365  }
3366  return 0;
3367 }
3368 
3370 {
3371  replay& recorder = play_controller_.get_replay();
3372  const int nargs = lua_gettop(L);
3373  if(nargs < 2 || nargs > 3) {
3374  return luaL_error(L, "Wrong number of arguments to ai.log_replay() - should be 2 or 3 arguments.");
3375  }
3376  const std::string key = nargs == 2 ? luaL_checkstring(L, 1) : luaL_checkstring(L, 2);
3377  config cfg;
3378  if(nargs == 2) {
3379  recorder.add_log_data(key, luaL_checkstring(L, 2));
3380  } else if(luaW_toconfig(L, 3, cfg)) {
3381  recorder.add_log_data(luaL_checkstring(L, 1), key, cfg);
3382  } else if(!lua_isstring(L, 3)) {
3383  return luaL_argerror(L, 3, "accepts only string or config");
3384  } else {
3385  recorder.add_log_data(luaL_checkstring(L, 1), key, luaL_checkstring(L, 3));
3386  }
3387  return 0;
3388 }
3389 
3390 /// Adding new events
3392 {
3393  vconfig cfg(luaW_checkvconfig(L, 1));
3394  game_events::manager & man = *game_state_.events_manager_;
3395 
3396  if (!cfg["delayed_variable_substitution"].to_bool(true)) {
3398  } else {
3399  man.add_event_handler(cfg.get_config());
3400  }
3401  return 0;
3402 }
3403 
3405 {
3406  game_state_.events_manager_->remove_event_handler(luaL_checkstring(L, 1));
3407  return 0;
3408 }
3409 
3411 {
3412  if (game_display_) {
3413  vconfig cfg(luaW_checkvconfig(L, 1));
3414 
3415  game_display_->adjust_color_overlay(cfg["red"], cfg["green"], cfg["blue"]);
3416  game_display_->invalidate_all();
3417  game_display_->draw(true,true);
3418  }
3419  return 0;
3420 }
3421 
3422 /**
3423  * Delays engine for a while.
3424  * - Arg 1: integer.
3425  * - Arg 2: boolean (optional).
3426  */
3428 {
3429  if(gamedata().phase() == game_data::PRELOAD || gamedata().phase() == game_data::PRESTART || gamedata().phase() == game_data::INITIAL) {
3430  //don't call play_slice if the game ui is not active yet.
3431  return 0;
3432  }
3433  events::command_disabler command_disabler;
3434  lua_Integer delay = luaL_checkinteger(L, 1);
3435  if(delay == 0) {
3436  play_controller_.play_slice(false);
3437  return 0;
3438  }
3439  if(luaW_toboolean(L, 2) && game_display_ && game_display_->turbo_speed() > 0) {
3440  delay /= game_display_->turbo_speed();
3441  }
3442  const unsigned final = SDL_GetTicks() + delay;
3443  do {
3444  play_controller_.play_slice(false);
3445  CVideo::delay(10);
3446  } while (static_cast<int>(final - SDL_GetTicks()) > 0);
3447  return 0;
3448 }
3449 
3451 {
3452  if (game_display_) {
3453  vconfig cfg(luaW_checkvconfig(L, 1));
3454 
3455  game_display &screen = *game_display_;
3456 
3457  terrain_label label(screen.labels(), cfg.get_config());
3458 
3459  screen.labels().set_label(label.location(), label.text(), label.creator(), label.team_name(), label.color(),
3460  label.visible_in_fog(), label.visible_in_shroud(), label.immutable(), label.category(), label.tooltip());
3461  }
3462  return 0;
3463 }
3464 
3466 {
3467  if (game_display_) {
3468  game_display & screen = *game_display_;
3469 
3470  vconfig cfg(luaW_checkvconfig(L, 1));
3471  bool clear_shroud(luaW_toboolean(L, 2));
3472 
3473  // We do this twice so any applicable redraws happen both before and after
3474  // any events caused by redrawing shroud are fired
3475  bool result = screen.maybe_rebuild();
3476  if (!result) {
3477  screen.invalidate_all();
3478  }
3479 
3480  if (clear_shroud) {
3481  side_filter filter(cfg, &game_state_);
3482  for (const int side : filter.get_teams()){
3483  actions::clear_shroud(side);
3484  }
3485  screen.recalculate_minimap();
3486  }
3487 
3488  result = screen.maybe_rebuild();
3489  if (!result) {
3490  screen.invalidate_all();
3491  }
3492 
3493  screen.draw(true,true);
3494  }
3495  return 0;
3496 }
3497 
3498 /**
3499  * Lua frontend to the modify_ai functionality
3500  * - Arg 1: config.
3501  */
3503 {
3504  config cfg;
3505  luaW_toconfig(L, 1, cfg);
3506  int side = cfg["side"];
3507  deprecated_message("wesnoth.modify_ai", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use wesnoth.add_ai_component, wesnoth.delete_ai_component, or wesnoth.change_ai_component.");
3509  return 0;
3510 }
3511 
3513 {
3514  bool exec = luaW_toboolean(L, -1);
3515  lua_pop(L, 1);
3516 
3517  lua_getfield(L, -1, "ca_ptr");
3518 
3519  ai::candidate_action *ca = static_cast<ai::candidate_action*>(lua_touserdata(L, -1));
3520  lua_pop(L, 2);
3521  if (exec) {
3522  ca->execute();
3523  return 0;
3524  }
3525  lua_pushinteger(L, ca->evaluate());
3526  return 1;
3527 }
3528 
3530 {
3531  lua_getfield(L, -1, "stg_ptr");
3532  ai::stage *stg = static_cast<ai::stage*>(lua_touserdata(L, -1));
3533  lua_pop(L, 2);
3534  stg->play_stage();
3535  return 0;
3536 }
3537 
3538 static void push_component(lua_State *L, ai::component* c, const std::string &ct = "")
3539 {
3540  lua_createtable(L, 0, 0); // Table for a component
3541 
3542  lua_pushstring(L, "name");
3543  lua_pushstring(L, c->get_name().c_str());
3544  lua_rawset(L, -3);
3545 
3546  lua_pushstring(L, "engine");
3547  lua_pushstring(L, c->get_engine().c_str());
3548  lua_rawset(L, -3);
3549 
3550  lua_pushstring(L, "id");
3551  lua_pushstring(L, c->get_id().c_str());
3552  lua_rawset(L, -3);
3553 
3554  if (ct == "candidate_action") {
3555  lua_pushstring(L, "ca_ptr");
3556  lua_pushlightuserdata(L, c);
3557  lua_rawset(L, -3);
3558 
3559  lua_pushstring(L, "exec");
3561  lua_rawset(L, -3);
3562  }
3563 
3564  if (ct == "stage") {
3565  lua_pushstring(L, "stg_ptr");
3566  lua_pushlightuserdata(L, c);
3567  lua_rawset(L, -3);
3568 
3569  lua_pushstring(L, "exec");
3571  lua_rawset(L, -3);
3572  }
3573 
3574 
3575  std::vector<std::string> c_types = c->get_children_types();
3576 
3577  for (std::vector<std::string>::const_iterator t = c_types.begin(); t != c_types.end(); ++t)
3578  {
3579  std::vector<ai::component*> children = c->get_children(*t);
3580  std::string type = *t;
3581  if (type == "aspect" || type == "goal" || type == "engine")
3582  {
3583  continue;
3584  }
3585 
3586  lua_pushstring(L, type.c_str());
3587  lua_createtable(L, 0, 0); // this table will be on top of the stack during recursive calls
3588 
3589  for (std::vector<ai::component*>::const_iterator i = children.begin(); i != children.end(); ++i)
3590  {
3591  lua_pushstring(L, (*i)->get_name().c_str());
3592  push_component(L, *i, type);
3593  lua_rawset(L, -3);
3594 
3595  //if (type == "candidate_action")
3596  //{
3597  // ai::candidate_action *ca = dynamic_cast<ai::candidate_action*>(*i);
3598  // ca->execute();
3599  //}
3600  }
3601 
3602  lua_rawset(L, -3); // setting the child table
3603  }
3604 
3605 
3606 }
3607 
3608 /**
3609  * Debug access to the ai tables
3610  * - Arg 1: int
3611  * - Ret 1: ai table
3612  */
3613 static int intf_debug_ai(lua_State *L)
3614 {
3615  if (!game_config::debug) { // This function works in debug mode only
3616  return 0;
3617  }
3618  int side = lua_tointeger(L, 1);
3619  lua_pop(L, 1);
3620 
3622 
3623  // Bad, but works
3624  std::vector<ai::component*> engines = c->get_children("engine");
3625  ai::engine_lua* lua_engine = nullptr;
3626  for (std::vector<ai::component*>::const_iterator i = engines.begin(); i != engines.end(); ++i)
3627  {
3628  if ((*i)->get_name() == "lua")
3629  {
3630  lua_engine = dynamic_cast<ai::engine_lua *>(*i);
3631  }
3632  }
3633 
3634  // Better way, but doesn't work
3635  //ai::component* e = ai::manager::get_singleton().get_active_ai_holder_for_side_dbg(side).get_component(c, "engine[lua]");
3636  //ai::engine_lua* lua_engine = dynamic_cast<ai::engine_lua *>(e);
3637 
3638  if (lua_engine == nullptr)
3639  {
3640  //no lua engine is defined for this side.
3641  //so set up a dummy engine
3642 
3643  ai::ai_composite * ai_ptr = dynamic_cast<ai::ai_composite *>(c);
3644 
3645  assert(ai_ptr);
3646 
3647  ai::ai_context& ai_context = ai_ptr->get_ai_context();
3649 
3650  lua_engine = new ai::engine_lua(ai_context, cfg);
3651  LOG_LUA << "Created new dummy lua-engine for debug_ai(). \n";
3652 
3653  //and add the dummy engine as a component
3654  //to the manager, so we could use it later
3655  cfg.add_child("engine", lua_engine->to_config());
3656  ai::component_manager::add_component(c, "engine[]", cfg);
3657  }
3658 
3659  lua_engine->push_ai_table(); // stack: [-1: ai_context]
3660 
3661  lua_pushstring(L, "components");
3662  push_component(L, c); // stack: [-1: component tree; -2: ai context]
3663  lua_rawset(L, -3);
3664 
3665  return 1;
3666 }
3667 
3668 /// Allow undo sets the flag saying whether the event has mutated the game to false.
3670 {
3671  bool allow;
3672  t_string reason;
3673  // The extra iststring is required to prevent totstring from converting a bool value
3674  if(luaW_iststring(L, 1) && luaW_totstring(L, 1, reason)) {
3675  allow = false;
3676  } else {
3677  allow = luaW_toboolean(L, 1);
3678  luaW_totstring(L, 2, reason);
3679  }
3680  gamedata().set_allow_end_turn(allow, reason);
3681  return 0;
3682 }
3683 
3684 /// Allow undo sets the flag saying whether the event has mutated the game to false.
3686 {
3687  if(lua_isboolean(L, 1)) {
3688  play_controller_.pump().set_undo_disabled(!luaW_toboolean(L, 1));
3689  }
3690  else {
3691  play_controller_.pump().set_undo_disabled(false);
3692  }
3693  return 0;
3694 }
3695 
3697 {
3698  play_controller_.pump().set_action_canceled();
3699  return 0;
3700 }
3701 
3702 /// Adding new time_areas dynamically with Standard Location Filters.
3704 {
3705  log_scope("time_area");
3706 
3707  vconfig cfg(luaW_checkvconfig(L, 1));
3708  const std::string id = cfg["id"];
3709 
3710  std::set<map_location> locs;
3711  const terrain_filter filter(cfg, &game_state_);
3712  filter.get_locations(locs, true);
3713  config parsed_cfg = cfg.get_parsed_config();
3714  tod_man().add_time_area(id, locs, parsed_cfg);
3715  LOG_LUA << "Lua inserted time_area '" << id << "'\n";
3716  return 0;
3717 }
3718 
3719 /// Removing new time_areas dynamically with Standard Location Filters.
3721 {
3722  log_scope("remove_time_area");
3723 
3724  const char * id = luaL_checkstring(L, 1);
3725  tod_man().remove_time_area(id);
3726  LOG_LUA << "Lua removed time_area '" << id << "'\n";
3727 
3728  return 0;
3729 }
3730 
3731 /// Replacing the current time of day schedule.
3733 {
3734  vconfig cfg = luaW_checkvconfig(L, 1);
3735 
3736  if(cfg.get_children("time").empty()) {
3737  ERR_LUA << "attempted to to replace ToD schedule with empty schedule" << std::endl;
3738  } else {
3739  tod_man().replace_schedule(cfg.get_parsed_config());
3740  if (game_display_) {
3741  game_display_->new_turn();
3742  }
3743  LOG_LUA << "replaced ToD schedule\n";
3744  }
3745  return 0;
3746 }
3747 
3749 {
3750  if(!game_display_) {
3751  return 0;
3752  }
3753  std::string area_id;
3754  std::size_t area_i = 0;
3755  if (lua_isstring(L, 2)) {
3756  area_id = lua_tostring(L, 1);
3757  std::vector<std::string> area_ids = tod_man().get_area_ids();
3758  area_i = std::distance(area_ids.begin(), std::find(area_ids.begin(), area_ids.end(), area_id));
3759  if(area_i >= area_ids.size()) {
3760  return luaL_argerror(L, 1, "invalid time area ID");
3761  }
3762  }
3763  int is_num = false;
3764  int new_time = lua_tonumberx(L, 1, &is_num) - 1;
3765  const std::vector<time_of_day>& times = area_id.empty()
3766  ? tod_man().times()
3767  : tod_man().times(area_i);
3768  int num_times = times.size();
3769  if(!is_num) {
3770  std::string time_id = luaL_checkstring(L, 1);
3771  new_time = 0;
3772  for(const time_of_day& time : times) {
3773  if(time_id == time.id) {
3774  break;
3775  }
3776  new_time++;
3777  }
3778  if(new_time >= num_times) {
3779  return luaL_argerror(L, 1, "invalid time of day ID");
3780  }
3781  }
3782  if(new_time < 0 || new_time >= num_times) {
3783  return luaL_argerror(L, 1, "invalid time of day index");
3784  }
3785 
3786  if(area_id.empty()) {
3787  tod_man().set_current_time(new_time);
3788  } else {
3789  tod_man().set_current_time(new_time, area_i);
3790  }
3791  return 0;
3792 }
3793 
3795 {
3796  int x = luaL_checkinteger(L, 1), y = luaL_checkinteger(L, 2);
3797 
3798  if (game_display_) {
3799  game_display_->scroll(x, y, true);
3800  game_display_->draw(true, true);
3801  }
3802 
3803  return 0;
3804 }
3805 
3806 namespace {
3807  struct lua_report_generator : reports::generator
3808  {
3809  lua_State *mState;
3810  std::string name;
3811  lua_report_generator(lua_State *L, const std::string &n)
3812  : mState(L), name(n) {}
3813  virtual config generate(reports::context & rc);
3814  };
3815 
3816  config lua_report_generator::generate(reports::context & /*rc*/)
3817  {
3818  lua_State *L = mState;
3819  config cfg;
3820  if (!luaW_getglobal(L, "wesnoth", "theme_items", name))
3821  return cfg;
3822  if (!luaW_pcall(L, 0, 1)) return cfg;
3823  luaW_toconfig(L, -1, cfg);
3824  lua_pop(L, 1);
3825  return cfg;
3826  }
3827 }//unnamed namespace for lua_report_generator
3828 
3829 /**
3830  * Executes its upvalue as a theme item generator.
3831  */
3833 {
3834  reports::context temp_context = reports::context(board(), *game_display_, tod_man(), play_controller_.get_whiteboard(), play_controller_.get_mouse_handler_base());
3835  luaW_pushconfig(L, reports_.generate_report(m.c_str(), temp_context , true));
3836  return 1;
3837 }
3838 
3839 /**
3840  * Creates a field of the theme_items table and returns it (__index metamethod).
3841  */
3843 {
3844  char const *m = luaL_checkstring(L, 2);
3845  lua_cpp::push_closure(L, std::bind(&game_lua_kernel::impl_theme_item, this, _1, std::string(m)), 0);
3846  lua_pushvalue(L, 2);
3847  lua_pushvalue(L, -2);
3848  lua_rawset(L, 1);
3849  reports_.register_generator(m, new lua_report_generator(L, m));
3850  return 1;
3851 }
3852 
3853 /**
3854  * Sets a field of the theme_items table (__newindex metamethod).
3855  */
3857 {
3858  char const *m = luaL_checkstring(L, 2);
3859  lua_pushvalue(L, 2);
3860  lua_pushvalue(L, 3);
3861  lua_rawset(L, 1);
3862  reports_.register_generator(m, new lua_report_generator(L, m));
3863  return 0;
3864 }
3865 
3866 /**
3867  * Gets all the WML variables currently set.
3868  * - Ret 1: WML table
3869  */
3871  luaW_pushconfig(L, gamedata().get_variables());
3872  return 1;
3873 }
3874 
3875 /**
3876  * Teeleports a unit to a location.
3877  * Arg 1: unit
3878  * Arg 2: target location
3879  * Arg 3: bool (ignore_passability)
3880  * Arg 4: bool (clear_shroud)
3881  * Arg 5: bool (animate)
3882  */
3884 {
3885  events::command_disabler command_disabler;
3886  unit_ptr u = luaW_checkunit_ptr(L, 1, true);
3887  map_location dst = luaW_checklocation(L, 2);
3888  bool check_passability = !luaW_toboolean(L, 3);
3889  bool clear_shroud = luaW_toboolean(L, 4);
3890  bool animate = luaW_toboolean(L, 5);
3891 
3892  if (dst == u->get_location() || !map().on_board(dst)) {
3893  return 0;
3894  }
3895  const map_location vacant_dst = find_vacant_tile(dst, pathfind::VACANT_ANY, check_passability ? u.get() : nullptr);
3896  if (!map().on_board(vacant_dst)) {
3897  return 0;
3898  }
3899  // Clear the destination hex before the move (so the animation can be seen).
3900  actions::shroud_clearer clearer;
3901  if ( clear_shroud ) {
3902  clearer.clear_dest(vacant_dst, *u);
3903  }
3904 
3905  map_location src_loc = u->get_location();
3906 
3907  std::vector<map_location> teleport_path;
3908  teleport_path.push_back(src_loc);
3909  teleport_path.push_back(vacant_dst);
3910  unit_display::move_unit(teleport_path, u, animate);
3911 
3912  units().move(src_loc, vacant_dst);
3914 
3915  u = &*units().find(vacant_dst);
3916  u->anim_comp().set_standing();
3917 
3918  if ( clear_shroud ) {
3919  // Now that the unit is visibly in position, clear the shroud.
3920  clearer.clear_unit(vacant_dst, *u);
3921  }
3922 
3923  if (map().is_village(vacant_dst)) {
3924  actions::get_village(vacant_dst, u->side());
3925  }
3926 
3927  game_display_->invalidate_unit_after_move(src_loc, vacant_dst);
3928  game_display_->draw();
3929 
3930  // Sighted events.
3931  clearer.fire_events();
3932  return 0;
3933 }
3934 
3935 /**
3936  * Removes a sound source by its ID
3937  * Arg 1: sound source ID
3938  */
3940 {
3941  soundsource::manager* man = play_controller_.get_soundsource_man();
3942  std::string id = luaL_checkstring(L, 1);
3943  man->remove(id);
3944  return 0;
3945 }
3946 
3947 /**
3948  * Add a new sound source
3949  * Arg 1: Table containing keyword arguments
3950  */
3952 {
3953  soundsource::manager* man = play_controller_.get_soundsource_man();
3954  config cfg = luaW_checkconfig(L, 1);
3955  try {
3956  soundsource::sourcespec spec(cfg);
3957  man->add(spec);
3958  man->update();
3959  } catch (const bad_lexical_cast &) {
3960  ERR_LUA << "Error when parsing sound_source config: invalid parameter." << std::endl;
3961  ERR_LUA << "sound_source config was: " << cfg.debug() << std::endl;
3962  ERR_LUA << "Skipping this sound source..." << std::endl;
3963  }
3964  return 0;
3965 }
3966 
3967 /**
3968  * Get an existing sound source
3969  * Arg 1: The sound source ID
3970  * Return: Config of sound source info, or nil if it didn't exist
3971  * This is a copy of the sound source info, so you need to call
3972  * add_sound_source again after changing it.
3973  */
3975 {
3976  soundsource::manager* man = play_controller_.get_soundsource_man();
3977  std::string id = luaL_checkstring(L, 1);
3978  config cfg = man->get(id);
3979  if(cfg.empty()) {
3980  return 0;
3981  }
3982  // Sound sources do not know their own string ID
3983  // Thus, we need to add this manually
3984  cfg["id"] = id;
3985  luaW_pushconfig(L, cfg);
3986  return 1;
3987 }
3988 
3989 /**
3990  * Logs a message
3991  * Arg 1: (optional) Logger; "wml" for WML errors or deprecations
3992  * Arg 2: Message
3993  * Arg 3: Whether to print to chat (always true if arg 1 is "wml")
3994  */
3996 {
3997  const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) : "";
3998  const std::string& msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
3999 
4000  if(logger == "wml" || logger == "WML") {
4001  lg::wml_error() << msg << '\n';
4002  } else {
4003  bool in_chat = luaW_toboolean(L, -1);
4004  game_state_.events_manager_->pump().put_wml_message(logger,msg,in_chat);
4005  }
4006  return 0;
4007 }
4008 
4010 {
4011  int side = luaL_checknumber(L, 1);
4012  map_location loc = luaW_checklocation(L, 2);
4013  if(side < 1 || static_cast<std::size_t>(side) > teams().size()) {
4014  std::string error = "side " + std::to_string(side) + " does not exist";
4015  return luaL_argerror(L, 1, error.c_str());
4016  }
4017 
4018  team& t = board().get_team(side);
4019  lua_pushboolean(L, fog ? t.fogged(loc) : t.shrouded(loc));
4020  return 1;
4021 }
4022 
4023 /**
4024  * Implements the lifting and resetting of fog via WML.
4025  * Keeping affect_normal_fog as false causes only the fog override to be affected.
4026  * Otherwise, fog lifting will be implemented similar to normal sight (cannot be
4027  * individually reset and ends at the end of the turn), and fog resetting will, in
4028  * addition to removing overrides, extend the specified teams' normal fog to all
4029  * hexes.
4030  *
4031  * Arg 1: (optional) Side number, or list of side numbers
4032  * Arg 2: List of locations; each is a two-element array or a table with x and y keys
4033  * Arg 3: (optional) boolean
4034  */
4036 {
4037  bool affect_normal_fog = false;
4038  if(lua_isboolean(L, -1)) {
4039  affect_normal_fog = luaW_toboolean(L, -1);
4040  }
4041  std::set<int> sides;
4042  if(lua_isnumber(L, 1)) {
4043  sides.insert(lua_tonumber(L, 1));
4044  } else if(lua_istable(L, 1) && lua_istable(L, 2)) {
4045  const auto& v = lua_check<std::vector<int>>(L, 1);
4046  sides.insert(v.begin(), v.end());
4047  } else {
4048  for(const team& t : teams()) {
4049  sides.insert(t.side()+1);
4050  }
4051  }
4052  const auto& v_locs = lua_check<std::vector<map_location>>(L, lua_istable(L, 2) ? 2 : 1);
4053  std::set<map_location> locs(v_locs.begin(), v_locs.end());
4054 
4055  for(const int &side_num : sides) {
4056  if(side_num < 1 || static_cast<std::size_t>(side_num) > teams().size()) {
4057  continue;
4058  }
4059  team &t = board().get_team(side_num);
4060  if(!clear) {
4061  // Extend fog.
4062  t.remove_fog_override(locs);
4063  if(affect_normal_fog) {
4064  t.refog();
4065  }
4066  } else if(!affect_normal_fog) {
4067  // Force the locations clear of fog.
4068  t.add_fog_override(locs);
4069  } else {
4070  // Simply clear fog from the locations.
4071  for(const map_location &hex : locs) {
4072  t.clear_fog(hex);
4073  }
4074  }
4075  }
4076 
4077  // Flag a screen update.
4078  game_display_->recalculate_minimap();
4079  game_display_->invalidate_all();
4080  return 0;
4081 }
4082 
4083 // Invokes a synced command
4085 {
4086  const std::string name = luaL_checkstring(L, 1);
4087  auto it = synced_command::registry().find(name);
4088  config cmd;
4089  if(it == synced_command::registry().end()) {
4090  // Custom command
4091  if(!luaW_getglobal(L, "wesnoth", "custom_synced_commands", name)) {
4092  return luaL_argerror(L, 1, "Unknown synced command");
4093  }
4094  config& cmd_tag = cmd.child_or_add("custom_command");
4095  cmd_tag["name"] = name;
4096  if(!lua_isnoneornil(L, 2)) {
4097  cmd_tag.add_child("data", luaW_checkconfig(L, 2));
4098  }
4099  } else {
4100  // Built-in command
4101  cmd.add_child(name, luaW_checkconfig(L, 2));
4102  }
4103  // Now just forward to the WML action.
4104  luaW_getglobal(L, "wesnoth", "wml_actions", "do_command");
4105  luaW_pushconfig(L, cmd);
4106  luaW_pcall(L, 1, 0);
4107  return 0;
4108 }
4109 
4110 // END CALLBACK IMPLEMENTATION
4111 
4113  return game_state_.board_;
4114 }
4115 
4117  return game_state_.board_.units_;
4118 }
4119 
4120 std::vector<team> & game_lua_kernel::teams() {
4121  return game_state_.board_.teams_;
4122 }
4123 
4125  return game_state_.board_.map();
4126 }
4127 
4129  return game_state_.gamedata_;
4130 }
4131 
4133  return game_state_.tod_manager_;
4134 }
4135 
4137  return *queued_events_.top();
4138 }
4139 
4140 
4142  : lua_kernel_base()
4143  , game_display_(nullptr)
4144  , game_state_(gs)
4145  , play_controller_(pc)
4146  , reports_(reports_object)
4147  , level_lua_()
4148  , queued_events_()
4149  , map_locked_(0)
4150 {
4151  static game_events::queued_event default_queued_event("_from_lua", "", map_location(), map_location(), config());
4152  queued_events_.push(&default_queued_event);
4153 
4154  lua_State *L = mState;
4155 
4156  cmd_log_ << "Registering game-specific wesnoth lib functions...\n";
4157 
4158  // Put some callback functions in the scripting environment.
4159  static luaL_Reg const callbacks[] {
4160  { "add_known_unit", &intf_add_known_unit },
4161  { "add_modification", &intf_add_modification },
4162  { "advance_unit", &intf_advance_unit },
4163  { "copy_unit", &intf_copy_unit },
4164  { "create_animator", &dispatch<&game_lua_kernel::intf_create_animator> },
4165  { "create_unit", &intf_create_unit },
4166  { "debug_ai", &intf_debug_ai },
4167  { "eval_conditional", &intf_eval_conditional },
4168  { "get_era", &intf_get_era },
4169  { "get_traits", &intf_get_traits },
4170  { "get_viewing_side", &intf_get_viewing_side },
4171  { "invoke_synced_command", &intf_invoke_synced_command },
4172  { "modify_ai", &intf_modify_ai_old },
4173  { "remove_modifications", &intf_remove_modifications },
4174  { "set_music", &intf_set_music },
4175  { "sound_volume", &intf_sound_volume },
4176  { "transform_unit", &intf_transform_unit },
4177  { "unit_defense", &intf_unit_defense },
4178  { "unit_movement_cost", &intf_unit_movement_cost },
4179  { "unit_vision_cost", &intf_unit_vision_cost },
4180  { "unit_jamming_cost", &intf_unit_jamming_cost },
4181  { "unit_resistance", &intf_unit_resistance },
4182  { "unsynced", &intf_do_unsynced },
4183  { "add_event_handler", &dispatch<&game_lua_kernel::intf_add_event > },
4184  { "add_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false > },
4185  { "add_tile_overlay", &dispatch<&game_lua_kernel::intf_add_tile_overlay > },
4186  { "add_time_area", &dispatch<&game_lua_kernel::intf_add_time_area > },
4187  { "add_sound_source", &dispatch<&game_lua_kernel::intf_add_sound_source > },
4188  { "allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn > },
4189  { "allow_undo", &dispatch<&game_lua_kernel::intf_allow_undo > },
4190  { "append_ai", &intf_append_ai },
4191  { "cancel_action", &dispatch<&game_lua_kernel::intf_cancel_action > },
4192  { "clear_menu_item", &dispatch<&game_lua_kernel::intf_clear_menu_item > },
4193  { "clear_messages", &dispatch<&game_lua_kernel::intf_clear_messages > },
4194  { "color_adjust", &dispatch<&game_lua_kernel::intf_color_adjust > },
4195  { "delay", &dispatch<&game_lua_kernel::intf_delay > },
4196  { "end_turn", &dispatch<&game_lua_kernel::intf_end_turn > },
4197  { "end_level", &dispatch<&game_lua_kernel::intf_end_level > },
4198  { "erase_unit", &dispatch<&game_lua_kernel::intf_erase_unit > },
4199  { "extract_unit", &dispatch<&game_lua_kernel::intf_extract_unit > },
4200  { "find_cost_map", &dispatch<&game_lua_kernel::intf_find_cost_map > },
4201  { "find_path", &dispatch<&game_lua_kernel::intf_find_path > },
4202  { "find_reach", &dispatch<&game_lua_kernel::intf_find_reach > },
4203  { "find_vacant_tile", &dispatch<&game_lua_kernel::intf_find_vacant_tile > },
4204  { "fire_event", &dispatch2<&game_lua_kernel::intf_fire_event, false > },
4205  { "fire_event_by_id", &dispatch2<&game_lua_kernel::intf_fire_event, true > },
4206  { "float_label", &dispatch<&game_lua_kernel::intf_float_label > },
4207  { "gamestate_inspector", &dispatch<&game_lua_kernel::intf_gamestate_inspector > },
4208  { "get_all_vars", &dispatch<&game_lua_kernel::intf_get_all_vars > },
4209  { "get_end_level_data", &dispatch<&game_lua_kernel::intf_get_end_level_data > },
4210  { "get_locations", &dispatch<&game_lua_kernel::intf_get_locations > },
4211  { "get_map_size", &dispatch<&game_lua_kernel::intf_get_map_size > },
4212  { "get_mouseover_tile", &dispatch<&game_lua_kernel::intf_get_mouseover_tile > },
4213  { "get_recall_units", &dispatch<&game_lua_kernel::intf_get_recall_units > },
4214  { "get_selected_tile", &dispatch<&game_lua_kernel::intf_get_selected_tile > },
4215  { "get_sides", &dispatch<&game_lua_kernel::intf_get_sides > },
4216  { "get_sound_source", &dispatch<&game_lua_kernel::intf_get_sound_source > },
4217  { "get_starting_location", &dispatch<&game_lua_kernel::intf_get_starting_location > },
4218  { "get_terrain", &dispatch<&game_lua_kernel::intf_get_terrain > },
4219  { "get_terrain_info", &dispatch<&game_lua_kernel::intf_get_terrain_info > },
4220  { "get_time_of_day", &dispatch<&game_lua_kernel::intf_get_time_of_day > },
4221  { "get_unit", &dispatch<&game_lua_kernel::intf_get_unit > },
4222  { "get_units", &dispatch<&game_lua_kernel::intf_get_units > },
4223  { "get_variable", &dispatch<&game_lua_kernel::intf_get_variable > },
4224  { "get_side_variable", &dispatch<&game_lua_kernel::intf_get_side_variable > },
4225  { "get_villages", &dispatch<&game_lua_kernel::intf_get_villages > },
4226  { "get_village_owner", &dispatch<&game_lua_kernel::intf_get_village_owner > },
4227  { "get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit > },
4228  { "highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex > },
4229  { "is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy > },
4230  { "label", &dispatch<&game_lua_kernel::intf_label > },
4231  { "lock_view", &dispatch<&game_lua_kernel::intf_lock_view > },
4232  { "log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
4233  { "log", &dispatch<&game_lua_kernel::intf_log > },
4234  { "match_location", &dispatch<&game_lua_kernel::intf_match_location > },
4235  { "match_side", &dispatch<&game_lua_kernel::intf_match_side > },
4236  { "match_unit", &dispatch<&game_lua_kernel::intf_match_unit > },
4237  { "message", &dispatch<&game_lua_kernel::intf_message > },
4238  { "open_help", &dispatch<&game_lua_kernel::intf_open_help > },
4239  { "play_sound", &dispatch<&game_lua_kernel::intf_play_sound > },
4240  { "print", &dispatch<&game_lua_kernel::intf_print > },
4241  { "put_recall_unit", &dispatch<&game_lua_kernel::intf_put_recall_unit > },
4242  { "put_unit", &dispatch<&game_lua_kernel::intf_put_unit > },
4243  { "redraw", &dispatch<&game_lua_kernel::intf_redraw > },
4244  { "remove_event_handler", &dispatch<&game_lua_kernel::intf_remove_event > },
4245  { "remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true > },
4246  { "remove_tile_overlay", &dispatch<&game_lua_kernel::intf_remove_tile_overlay > },
4247  { "remove_time_area", &dispatch<&game_lua_kernel::intf_remove_time_area > },
4248  { "remove_sound_source", &dispatch<&game_lua_kernel::intf_remove_sound_source > },
4249  { "replace_schedule", &dispatch<&game_lua_kernel::intf_replace_schedule > },
4250  { "scroll", &dispatch<&game_lua_kernel::intf_scroll > },
4251  { "scroll_to_tile", &dispatch<&game_lua_kernel::intf_scroll_to_tile > },
4252  { "select_hex", &dispatch<&game_lua_kernel::intf_select_hex > },
4253  { "set_time_of_day", &dispatch<&game_lua_kernel::intf_set_time_of_day > },
4254  { "deselect_hex", &dispatch<&game_lua_kernel::intf_deselect_hex > },
4255  { "select_unit", &dispatch<&game_lua_kernel::intf_select_unit > },
4256  { "skip_messages", &dispatch<&game_lua_kernel::intf_skip_messages > },
4257  { "is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true > },
4258  { "is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false > },
4259  { "is_skipping_messages", &dispatch<&game_lua_kernel::intf_is_skipping_messages > },
4260  { "set_end_campaign_credits", &dispatch<&game_lua_kernel::intf_set_end_campaign_credits > },
4261  { "set_end_campaign_text", &dispatch<&game_lua_kernel::intf_set_end_campaign_text > },
4262  { "set_menu_item", &dispatch<&game_lua_kernel::intf_set_menu_item > },
4263  { "set_next_scenario", &dispatch<&game_lua_kernel::intf_set_next_scenario > },
4264  { "set_side_id", &dispatch<&game_lua_kernel::intf_set_side_id > },
4265  { "set_terrain", &dispatch<&game_lua_kernel::intf_set_terrain > },
4266  { "set_variable", &dispatch<&game_lua_kernel::intf_set_variable > },
4267  { "set_side_variable", &dispatch<&game_lua_kernel::intf_set_side_variable > },
4268  { "set_village_owner", &dispatch<&game_lua_kernel::intf_set_village_owner > },
4269  { "simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
4270  { "switch_ai", &intf_switch_ai },
4271  { "synchronize_choice", &intf_synchronize_choice },
4272  { "synchronize_choices", &intf_synchronize_choices },
4273  { "terrain_mask", &dispatch<&game_lua_kernel::intf_terrain_mask > },
4274  { "zoom", &dispatch<&game_lua_kernel::intf_zoom > },
4275  { "teleport", &dispatch<&game_lua_kernel::intf_teleport > },
4276  { "unit_ability", &dispatch<&game_lua_kernel::intf_unit_ability > },
4277  { "view_locked", &dispatch<&game_lua_kernel::intf_view_locked > },
4278  { "place_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, true > },
4279  { "remove_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, false > },
4280  { nullptr, nullptr }
4281  };
4282  std::vector<lua_cpp::Reg> const cpp_callbacks {
4283  {"add_ai_component", std::bind(intf_modify_ai, _1, "add")},
4284  {"delete_ai_component", std::bind(intf_modify_ai, _1, "delete")},
4285  {"change_ai_component", std::bind(intf_modify_ai, _1, "change")},
4286  {nullptr, nullptr}
4287  };
4288  lua_getglobal(L, "wesnoth");
4289  if (!lua_istable(L,-1)) {
4290  lua_newtable(L);
4291  }
4292  luaL_setfuncs(L, callbacks, 0);
4293  lua_cpp::set_functions(L, cpp_callbacks);
4294 
4295  if(play_controller_.get_classification().campaign_type == game_classification::CAMPAIGN_TYPE::TEST) {
4296  static luaL_Reg const test_callbacks[] {
4297  { "fire_wml_menu_item", &dispatch<&game_lua_kernel::intf_fire_wml_menu_item > },
4298  { nullptr, nullptr }
4299  };
4300  luaL_setfuncs(L, test_callbacks , 0);
4301  }
4302 
4303  lua_setglobal(L, "wesnoth");
4304 
4305  // Create the getside metatable.
4307 
4308  // Create the gettype metatable.
4310 
4311  //Create the getrace metatable
4313 
4314  //Create the unit metatables
4317 
4318  // Create the vconfig metatable.
4320 
4321  // Create the unit_types table
4323 
4324  // Create the ai elements table.
4325  cmd_log_ << "Adding ai elements table...\n";
4326 
4328 
4329  // Create the current variable with its metatable.
4330  cmd_log_ << "Adding wesnoth current table...\n";
4331 
4332  lua_getglobal(L, "wesnoth");
4333  lua_newuserdata(L, 0);
4334  lua_createtable(L, 0, 2);
4335  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_get>);
4336  lua_setfield(L, -2, "__index");
4337  lua_pushstring(L, "current config");
4338  lua_setfield(L, -2, "__metatable");
4339  lua_setmetatable(L, -2);
4340  lua_setfield(L, -2, "current");
4341  lua_pop(L, 1);
4342 
4343  // Create the playlist table with its metatable
4345 
4346  // Create the wml_actions table.
4347  cmd_log_ << "Adding wml_actions table...\n";
4348 
4349  lua_getglobal(L, "wesnoth");
4350  lua_newtable(L);
4351  lua_setfield(L, -2, "wml_actions");
4352  lua_pop(L, 1);
4353 
4354  // Create the wml_conditionals table.
4355  cmd_log_ << "Adding wml_conditionals table...\n";
4356 
4357  lua_getglobal(L, "wesnoth");
4358  lua_newtable(L);
4359  lua_setfield(L, -2, "wml_conditionals");
4360  lua_pop(L, 1);
4364 
4365  // Create the effects table.
4366  cmd_log_ << "Adding effects table...\n";
4367 
4368  lua_getglobal(L, "wesnoth");
4369  lua_newtable(L);
4370  lua_setfield(L, -2, "effects");
4371  lua_pop(L, 1);
4372 
4373  // Create the custom_synced_commands table.
4374  cmd_log_ << "Adding custom_synced_commands table...\n";
4375 
4376  lua_getglobal(L, "wesnoth");
4377  lua_newtable(L);
4378  lua_setfield(L, -2, "custom_synced_commands");
4379  lua_pop(L, 1);
4380 
4381  // Create the game_events table.
4382  cmd_log_ << "Adding game_events table...\n";
4383 
4384  lua_getglobal(L, "wesnoth");
4385  lua_newtable(L);
4386  lua_setfield(L, -2, "game_events");
4388  lua_setfield(L, -2, "special_locations");
4389  lua_pop(L, 1);
4390 
4391  // Create the theme_items table.
4392  cmd_log_ << "Adding theme_items table...\n";
4393 
4394  lua_getglobal(L, "wesnoth");
4395  lua_newtable(L);
4396  lua_createtable(L, 0, 2);
4397  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_get>);
4398  lua_setfield(L, -2, "__index");
4399  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_set>);
4400  lua_setfield(L, -2, "__newindex");
4401  lua_setmetatable(L, -2);
4402  lua_setfield(L, -2, "theme_items");
4403  lua_pop(L, 1);
4404 
4405  lua_settop(L, 0);
4406 
4407  for(const auto& handler : game_events::wml_action::registry())
4408  {
4409  set_wml_action(handler.first, handler.second);
4410  }
4411  luaW_getglobal(L, "wesnoth", "effects");
4412  for(const std::string& effect : unit::builtin_effects) {
4413  lua_pushstring(L, effect.c_str());
4415  lua_rawset(L, -3);
4416  }
4417  lua_settop(L, 0);
4418 }
4419 
4421 {
4422  lua_State *L = mState;
4423  assert(level_lua_.empty());
4424  level_lua_.append_children(level, "lua");
4425  // Create the sides table.
4426  // note:
4427  // This table is redundant to the return value of wesnoth.get_sides({}).
4428  // Still needed for backwards compatibility.
4429  lua_settop(L, 0);
4430  lua_getglobal(L, "wesnoth");
4431 
4432  lua_pushstring(L, "get_sides");
4433  lua_rawget(L, -2);
4434  lua_createtable(L, 0, 0);
4435 
4436  if (!protected_call(1, 1, std::bind(&lua_kernel_base::log_error, this, _1, _2))) {
4437  cmd_log_ << "Failed to compute wesnoth.sides\n";
4438  } else {
4439  lua_setfield(L, -2, "sides");
4440  cmd_log_ << "Added wesnoth.sides\n";
4441  }
4442 
4443  //Create the races table.
4444  cmd_log_ << "Adding races table...\n";
4445 
4446  lua_settop(L, 0);
4447  lua_getglobal(L, "wesnoth");
4448  luaW_pushracetable(L);
4449  lua_setfield(L, -2, "races");
4450  lua_pop(L, 1);
4451 
4452  // Execute the preload scripts.
4453  cmd_log_ << "Running preload scripts...\n";
4454 
4455  game_config::load_config(game_lua_kernel::preload_config);
4456  for (const config &cfg : game_lua_kernel::preload_scripts) {
4457  run_lua_tag(cfg);
4458  }
4459  for (const config &cfg : level_lua_.child_range("lua")) {
4460  run_lua_tag(cfg);
4461  }
4462 }
4463 
4465  game_display_ = gd;
4466 }
4467 
4468 /// These are the child tags of [scenario] (and the like) that are handled
4469 /// elsewhere (in the C++ code).
4470 /// Any child tags not in this list will be passed to Lua's on_load event.
4471 static char const *handled_file_tags[] {
4472  "color_palette", "color_range", "display", "end_level_data", "era",
4473  "event", "generator", "label", "lua", "map", "menu_item",
4474  "modification", "music", "options", "side", "sound_source",
4475  "story", "terrain_graphics", "time", "time_area", "tunnel",
4476  "undo_stack", "variables"
4477 };
4478 
4479 static bool is_handled_file_tag(const std::string &s)
4480 {
4481  for (char const *t : handled_file_tags) {
4482  if (s == t) return true;
4483  }
4484  return false;
4485 }
4486 
4487 /**
4488  * Executes the game_events.on_load function and passes to it all the
4489  * scenario tags not yet handled.
4490  */
4492 {
4493  lua_State *L = mState;
4494 
4495  if (!luaW_getglobal(L, "wesnoth", "game_events", "on_load"))
4496  return;
4497 
4498  lua_newtable(L);
4499  int k = 1;
4500  for (const config::any_child &v : level.all_children_range())
4501  {
4502  if (is_handled_file_tag(v.key)) continue;
4503  lua_createtable(L, 2, 0);
4504  lua_pushstring(L, v.key.c_str());
4505  lua_rawseti(L, -2, 1);
4506  luaW_pushconfig(L, v.cfg);
4507  lua_rawseti(L, -2, 2);
4508  lua_rawseti(L, -2, k++);
4509  }
4510 
4511  luaW_pcall(L, 1, 0, true);
4512 }
4513 
4514 /**
4515  * Executes the game_events.on_save function and adds to @a cfg the
4516  * returned tags. Also flushes the [lua] tags.
4517  */
4519 {
4520  lua_State *L = mState;
4521 
4522  if (!luaW_getglobal(L, "wesnoth", "game_events", "on_save"))
4523  return;
4524 
4525  if (!luaW_pcall(L, 0, 1, false))
4526  return;
4527 
4528  config v;
4529  luaW_toconfig(L, -1, v);
4530  lua_pop(L, 1);
4531 
4532  for (;;)
4533  {
4535  if (i == v.ordered_end()) break;
4536  if (is_handled_file_tag(i->key))
4537  {
4538  /*
4539  * It seems the only tags appearing in the config v variable here
4540  * are the core-lua-handled (currently [item] and [objectives])
4541  * and the extra UMC ones.
4542  */
4543  const std::string m = "Tag is already used: [" + i->key + "]";
4544  log_error(m.c_str());
4545  v.erase(i);
4546  continue;
4547  }
4548  cfg.splice_children(v, i->key);
4549  }
4550 }
4551 
4552 /**
4553  * Executes the game_events.on_event function.
4554  * Returns false if there was no lua handler for this event
4555  */
4557 {
4558  lua_State *L = mState;
4559 
4560  if (!luaW_getglobal(L, "wesnoth", "game_events", "on_event"))
4561  return false;
4562 
4563  queued_event_context dummy(&ev, queued_events_);
4564  lua_pushstring(L, ev.name.c_str());
4565  luaW_pcall(L, 1, 0, false);
4566  return true;
4567 }
4568 
4569 void game_lua_kernel::custom_command(const std::string& name, const config& cfg)
4570 {
4571  lua_State *L = mState;
4572 
4573  if (!luaW_getglobal(L, "wesnoth", "custom_synced_commands", name)) {
4574  return;
4575  }
4576  luaW_pushconfig(L, cfg);
4577  luaW_pcall(L, 1, 0, false);
4578 }
4579 
4580 /**
4581  * Applies its upvalue as an effect
4582  * Arg 1: The unit to apply to
4583  * Arg 3: The [effect] tag contents
4584  * Arg 3: If false, only build description
4585  * Return: The description of the effect
4586  */
4588 {
4589  std::string which_effect = lua_tostring(L, lua_upvalueindex(1));
4590  bool need_apply = luaW_toboolean(L, lua_upvalueindex(2));
4591  // Argument 1 is the implicit "self" argument, which isn't needed here
4592  lua_unit u(luaW_checkunit(L, 2));
4593  config cfg = luaW_checkconfig(L, 3);
4594 
4595  // The times= key is supposed to be ignored by the effect function.
4596  // However, just in case someone doesn't realize this, we will set it to 1 here.
4597  cfg["times"] = 1;
4598 
4599  if(need_apply) {
4600  u->apply_builtin_effect(which_effect, cfg);
4601  return 0;
4602  } else {
4603  std::string description = u->describe_builtin_effect(which_effect, cfg);
4604  lua_pushstring(L, description.c_str());
4605  return 1;
4606  }
4607 }
4608 
4609 /**
4610 * Registers a function for use as an effect handler.
4611 */
4613 {
4614  lua_State *L = mState;
4615 
4616  // The effect name is at the top of the stack
4617  int str_i = lua_gettop(L);
4618  lua_newtable(L); // The functor table
4619  lua_newtable(L); // The functor metatable
4620  lua_pushstring(L, "__call");
4621  lua_pushvalue(L, str_i);
4622  lua_pushboolean(L, true);
4623  lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
4624  lua_rawset(L, -3); // Set the call metafunction
4625  lua_pushstring(L, "__descr");
4626  lua_pushvalue(L, str_i);
4627  lua_pushboolean(L, false);
4628  lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
4629  lua_rawset(L, -3); // Set the descr "metafunction"
4630  lua_setmetatable(L, -2); // Apply the metatable to the functor table
4631 }
4632 
4633 
4634 /**
4635  * Executes its upvalue as a wml action.
4636  */
4638 {
4641 
4642  vconfig vcfg = luaW_checkvconfig(L, 1);
4643  h(get_event_info(), vcfg);
4644  return 0;
4645 }
4646 
4647 /**
4648  * Registers a function for use as an action handler.
4649  */
4651 {
4652  lua_State *L = mState;
4653 
4654  lua_getglobal(L, "wesnoth");
4655  lua_pushstring(L, "wml_actions");
4656  lua_rawget(L, -2);
4657  lua_pushstring(L, cmd.c_str());
4658  lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
4659  lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_wml_action>, 1);
4660  lua_rawset(L, -3);
4661  lua_pop(L, 2);
4662 }
4663 
4664 using wml_conditional_handler = bool(*)(const vconfig&);
4665 
4666 /**
4667  * Executes its upvalue as a wml condition and returns the result.
4668  */
4670 {
4673 
4674  vconfig vcfg = luaW_checkvconfig(L, 1);
4675  lua_pushboolean(L, h(vcfg));
4676  return 1;
4677 }
4678 
4679 /**
4680  * Registers a function for use as a conditional handler.
4681  */
4683 {
4684  lua_State *L = mState;
4685 
4686  lua_getglobal(L, "wesnoth");
4687  lua_pushstring(L, "wml_conditionals");
4688  lua_rawget(L, -2);
4689  lua_pushstring(L, cmd.c_str());
4690  lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
4692  lua_rawset(L, -3);
4693  lua_pop(L, 2);
4694 }
4695 
4696 /**
4697  * Runs a command from an event handler.
4698  * @return true if there is a handler for the command.
4699  * @note @a cfg should be either volatile or long-lived since the Lua
4700  * code may grab it for an arbitrary long time.
4701  */
4702 bool game_lua_kernel::run_wml_action(const std::string& cmd, const vconfig& cfg,
4703  const game_events::queued_event& ev)
4704 {
4705  lua_State *L = mState;
4706 
4707 
4708  if (!luaW_getglobal(L, "wesnoth", "wml_actions", cmd))
4709  return false;
4710 
4711  queued_event_context dummy(&ev, queued_events_);
4712  luaW_pushvconfig(L, cfg);
4713  luaW_pcall(L, 1, 0, true);
4714  return true;
4715 }
4716 
4717 
4718 /**
4719  * Evaluates a WML conidition.
4720  *
4721  * @returns Whether the condition passed.
4722  * @note @a cfg should be either volatile or long-lived since the Lua
4723  * code may grab it for an arbitrarily long time.
4724  */
4725 bool game_lua_kernel::run_wml_conditional(const std::string& cmd, const vconfig& cfg)
4726 {
4727  lua_State* L = mState;
4728 
4729  // If an invalid coniditional tag is used, consider it a pass.
4730  if(!luaW_getglobal(L, "wesnoth", "wml_conditionals", cmd)) {
4731  lg::wml_error() << "unknown conditional wml: [" << cmd << "]\n";
4732  return true;
4733  }
4734 
4735  luaW_pushvconfig(L, cfg);
4736 
4737  // Any runtime error is considered a fail.
4738  if(!luaW_pcall(L, 1, 1, true)) {
4739  return false;
4740  }
4741 
4742  bool b = luaW_toboolean(L, -1);
4743 
4744  lua_pop(L, 1);
4745  return b;
4746 }
4747 
4748 
4749 /**
4750 * Runs a script from a location filter.
4751 * The script is an already compiled function given by its name.
4752 */
4753 bool game_lua_kernel::run_filter(char const *name, const map_location& l)
4754 {
4757  return run_filter(name, 2);
4758 }
4759 /**
4760 * Runs a script from a unit filter.
4761 * The script is an already compiled function given by its name.
4762 */
4763 bool game_lua_kernel::run_filter(char const *name, const unit& u)
4764 {
4765  lua_State *L = mState;
4766  lua_unit* lu = luaW_pushlocalunit(L, const_cast<unit&>(u));
4767  // stack: unit
4768  // put the unit to the stack twice to prevent gc.
4769  lua_pushvalue(L, -1);
4770  // stack: unit, unit
4771  bool res = run_filter(name, 1);
4772  // stack: unit
4773  lu->clear_ref();
4774  lua_pop(L, 1);
4775  return res;
4776 }
4777 /**
4778 * Runs a script from a filter.
4779 * The script is an already compiled function given by its name.
4780 */
4781 bool game_lua_kernel::run_filter(char const *name, int nArgs)
4782 {
4783  map_locker(this);
4784  lua_State *L = mState;
4785  // Get the user filter by name.
4786  const std::vector<std::string>& path = utils::split(name, '.', utils::STRIP_SPACES);
4787  if (!luaW_getglobal(L, path))
4788  {
4789  std::string message = std::string() + "function " + name + " not found";
4790  log_error(message.c_str(), "Lua SUF Error");
4791  //we pushed nothing and can safeley return.
4792  return false;
4793  }
4794  lua_insert(L, -nArgs - 1);
4795 
4796  if (!luaW_pcall(L, nArgs, 1)) return false;
4797 
4798  bool b = luaW_toboolean(L, -1);
4799  lua_pop(L, 1);
4800  return b;
4801 }
4802 
4803 std::string game_lua_kernel::apply_effect(const std::string& name, unit& u, const config& cfg, bool need_apply)
4804 {
4805  lua_State *L = mState;
4806  int top = lua_gettop(L);
4807  std::string descr;
4808  // Stack: nothing
4809  lua_unit* lu = luaW_pushlocalunit(L, u);
4810  // Stack: unit
4811  // (Note: The unit needs to be on the stack twice to prevent untimely GC.)
4812  luaW_pushconfig(L, cfg);
4813  // Stack: unit, cfg
4814  if(luaW_getglobal(L, "wesnoth", "effects", name)) {
4815  map_locker(this);
4816  // Stack: unit, cfg, effect
4817  if(lua_istable(L, -1)) {
4818  // Effect is implemented by a table with __call and __descr
4819  if(need_apply) {
4820  lua_pushvalue(L, -1);
4821  // Stack: unit, cfg, effect, effect
4822  lua_pushvalue(L, top + 1);
4823  // Stack: unit, cfg, effect, effect, unit
4824  lua_pushvalue(L, top + 2);
4825  // Stack: unit, cfg, effect, effect, unit, cfg
4826  luaW_pcall(L, 2, 0);
4827  // Stack: unit, cfg, effect
4828  }
4829  if(luaL_getmetafield(L, -1, "__descr")) {
4830  // Stack: unit, cfg, effect, __descr
4831  if(lua_isstring(L, -1)) {
4832  // __descr was a static string
4833  descr = lua_tostring(L, -1);
4834  } else {
4835  lua_pushvalue(L, -2);
4836  // Stack: unit, cfg, effect, __descr, effect
4837  lua_pushvalue(L, top + 1);
4838  // Stack: unit, cfg, effect, __descr, effect, unit
4839  lua_pushvalue(L, top + 2);
4840  // Stack: unit, cfg, effect, __descr, effect, unit, cfg
4841  luaW_pcall(L, 3, 1);
4842  if(lua_isstring(L, -1) && !lua_isnumber(L, -1)) {
4843  descr = lua_tostring(L, -1);
4844  } else {
4845  ERR_LUA << "Effect __descr metafunction should have returned a string, but instead returned ";
4846  if(lua_isnone(L, -1)) {
4847  ERR_LUA << "nothing";
4848  } else {
4849  ERR_LUA << lua_typename(L, lua_type(L, -1));
4850  }
4851  }
4852  }
4853  }
4854  } else if(need_apply) {
4855  // Effect is assumed to be a simple function; no description is provided
4856  lua_pushvalue(L, top + 1);
4857  // Stack: unit, cfg, effect, unit
4858  lua_pushvalue(L, top + 2);
4859  // Stack: unit, cfg, effect, unit, cfg
4860  luaW_pcall(L, 2, 0);
4861  // Stack: unit, cfg
4862  }
4863  }
4864  lua_settop(L, top);
4865  lu->clear_ref();
4866  return descr;
4867 }
4868 
4870 {
4871  return ai::lua_ai_context::create(mState,code,engine);
4872 }
4873 
4875 {
4876  return ai::lua_ai_action_handler::create(mState,code,context);
4877 }
4878 
4880 {
4881  lua_State *L = mState;
4882 
4883  if (!luaW_getglobal(L, "wesnoth", "game_events", "on_mouse_move")) {
4884  return;
4885  }
4886  lua_push(L, loc.wml_x());
4887  lua_push(L, loc.wml_y());
4888  luaW_pcall(L, 2, 0, false);
4889  return;
4890 }
4891 
4893 {
4894  lua_State *L = mState;
4895 
4896  if (!luaW_getglobal(L, "wesnoth", "game_events", "on_mouse_action")) {
4897  return;
4898  }
4899  lua_push(L, loc.wml_x());
4900  lua_push(L, loc.wml_y());
4901  luaW_pcall(L, 2, 0, false);
4902  return;
4903 }
const_attack_ptr weapon
The weapon used by the unit to attack the opponent, or nullptr if there is none.
Definition: attack.hpp:50
void set_wml_y(int v)
Definition: location.hpp:161
bool luaW_checkvariable(lua_State *L, variable_access_create &v, int n)
Definition: lua_common.cpp:903
int dispatch(lua_State *L)
#define modify_bool_attrib(name, accessor)
Definition: lua_common.hpp:339
bool luaW_tableget(lua_State *L, int index, const char *key)
Definition: lua_common.cpp:946
play_controller * controller
Definition: resources.cpp:21
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
int intf_get_terrain_info(lua_State *L)
Gets details about a terrain.
static int intf_transform_unit(lua_State *L)
Changes a unit to the given unit type.
#define lua_isnoneornil(L, n)
Definition: lua.h:359
void wait_for_end() const
Definition: animation.cpp:1442
unsigned int end_text_duration
for how long the end-of-campaign text is shown
bool empty() const
Tests for an attribute that either was never set or was set to "".
variable_info_mutable< variable_info_implementation::vi_policy_throw > variable_access_throw
&#39;Throw if nonexistent&#39; access.
LUA_API void lua_pushlightuserdata(lua_State *L, void *p)
Definition: lapi.cpp:565
static int impl_end_level_data_get(lua_State *L)
LUALIB_API void * luaL_checkudata(lua_State *L, int ud, const char *tname)
Definition: lauxlib.cpp:333
config get_user_choice(const std::string &name, const user_choice &uch, int side=0)
std::stack< game_events::queued_event const *> queued_events_
Game board class.
Definition: game_board.hpp:50
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:684
#define lua_pushcfunction(L, f)
Definition: lua.h:350
static int intf_advance_unit(lua_State *L)
Advances a unit if the unit has enough xp.
static int intf_get_era(lua_State *L)
Gets a table for an era tag.
static int special_locations_pairs(lua_State *L)
virtual std::string get_id() const =0
utils::string_view luaW_tostring(lua_State *L, int index)
Definition: lua_common.cpp:961
int map_locked_
A value != 0 means that the shouldn&#39;t remove any units from the map, usually because we are currently...
std::string apply_effect(const std::string &name, unit &u, const config &cfg, bool need_apply)
bool is_odd(T num)
Definition: math.hpp:34
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:420
lua_unit * luaW_pushunit(lua_State *L, Args... args)
Definition: lua_unit.hpp:114
bool on_map() const
Definition: lua_unit.hpp:99
int h() const
Effective map height.
Definition: map.hpp:128
bool end_credits
whether to show the standard credits at the end
void set_shroud(bool shroud)
Definition: team.hpp:324
bool is_castle() const
Definition: terrain.hpp:66
double untouched
Resulting chance we were not hit by this opponent (important if it poisons)
std::vector< double > hp_dist
Resulting probability distribution (might be not as large as max_hp)
bool update_locked() const
Whether the screen has been &#39;locked&#39; or not.
Definition: video.cpp:344
void show_help(const std::string &show_topic, int xloc, int yloc)
Open the help browser, show topic with id show_topic.
Definition: help.cpp:116
static char const * handled_file_tags[]
These are the child tags of [scenario] (and the like) that are handled elsewhere (in the C++ code)...
entity_location loc2
Definition: pump.hpp:65
LUA_API lua_Number lua_tonumberx(lua_State *L, int idx, int *pisnum)
Definition: lapi.cpp:345
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
int intf_remove_sound_source(lua_State *L)
Removes a sound source by its ID Arg 1: sound source ID.
int intf_allow_end_turn(lua_State *)
Allow undo sets the flag saying whether the event has mutated the game to false.
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:921
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:1267
void start_animations()
Definition: animation.cpp:1383
int intf_find_path(lua_State *L)
Finds a path between two locations.
const terrain_code NONE_TERRAIN
Definition: translation.hpp:59
bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
Gets an optional vconfig from either a table or a userdata.
Definition: lua_common.cpp:816
std::string register_metatables(lua_State *L)
Definition: lua_unit.cpp:611
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:252
int intf_set_end_campaign_credits(lua_State *L)
void reshroud()
Definition: team.hpp:322
const t_string & description() const
Definition: terrain.hpp:35
LUALIB_API lua_Number luaL_checknumber(lua_State *L, int arg)
Definition: lauxlib.cpp:408
game_display * game_display_
void luaW_pushconfig(lua_State *L, const config &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:712
LUA_API void lua_settop(lua_State *L, int idx)
Definition: lapi.cpp:172
int intf_fire_event(lua_State *L, const bool by_id)
Fires an event.
#define lua_isnone(L, n)
Definition: lua.h:358
int intf_get_time_of_day(lua_State *L)
Gets time of day information.
int intf_get_unit(lua_State *)
Gets the unit at the given location or with the given id.
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:251
int village_support
Definition: game_config.cpp:55
bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
Calls a Lua function stored below its nArgs arguments at the top of the stack.
void luaW_pushteam(lua_State *L, team &tm)
Create a full userdata containing a pointer to the team.
Definition: lua_team.cpp:231
std::string plague_type
The plague type used by the attack, if any.
Definition: attack.hpp:82
This class represents a single unit of a specific type.
Definition: unit.hpp:99
int dummy
Definition: lstrlib.cpp:1125
game_classification * classification
Definition: resources.cpp:34
int intf_get_variable(lua_State *L)
Gets a WML variable.
tod_color color
The color modifications that should be made to the game board to reflect the time of day...
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit&#39;s movement cost on a particular terrain.
Definition: unit.hpp:1341
int intf_match_location(lua_State *L)
Matches a location against the given filter.
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
void advance_to(const unit_type &t, bool use_traits=false)
Advances this unit to another type.
Definition: unit.cpp:910
std::vector< int > get_sides_vector(const vconfig &cfg)
Gets a vector of sides from side= attribute in a given config node.
Various functions implementing vision (through fog of war and shroud).
int luaW_type_error(lua_State *L, int narg, const char *tname)
bool run_filter(char const *name, const unit &u)
Runs a script from a unit filter.
int impl_theme_items_get(lua_State *L)
Creates a field of the theme_items table and returns it (__index metamethod).
int intf_replace_schedule(lua_State *l)
Replacing the current time of day schedule.
static int special_locations_len(lua_State *L)
bool play_stage()
Play the turn - strategy.
Definition: stage.cpp:56
void set_clip_rect(const SDL_Rect &r)
unit & luaW_checkunit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:192
static manager & get_singleton()
Definition: manager.hpp:150
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:557
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:658
Various functions that implement attacks and attack calculations.
int impl_end_level_data_set(lua_State *)
Variant for storing WML attributes.
int intf_view_locked(lua_State *L)
Gets whether gamemap scrolling is disabled for the user.
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
bool luaW_isunit(lua_State *L, int index)
Test if a Lua value is a unit.
Definition: lua_unit.cpp:114
void set_next_scenario(const std::string &next_scenario)
Definition: game_data.hpp:94
bool clear_fog(const map_location &loc)
Definition: team.hpp:321
void add_log_data(const std::string &key, const std::string &var)
Definition: replay.cpp:309
virtual std::vector< component * > get_children(const std::string &type)
Definition: component.cpp:111
std::vector< team > & teams()
static const char animatorKey[]
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
int intf_set_terrain(lua_State *L)
Sets a terrain code.
int intf_log(lua_State *L)
Logs a message Arg 1: (optional) Logger; "wml" for WML errors or deprecations Arg 2: Message Arg 3: W...
logger & info()
Definition: log.cpp:90
#define a
static map & registry()
using static function variable instead of static member variable to prevent static initialization fia...
int intf_clear_messages(lua_State *)
Removes all messages from the chat window.
Definition: video.hpp:31
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:412
void refog()
Definition: team.hpp:323
int intf_play_sound(lua_State *L)
Plays a sound, possibly repeated.
int next_player_number_
Definition: game_state.hpp:58
int intf_terrain_mask(lua_State *L)
Reaplces part of the map.
static int impl_animator_get(lua_State *L)
bool have_location(const vconfig &cfg)
int intf_label(lua_State *L)
void add_animation(const unit *animated_unit, const unit_animation *animation, const map_location &src=map_location::null_location(), bool with_bars=false, const std::string &text="", const color_t text_color={0, 0, 0})
Definition: animation.cpp:1325
static int special_locations_newindex(lua_State *L)
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition: variable.hpp:99
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:54
LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:817
LUALIB_API int luaL_getmetafield(lua_State *L, int obj, const char *event)
Definition: lauxlib.cpp:772
LUA_API int lua_gettop(lua_State *L)
Definition: lapi.cpp:167
child_itors child_range(config_key_type key)
Definition: config.cpp:362
std::string register_table(lua_State *L)
std::string id
Definition: time_of_day.hpp:91
int border_size() const
Size of the map border.
Definition: map.hpp:131
int intf_extract_unit(lua_State *L)
Extracts a unit from the map or a recall list and gives it to Lua.
double average_hp(unsigned int healing=0) const
What&#39;s the average hp (weighted average of hp_dist).
std::pair< int, int > get_pair_at(int x, int y) const
Accessor for the cost/reach-amount pairs.
Definition: pathfind.cpp:962
const map_location & filter_loc() const
bool luaW_pushvariable(lua_State *L, variable_access_const &v)
Definition: lua_common.cpp:875
static int intf_copy_unit(lua_State *L)
Copies a unit.
void remove_floating_label(int handle)
removes the floating label given by &#39;handle&#39; from the screen
std::string register_metatable(lua_State *L)
static int special_locations_next(lua_State *L)
int intf_get_all_vars(lua_State *L)
Gets all the WML variables currently set.
LUA_API int lua_getglobal(lua_State *L, const char *name)
Definition: lapi.cpp:605
int intf_find_cost_map(lua_State *L)
Is called with one or more units and builds a cost map.
void clear(const std::string &key)
Definition: general.cpp:205
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:84
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:217
dest_vect destinations
Definition: pathfind.hpp:99
unit_ptr clone() const
Definition: unit.hpp:173
#define lua_tointeger(L, i)
Definition: lua.h:342
int intf_set_time_of_day(lua_State *L)
int wml_x() const
Definition: location.hpp:157
virtual config to_config() const
Serialize to config.
Definition: engine_lua.cpp:385
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
static int special_locations_index(lua_State *L)
static lua_ai_context * create(lua_State *L, char const *code, engine_lua *engine)
Definition: core.cpp:984
LUA_API int lua_rawget(lua_State *L, int idx)
Definition: lapi.cpp:647
Composite AI stages.
std::string image
The image to be displayed in the game status.
Definition: time_of_day.hpp:88
unit_type_data unit_types
Definition: types.cpp:1525
void set_wml_x(int v)
Definition: location.hpp:160
int intf_get_fog_or_shroud(lua_State *L, bool fog)
static int cfun_wml_condition(lua_State *L)
Executes its upvalue as a wml condition and returns the result.
bool prescenario_save
Should a prescenario be created the next game?
int intf_set_village_owner(lua_State *L)
Sets the owner of a village.
lua_unit * luaW_pushlocalunit(lua_State *L, unit &u)
Pushes a private unit on the stack.
Definition: lua_unit.cpp:213
const color_t LABEL_COLOR
int intf_unit_ability(lua_State *L)
Returns true if the unit has the given ability enabled.
int intf_match_unit(lua_State *L)
Matches a unit against the given filter.
int intf_set_next_scenario(lua_State *L)
void push_builtin_effect()
Registers a function for use as an effect handler.
static CVideo & get_singleton()
Definition: video.hpp:43
std::string name
Definition: pump.hpp:62
Replay control code.
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
#define lua_tonumber(L, i)
Definition: lua.h:341
config & child_or_add(config_key_type key)
Definition: config.cpp:466
advance_unit_params & fire_events(bool value)
Definition: advancement.hpp:43
bool replay_save
Should a replay save be made?
void remove(const std::string &id)
Definition: soundsource.cpp:65
std::string save_id_or_number() const
Definition: team.hpp:232
void custom_command(const std::string &, const config &)
Additional functionality for a non-const variable_info.
bool slows
Attack slows opponent when it hits.
Definition: attack.hpp:55
int intf_skip_messages(lua_State *L)
Set whether to skip messages Arg 1 (optional) - boolean.
void set_font_size(int font_size)
LUA_API int lua_isuserdata(lua_State *L, int idx)
Definition: lapi.cpp:289
const std::string & editor_image() const
Definition: terrain.hpp:32
LUA_API void lua_pushcclosure(lua_State *L, lua_CFunction fn, int n)
Definition: lapi.cpp:532
int intf_scroll(lua_State *L)
virtual void draw()
Draws invalidated items.
Definition: display.cpp:2436
int impl_theme_items_set(lua_State *L)
Sets a field of the theme_items table (__newindex metamethod).
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:241
#define return_vector_string_attrib(name, accessor)
Definition: lua_common.hpp:268
static bool is_handled_file_tag(const std::string &s)
unit_ptr get_shared()
Definition: lua_unit.cpp:57
int intf_get_sides(lua_State *L)
Returns a proxy table array for all sides matching the given SSF.
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:371
void add_unit(const unit &u, bool use_max_moves=true)
Adds a units cost map to cost_map (increments the elements in cost_map)
Definition: pathfind.cpp:911
int drain_constant
Base HP drained regardless of damage dealt.
Definition: attack.hpp:77
an object to leave the synced context during draw or unsynced wml items when we don’t know whether w...
int(game_lua_kernel::* member_callback2)(lua_State *, bool)
int intf_gamestate_inspector(lua_State *)
-file sdl_utils.hpp
int impl_game_config_set(lua_State *L) override
Sets some game_config data (__newindex metamethod).
void(* handler)(const queued_event &, const vconfig &)
Definition: action_wml.hpp:49
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
Definition: lua_common.cpp:704
Definitions for the interface to Wesnoth Markup Language (WML).
bool empty() const
Definition: variable.hpp:100
const game_events::queued_event & get_event_info()
LUA_API int lua_absindex(lua_State *L, int idx)
Definition: lapi.cpp:160
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
void load_config(const config &v)
static synced_state get_synced_state()
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
Definition: attack.hpp:73
int rest_heal_amount
Definition: game_config.cpp:61
std::map< int, config > get_user_choice_multiple_sides(const std::string &name, const user_choice &uch, std::set< int > sides)
Performs a choice for multiple sides for WML events.
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
Definition: unit.hpp:154
const_attr_itors attribute_range() const
Definition: config.cpp:809
LUA_API void * lua_newuserdata(lua_State *L, size_t size)
Definition: lapi.cpp:1184
A single unit type that the player may recruit.
Definition: types.hpp:42
int intf_get_locations(lua_State *L)
Gets all the locations matching a given filter.
game_data * gamedata
Definition: resources.cpp:22
int intf_log_replay(lua_State *L)
advance_unit_params & animate(bool value)
Definition: advancement.hpp:44
Unit and team statistics.
#define lua_pop(L, n)
Definition: lua.h:344
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:988
void append_active_ai_for_side(ai::side_number side, const config &cfg)
Appends AI parameters to active AI of the given side.
Definition: manager.cpp:668
#define return_cfgref_attrib(name, accessor)
Definition: lua_common.hpp:260
bool clear_dest(const map_location &dest, const unit &viewer)
Clears shroud (and fog) at the provided location and its immediate neighbors.
Definition: vision.cpp:494
bool reveal_map
Should we reveal map when game is ended? (Multiplayer only)
bool have_unit(const vconfig &cfg)
#define b
bool is_village() const
Definition: terrain.hpp:65
bool clear_unit(const map_location &view_loc, team &view_team, std::size_t viewer_id, int sight_range, bool slowed, const movetype::terrain_costs &costs, const map_location &real_loc, const std::set< map_location > *known_units=nullptr, std::size_t *enemy_count=nullptr, std::size_t *friend_count=nullptr, move_unit_spectator *spectator=nullptr, bool instant=true)
Clears shroud (and fog) around the provided location for view_team based on sight_range, costs, and slowed.
Definition: vision.cpp:330
int intf_find_reach(lua_State *L)
Finds all the locations reachable by a unit.
config to_config() const
void set_game_display(game_display *gd)
bool poisons
Attack poisons opponent when it hits.
Definition: attack.hpp:59
int vision_cost(const t_translation::terrain_code &terrain) const
Get the unit&#39;s vision cost on a particular terrain.
Definition: unit.hpp:1351
int intf_set_variable(lua_State *L)
Sets a WML variable.
bool fog()
Definition: game.cpp:552
LUALIB_API const char * luaL_optlstring(lua_State *L, int arg, const char *def, size_t *len)
Definition: lauxlib.cpp:397
std::string register_metatable(lua_State *L)
Definition: lua_team.cpp:209
void set_flag(const std::string &flag)
Definition: team.hpp:303
int intf_delay(lua_State *L)
Delays engine for a while.
t_string name
Definition: time_of_day.hpp:89
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
static const char * name(const std::vector< SDL_Joystick *> &joysticks, const std::size_t index)
Definition: joystick.cpp:48
void add_modification(const std::string &type, const config &modification, bool no_add=false)
Add a new modification to the unit.
Definition: unit.cpp:2219
#define lua_upvalueindex(i)
Definition: lua.h:43
bool backstab_pos
True if the attacker is in position to backstab the defender (this is used to determine whether to ap...
Definition: attack.hpp:60
static void extract_preload_scripts(const config &game_config)
int light_bonus(int base) const
Returns the light (lawful) bonus for this terrain when the time of day gives a base bonus...
Definition: terrain.hpp:56
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit&#39;s defense on a given terrain.
Definition: unit.cpp:1607
static void clear_status_caches()
Clear this unit status cache for all units.
Definition: unit.cpp:707
LUA_INTEGER lua_Integer
Definition: lua.h:93
unsigned attribute_count() const
Count the number of non-blank attributes.
Definition: config.cpp:407
const_all_children_iterator ordered_end() const
Definition: config.cpp:911
child_list get_children(const std::string &key) const
Definition: variable.cpp:190
std::vector< const unit * > all_matches_with_unit(const unit &u) const
Definition: filter.hpp:166
#define LUA_TSTRING
Definition: lua.h:68
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
static int intf_eval_conditional(lua_State *L)
Evaluates a boolean WML conditional.
static game_config_manager * get()
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
void select_hex_callback(const map_location &loc)
int show_gamestate_inspector(const vconfig &cfg, const game_data &data, const game_state &state)
Definition: lua_gui2.cpp:928
const t_string & editor_name() const
Definition: terrain.hpp:34
int intf_get_map_size(lua_State *L)
Returns the map size.
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
int damage
Effective damage of the weapon (all factors accounted for).
Definition: attack.hpp:74
static int intf_get_viewing_side(lua_State *L)
Gets currently viewing side.
game_events::pump_result_t fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:546
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
virtual void log_error(char const *msg, char const *context="Lua error") override
Error reporting mechanisms, used by virtual methods protected_call and load_string.
std::string register_vconfig_metatable(lua_State *L)
Adds the vconfig metatable.
Definition: lua_common.cpp:444
static int impl_add_animation(lua_State *L)
Various functions that implement advancements of units.
#define modify_string_attrib(name, accessor)
Definition: lua_common.hpp:292
const std::string & id() const
Gets this unit&#39;s id.
Definition: unit.hpp:343
bool linger_mode
Should linger mode be invoked?
std::vector< map_location > steps
Definition: pathfind.hpp:134