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