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