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