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