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