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