The Battle for Wesnoth  1.19.10+dev
game_lua_kernel.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2025
3  by Guillaume Melquiond <guillaume.melquiond@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Provides a Lua interpreter, to be embedded in WML.
19  *
20  * @note Naming conventions:
21  * - intf_ functions are exported in the wesnoth domain,
22  * - impl_ functions are hidden inside metatables,
23  * - cfun_ functions are closures,
24  * - luaW_ functions are helpers in Lua style.
25  */
26 
28 
29 #include "actions/attack.hpp" // for battle_context_unit_stats, etc
30 #include "actions/advancement.hpp" // for advance_unit_at, etc
31 #include "actions/move.hpp" // for clear_shroud
32 #include "actions/vision.hpp" // for clear_shroud and create_jamming_map
33 #include "actions/undo.hpp" // for clear_shroud and create_jamming_map
34 #include "actions/undo_action.hpp" // for clear_shroud and create_jamming_map
35 #include "ai/composite/ai.hpp" // for ai_composite
36 #include "ai/composite/component.hpp" // for component, etc
37 #include "ai/composite/contexts.hpp" // for ai_context
38 #include "ai/lua/engine_lua.hpp" // for engine_lua
39 #include "ai/composite/rca.hpp" // for candidate_action
40 #include "ai/composite/stage.hpp" // for stage
41 #include "ai/configuration.hpp" // for configuration
42 #include "ai/lua/core.hpp" // for lua_ai_context, etc
43 #include "ai/manager.hpp" // for manager, holder
44 #include "attack_prediction.hpp" // for combatant
45 #include "chat_events.hpp" // for chat_handler, etc
46 #include "config.hpp" // for config, etc
47 #include "display_chat_manager.hpp" // for clear_chat_messages
48 #include "floating_label.hpp"
49 #include "formatter.hpp"
50 #include "game_board.hpp" // for game_board
51 #include "game_classification.hpp" // for game_classification, etc
52 #include "game_config.hpp" // for debug, base_income, etc
53 #include "game_config_manager.hpp" // for game_config_manager
54 #include "game_data.hpp" // for game_data, etc
55 #include "game_display.hpp" // for game_display
56 #include "game_errors.hpp" // for game_error
57 #include "game_events/conditional_wml.hpp" // for conditional_passed
59 #include "game_events/handlers.hpp"
60 #include "game_events/manager_impl.hpp" // for pending_event_handler
61 #include "game_events/pump.hpp" // for queued_event
62 #include "preferences/preferences.hpp" // for encountered_units
63 #include "log.hpp" // for LOG_STREAM, logger, etc
64 #include "map/map.hpp" // for gamemap
65 #include "map/label.hpp"
66 #include "map/location.hpp" // for map_location
67 #include "mouse_events.hpp" // for mouse_handler
68 #include "mp_game_settings.hpp" // for mp_game_settings
69 #include "overlay.hpp"
70 #include "pathfind/pathfind.hpp" // for full_cost_map, plain_route, etc
71 #include "pathfind/teleport.hpp" // for get_teleport_locations, etc
72 #include "play_controller.hpp" // for play_controller
74 #include "recall_list_manager.hpp" // for recall_list_manager
75 #include "replay.hpp" // for get_user_choice, etc
76 #include "reports.hpp" // for register_generator, etc
77 #include "resources.hpp" // for whiteboard
79 #include "scripting/lua_audio.hpp"
80 #include "scripting/lua_unit.hpp"
82 #include "scripting/lua_common.hpp"
84 #include "scripting/lua_gui2.hpp" // for show_gamestate_inspector
86 #include "scripting/lua_race.hpp"
87 #include "scripting/lua_team.hpp"
90 #include "scripting/push_check.hpp"
91 #include "synced_commands.hpp"
92 #include "color.hpp" // for surface
93 #include "side_filter.hpp" // for side_filter
94 #include "sound.hpp" // for commit_music_changes, etc
95 #include "synced_context.hpp" // for synced_context, etc
96 #include "synced_user_choice.hpp"
97 #include "team.hpp" // for team, village_owner
98 #include "terrain/terrain.hpp" // for terrain_type
99 #include "terrain/filter.hpp" // for terrain_filter
100 #include "terrain/translation.hpp" // for read_terrain_code, etc
101 #include "time_of_day.hpp" // for time_of_day
102 #include "tod_manager.hpp" // for tod_manager
103 #include "tstring.hpp" // for t_string, operator+
104 #include "units/unit.hpp" // for unit
105 #include "units/animation_component.hpp" // for unit_animation_component
106 #include "units/udisplay.hpp"
107 #include "units/filter.hpp"
108 #include "units/map.hpp" // for unit_map, etc
109 #include "units/ptr.hpp" // for unit_const_ptr, unit_ptr
110 #include "units/types.hpp" // for unit_type_data, unit_types, etc
111 #include "utils/scope_exit.hpp"
112 #include "variable.hpp" // for vconfig, etc
113 #include "variable_info.hpp"
114 #include "video.hpp" // only for faked
115 #include "whiteboard/manager.hpp" // for whiteboard
116 #include "deprecation.hpp"
117 
118 #include <functional> // for bind_t, bind
119 #include <array>
120 #include <cassert> // for assert
121 #include <cstring> // for strcmp
122 #include <iterator> // for distance, advance
123 #include <map> // for map, map<>::value_type, etc
124 #include <new> // for operator new
125 #include <set> // for set
126 #include <sstream> // for operator<<, basic_ostream, etc
127 #include <thread>
128 #include <utility> // for pair
129 #include <algorithm>
130 #include <vector> // for vector, etc
131 
132 #ifdef DEBUG_LUA
133 #include "scripting/debug_lua.hpp"
134 #endif
135 
136 static lg::log_domain log_scripting_lua("scripting/lua");
137 #define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
138 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
139 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
140 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
141 
142 static lg::log_domain log_wml("wml");
143 #define ERR_WML LOG_STREAM(err, log_wml)
144 
145 std::vector<config> game_lua_kernel::preload_scripts;
147 
148 // Template which allows to push member functions to the lua kernel base into lua as C functions, using a shim
149 typedef int (game_lua_kernel::*member_callback)(lua_State *);
150 
151 template <member_callback method>
152 int dispatch(lua_State *L) {
153  return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L);
154 }
155 
156 // Pass a const bool also...
157 typedef int (game_lua_kernel::*member_callback2)(lua_State *, bool);
158 
159 template <member_callback2 method, bool b>
160 int dispatch2(lua_State *L) {
161  return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L, b);
162 }
163 
165 {
166  map_locker(game_lua_kernel* kernel) : kernel_(kernel)
167  {
168  ++kernel_->map_locked_;
169  }
171  {
172  --kernel_->map_locked_;
173  }
175 };
176 
177 
179 {
181  for (const config& cfg : game_config.child_range("lua")) {
182  game_lua_kernel::preload_scripts.push_back(cfg);
183  }
184  game_lua_kernel::preload_config = game_config.mandatory_child("game_config");
185 }
186 
187 void game_lua_kernel::log_error(char const * msg, char const * context)
188 {
190  lua_chat(context, msg);
191 }
192 
193 void game_lua_kernel::lua_chat(const std::string& caption, const std::string& msg)
194 {
195  if (game_display_) {
196  game_display_->get_chat_manager().add_chat_message(std::time(nullptr), caption, 0, msg,
198  }
199 }
200 
201 /**
202  * Gets a vector of sides from side= attribute in a given config node.
203  * Promotes consistent behavior.
204  */
205 std::vector<int> game_lua_kernel::get_sides_vector(const vconfig& cfg)
206 {
207  const config::attribute_value sides = cfg["side"];
208  const vconfig &ssf = cfg.child("filter_side");
209 
210  if (!ssf.null()) {
211  if(!sides.empty()) { WRN_LUA << "ignoring duplicate side filter information (inline side=)"; }
213  return filter.get_teams();
214  }
215 
216  side_filter filter(sides.str(), &game_state_);
217  return filter.get_teams();
218 }
219 
220 namespace {
221  /**
222  * Temporary entry to a queued_event stack
223  */
224  struct queued_event_context
225  {
226  typedef game_events::queued_event qe;
227  std::stack<qe const *> & stack_;
228 
229  queued_event_context(qe const *new_qe, std::stack<qe const*> & stack)
230  : stack_(stack)
231  {
232  stack_.push(new_qe);
233  }
234 
235  ~queued_event_context()
236  {
237  stack_.pop();
238  }
239  };
240 }//unnamed namespace for queued_event_context
241 
242 /**
243  * Gets currently viewing side.
244  * - Ret 1: integer specifying the currently viewing side
245  * - Ret 2: Bool whether the vision is not limited to that team, this can for example be true during replays.
246  */
247 static int intf_get_viewing_side(lua_State *L)
248 {
249  if(const display* disp = display::get_singleton()) {
250  lua_pushinteger(L, disp->viewing_team().side());
251  lua_pushboolean(L, disp->show_everything());
252  return 2;
253  }
254  else {
255  return 0;
256  }
257 }
258 
259 static int intf_handle_user_interact(lua_State *)
260 {
262  return 0;
263 }
264 
265 static const char animatorKey[] = "unit animator";
266 
267 static int impl_animator_collect(lua_State* L) {
268  unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
269  anim.~unit_animator();
270  return 0;
271 }
272 
273 static int impl_add_animation(lua_State* L)
274 {
275  unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
276  unit_ptr up = luaW_checkunit_ptr(L, 2, false);
277  unit& u = *up;
278  std::string which = luaL_checkstring(L, 3);
279 
280  std::string hits_str = luaL_checkstring(L, 4);
281  strike_result::type hits = strike_result::get_enum(hits_str).value_or(strike_result::type::invalid);
282 
283  map_location dest;
284  int v1 = 0, v2 = 0;
285  bool bars = false;
286  t_string text;
287  color_t color{255, 255, 255};
288  const_attack_ptr primary, secondary;
289 
290  if(lua_istable(L, 5)) {
291  lua_getfield(L, 5, "target");
292  if(luaW_tolocation(L, -1, dest)) {
293  if(dest == u.get_location()) {
294  return luaL_argerror(L, 5, "target location must be different from animated unit's location");
295  } else if(!tiles_adjacent(dest, u.get_location())) {
296  return luaL_argerror(L, 5, "target location must be adjacent to the animated unit");
297  }
298  } else {
299  // luaW_tolocation may set the location to (0,0) if it fails
300  dest = map_location();
301  if(!lua_isnoneornil(L, -1)) {
302  return luaW_type_error(L, 5, "target", "location table");
303  }
304  }
305  lua_pop(L, 1);
306 
307  lua_getfield(L, 5, "value");
308  if(lua_isnumber(L, -1)) {
309  v1 = lua_tointeger(L, -1);
310  } else if(lua_istable(L, -1)) {
311  lua_rawgeti(L, -1, 1);
312  v1 = lua_tointeger(L, -1);
313  lua_pop(L, 1);
314  lua_rawgeti(L, -1, 2);
315  v2 = lua_tointeger(L, -1);
316  lua_pop(L, 1);
317  } else if(!lua_isnoneornil(L, -1)) {
318  return luaW_type_error(L, 5, "value", "number or array of two numbers");
319  }
320  lua_pop(L, 1);
321 
322  lua_getfield(L, 5, "with_bars");
323  if(lua_isboolean(L, -1)) {
324  bars = luaW_toboolean(L, -1);
325  } else if(!lua_isnoneornil(L, -1)) {
326  return luaW_type_error(L, 5, "with_bars", lua_typename(L, LUA_TBOOLEAN));
327  }
328  lua_pop(L, 1);
329 
330  lua_getfield(L, 5, "text");
331  if(lua_isstring(L, -1)) {
332  text = lua_tostring(L, -1);
333  } else if(luaW_totstring(L, -1, text)) {
334  // Do nothing; luaW_totstring already assigned the value
335  } else if(!lua_isnoneornil(L, -1)) {
336  return luaW_type_error(L, 5, "text", lua_typename(L, LUA_TSTRING));
337  }
338  lua_pop(L, 1);
339 
340  lua_getfield(L, 5, "color");
341  if(lua_istable(L, -1) && lua_rawlen(L, -1) == 3) {
342  int idx = lua_absindex(L, -1);
343  lua_rawgeti(L, idx, 1); // red @ -3
344  lua_rawgeti(L, idx, 2); // green @ -2
345  lua_rawgeti(L, idx, 3); // blue @ -1
346  color = color_t(lua_tointeger(L, -3), lua_tointeger(L, -2), lua_tointeger(L, -1));
347  lua_pop(L, 3);
348  } else if(!lua_isnoneornil(L, -1)) {
349  return luaW_type_error(L, 5, "color", "array of three numbers");
350  }
351  lua_pop(L, 1);
352 
353  lua_getfield(L, 5, "primary");
354  primary = luaW_toweapon(L, -1);
355  if(!primary && !lua_isnoneornil(L, -1)) {
356  return luaW_type_error(L, 5, "primary", "weapon");
357  }
358  lua_pop(L, 1);
359 
360  lua_getfield(L, 5, "secondary");
361  secondary = luaW_toweapon(L, -1);
362  if(!secondary && !lua_isnoneornil(L, -1)) {
363  return luaW_type_error(L, 5, "secondary", "weapon");
364  }
365  lua_pop(L, 1);
366  } else if(!lua_isnoneornil(L, 5)) {
367  return luaW_type_error(L, 5, "table of options");
368  }
369 
370  anim.add_animation(up, which, u.get_location(), dest, v1, bars, text, color, hits, primary, secondary, v2);
371  return 0;
372 }
373 
375 {
376  if(video::headless() || resources::controller->is_skipping_replay()) {
377  return 0;
378  }
379  events::command_disabler command_disabler;
380  unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
382  anim.start_animations();
383  anim.wait_for_end();
384  anim.set_all_standing();
385  anim.clear();
386  return 0;
387 }
388 
389 static int impl_clear_animation(lua_State* L)
390 {
391  unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
392  anim.clear();
393  return 0;
394 }
395 
396 static int impl_animator_get(lua_State* L)
397 {
398  const char* m = lua_tostring(L, 2);
399  return luaW_getmetafield(L, 1, m);
400 }
401 
403 {
404  new(L) unit_animator;
405  if(luaL_newmetatable(L, animatorKey)) {
406  luaL_Reg metafuncs[] {
407  {"__gc", impl_animator_collect},
408  {"__index", impl_animator_get},
409  {"add", impl_add_animation},
410  {"run", &dispatch<&game_lua_kernel::impl_run_animation>},
411  {"clear", impl_clear_animation},
412  {nullptr, nullptr},
413  };
414  luaL_setfuncs(L, metafuncs, 0);
415  lua_pushstring(L, "__metatable");
416  lua_setfield(L, -2, animatorKey);
417  }
418  lua_setmetatable(L, -2);
419  return 1;
420 }
421 
423 {
424  if (game_display_) {
426  std::string name;
427  if(luaW_tovconfig(L, 1, cfg)) {
428  name = cfg["name"].str();
429  deprecated_message("gui.show_inspector(cfg)", DEP_LEVEL::INDEFINITE, {1, 19, 0}, "Instead of {name = 'title' }, pass just 'title'.");
430  } else {
431  name = luaL_optstring(L, 1, "");
432  }
434  }
435  return 0;
436 }
437 
438 /**
439  * Gets the unit at the given location or with the given id.
440  * - Arg 1: location
441  * OR
442  * - Arg 1: string ID
443  * - Ret 1: full userdata with __index pointing to impl_unit_get and
444  * __newindex pointing to impl_unit_set.
445  */
447 {
449  if(lua_isstring(L, 1) && !lua_isnumber(L, 1)) {
450  std::string id = luaL_checkstring(L, 1);
451  for(const unit& u : units()) {
452  if(u.id() == id) {
453  luaW_pushunit(L, u.underlying_id());
454  return 1;
455  }
456  }
457  return 0;
458  }
459  if(!luaW_tolocation(L, 1, loc)) {
460  return luaL_argerror(L, 1, "expected string or location");
461  }
463 
464  if (!ui.valid()) return 0;
465 
466  luaW_pushunit(L, ui->underlying_id());
467  return 1;
468 }
469 
470 /**
471  * Gets the unit displayed in the sidebar.
472  * - Ret 1: full userdata with __index pointing to impl_unit_get and
473  * __newindex pointing to impl_unit_set.
474  */
476 {
477  if (!game_display_) {
478  return 0;
479  }
480 
485  if (!ui.valid()) return 0;
486 
487  luaW_pushunit(L, ui->underlying_id());
488  return 1;
489 }
490 
491 /**
492  * Gets all the units matching a given filter.
493  * - Arg 1: optional table containing a filter
494  * - Arg 2: optional location (to find all units that would match on that location)
495  * OR unit (to find all units that would match adjacent to that unit)
496  * - Ret 1: table containing full userdata with __index pointing to
497  * impl_unit_get and __newindex pointing to impl_unit_set.
498  */
500 {
501  vconfig filter = luaW_checkvconfig(L, 1, true);
502  unit_filter filt(filter);
503  std::vector<const unit*> units;
504 
505  if(unit* u_adj = luaW_tounit(L, 2)) {
506  if(!u_adj) {
507  return luaL_argerror(L, 2, "unit not found");
508  }
509  units = filt.all_matches_with_unit(*u_adj);
510  } else if(!lua_isnoneornil(L, 2)) {
512  luaW_tolocation(L, 2, loc);
513  if(!loc.valid()) {
514  return luaL_argerror(L, 2, "invalid location");
515  }
516  units = filt.all_matches_at(loc);
517  } else {
518  units = filt.all_matches_on_map();
519  }
520 
521  // Go through all the units while keeping the following stack:
522  // 1: return table, 2: userdata
523  lua_settop(L, 0);
524  lua_newtable(L);
525  int i = 1;
526 
527  for (const unit * ui : units) {
528  luaW_pushunit(L, ui->underlying_id());
529  lua_rawseti(L, 1, i);
530  ++i;
531  }
532  return 1;
533 }
534 
535 /**
536  * Matches a unit against the given filter.
537  * - Arg 1: full userdata.
538  * - Arg 2: table containing a filter
539  * - Arg 3: optional location OR optional "adjacent" unit
540  * - Ret 1: boolean.
541  */
543 {
544  lua_unit& u = *luaW_checkunit_ref(L, 1);
545 
546  vconfig filter = luaW_checkvconfig(L, 2, true);
547 
548  if (filter.null()) {
549  lua_pushboolean(L, true);
550  return 1;
551  }
552 
553  if(unit* u_adj = luaW_tounit(L, 3)) {
554  if(int side = u.on_recall_list()) {
555  WRN_LUA << "wesnoth.units.matches called with a secondary unit (3rd argument), ";
556  WRN_LUA << "but unit to match was on recall list. ";
557  WRN_LUA << "Thus the 3rd argument is ignored.";
558  team &t = board().get_team(side);
559  scoped_recall_unit auto_store("this_unit", t.save_id_or_number(), t.recall_list().find_index(u->id()));
560  lua_pushboolean(L, unit_filter(filter).matches(*u, map_location()));
561  return 1;
562  }
563  if (!u_adj) {
564  return luaL_argerror(L, 3, "unit not found");
565  }
566  lua_pushboolean(L, unit_filter(filter).matches(*u, *u_adj));
567  } else if(int side = u.on_recall_list()) {
569  luaW_tolocation(L, 3, loc); // If argument 3 isn't a location, loc is unchanged
570  team &t = board().get_team(side);
571  scoped_recall_unit auto_store("this_unit", t.save_id_or_number(), t.recall_list().find_index(u->id()));
572  lua_pushboolean(L, unit_filter(filter).matches(*u, loc));
573  return 1;
574  } else {
576  luaW_tolocation(L, 3, loc); // If argument 3 isn't a location, loc is unchanged
577  lua_pushboolean(L, unit_filter(filter).matches(*u, loc));
578  }
579  return 1;
580 }
581 
582 /**
583  * Gets the numeric ids of all the units matching a given filter on the recall lists.
584  * - Arg 1: optional table containing a filter
585  * - Ret 1: table containing full userdata with __index pointing to
586  * impl_unit_get and __newindex pointing to impl_unit_set.
587  */
589 {
590  vconfig filter = luaW_checkvconfig(L, 1, true);
591 
592  // Go through all the units while keeping the following stack:
593  // 1: return table, 2: userdata
594  lua_settop(L, 0);
595  lua_newtable(L);
596  int i = 1, s = 1;
597  const unit_filter ufilt(filter);
598  for (team &t : teams())
599  {
600  for (unit_ptr & u : t.recall_list())
601  {
602  if (!filter.null()) {
603  scoped_recall_unit auto_store("this_unit",
604  t.save_id_or_number(), t.recall_list().find_index(u->id()));
605  if (!ufilt( *u, map_location() ))
606  continue;
607  }
608  luaW_pushunit(L, s, u->underlying_id());
609  lua_rawseti(L, 1, i);
610  ++i;
611  }
612  ++s;
613  }
614  return 1;
615 }
616 
617 /**
618  * Fires an event.
619  * - Arg 1: string containing the event name or id.
620  * - Arg 2: optional first location.
621  * - Arg 3: optional second location.
622  * - Arg 4: optional WML table used used as the event data
623  * Typically this contains [first] as the [weapon] tag and [second] as the [second_weapon] tag.
624  * - Ret 1: boolean indicating whether the event was processed or not.
625  */
626 int game_lua_kernel::intf_fire_event(lua_State *L, const bool by_id)
627 {
628  char const *m = luaL_checkstring(L, 1);
629 
630  int pos = 2;
631  map_location l1, l2;
632  config data;
633 
634  if (luaW_tolocation(L, 2, l1)) {
635  if (luaW_tolocation(L, 3, l2)) {
636  pos = 4;
637  } else {
638  pos = 3;
639  }
640  }
641 
642  luaW_toconfig(L, pos, data);
643 
644  // Support WML names for some common data
645  if(data.has_child("primary_attack")) {
646  data.add_child("first", data.mandatory_child("primary_attack"));
647  data.remove_children("primary_attack");
648  }
649  if(data.has_child("secondary_attack")) {
650  data.add_child("second", data.mandatory_child("secondary_attack"));
651  data.remove_children("secondary_attack");
652  }
653 
654  bool b = false;
655 
656  if (by_id) {
657  b = std::get<0>(play_controller_.pump().fire("", m, l1, l2, data));
658  }
659  else {
660  b = std::get<0>(play_controller_.pump().fire(m, l1, l2, data));
661  }
662  lua_pushboolean(L, b);
663  return 1;
664 }
665 
666 
667 /**
668  * Fires a wml menu item.
669  * - Arg 1: id of the item. it is not possible to fire items that don't have ids with this function.
670  * - Arg 2: optional first location.
671  * - Ret 1: boolean, true indicating that the event was fired successfully
672  *
673  * NOTE: This is not an "official" feature, it may currently cause assertion failures if used with
674  * menu items which have "needs_select". It is not supported right now to use it this way.
675  * The purpose of this function right now is to make it possible to have automated sanity tests for
676  * the wml menu items system.
677  */
679 {
680  char const *m = luaL_checkstring(L, 1);
681 
682  map_location l1 = luaW_checklocation(L, 2);
683 
685  lua_pushboolean(L, b);
686  return 1;
687 }
688 
689 /**
690  * Gets a WML variable.
691  * - Arg 1: string containing the variable name.
692  * - Arg 2: optional bool indicating if tables for containers should be left empty.
693  * - Ret 1: value of the variable, if any.
694  */
696 {
697  char const *m = luaL_checkstring(L, 1);
699  return luaW_pushvariable(L, v) ? 1 : 0;
700 }
701 
702 /**
703  * Sets a WML variable.
704  * - Arg 1: string containing the variable name.
705  * - Arg 2: boolean/integer/string/table containing the value.
706  */
708 {
709  const std::string m = luaL_checkstring(L, 1);
710  if(m.empty()) return luaL_argerror(L, 1, "empty variable name");
711  if (lua_isnoneornil(L, 2)) {
713  return 0;
714  }
716  luaW_checkvariable(L, v, 2);
717  return 0;
718 }
719 
720 
722 {
723  config cfg = luaW_checkconfig(L, 1);
724  cfg["side"] = teams().size() + 1;
726  lua_pushinteger(L, teams().size());
727 
728  return 1;
729 }
730 
732 {
733  game_state_.get_wml_menu_items().set_item(luaL_checkstring(L, 1), luaW_checkvconfig(L,2));
734  return 0;
735 }
736 
738 {
739  std::string ids(luaL_checkstring(L, 1));
740  for(const std::string& id : utils::split(ids, ',', utils::STRIP_SPACES)) {
741  if(id.empty()) {
742  WRN_LUA << "[clear_menu_item] has been given an empty id=, ignoring";
743  continue;
744  }
746  }
747  return 0;
748 }
749 
750 /**
751  * Toggle shroud on some locations
752  * Arg 1: Side number
753  * Arg 2: List of locations on which to place/remove shroud
754  */
755 int game_lua_kernel::intf_toggle_shroud(lua_State *L, bool place_shroud)
756 {
757  team& t = luaW_checkteam(L, 1, board());
758 
759  if(lua_istable(L, 2)) {
760  std::set<map_location> locs = luaW_check_locationset(L, 2);
761 
762  for (const map_location& loc : locs)
763  {
764  if (place_shroud) {
765  t.place_shroud(loc);
766  } else {
767  t.clear_shroud(loc);
768  }
769  }
770  } else {
771  return luaL_argerror(L, 2, "expected list of locations");
772  }
773 
777 
778  return 0;
779 }
780 
781 /**
782  * Overrides the shroud entirely. All locations are shrouded, except for the ones passed in as argument 2.
783  * Arg 1: Side number
784  * Arg 2: List of locations that should be unshrouded
785  */
787 {
788  team& t = luaW_checkteam(L, 1, board());
789 
790  if(lua_istable(L, 2)) {
791  std::set<map_location> locs = luaW_check_locationset(L, 2);
792  t.reshroud();
793  for(const map_location& loc : locs) {
794  t.clear_shroud(loc);
795  }
796  } else {
797  return luaW_type_error(L, 2, "list of locations");
798  }
799 
803 
804  return 0;
805 }
806 
807 /**
808  * Highlights the given location on the map.
809  * - Arg 1: location.
810  */
812 {
813  if (!game_display_) {
814  return 0;
815  }
816 
817  const map_location loc = luaW_checklocation(L, 1);
818  if(!map().on_board(loc)) return luaL_argerror(L, 1, "not on board");
821 
822  return 0;
823 }
824 
825 /**
826  * Returns whether the first side is an enemy of the second one.
827  * - Args 1,2: side numbers.
828  * - Ret 1: boolean.
829  */
831 {
832  unsigned side_1, side_2;
833  if(team* t = luaW_toteam(L, 1)) {
834  side_1 = t->side();
835  } else {
836  side_1 = luaL_checkinteger(L, 1);
837  }
838  if(team* t = luaW_toteam(L, 2)) {
839  side_2 = t->side();
840  } else {
841  side_2 = luaL_checkinteger(L, 2);
842  }
843  if (side_1 > teams().size() || side_2 > teams().size()) return 0;
844  lua_pushboolean(L, board().get_team(side_1).is_enemy(side_2));
845  return 1;
846 }
847 
848 /**
849  * Gets whether gamemap scrolling is disabled for the user.
850  * - Ret 1: boolean.
851  */
853 {
854  if (!game_display_) {
855  return 0;
856  }
857 
858  lua_pushboolean(L, game_display_->view_locked());
859  return 1;
860 }
861 
862 /**
863  * Sets whether gamemap scrolling is disabled for the user.
864  * - Arg 1: boolean, specifying the new locked/unlocked status.
865  */
867 {
868  bool lock = luaW_toboolean(L, 1);
869  if (game_display_) {
871  }
872  return 0;
873 }
874 
875 static void luaW_push_tod(lua_State* L, const time_of_day& tod)
876 {
877  lua_newtable(L);
878  lua_pushstring(L, tod.id.c_str());
879  lua_setfield(L, -2, "id");
880  lua_pushinteger(L, tod.lawful_bonus);
881  lua_setfield(L, -2, "lawful_bonus");
882  lua_pushinteger(L, tod.bonus_modified);
883  lua_setfield(L, -2, "bonus_modified");
884  lua_pushstring(L, tod.image.c_str());
885  lua_setfield(L, -2, "image");
886  luaW_pushtstring(L, tod.name);
887  lua_setfield(L, -2, "name");
888  lua_pushstring(L, tod.sounds.c_str());
889  lua_setfield(L, -2, "sound");
890  lua_pushstring(L, tod.image_mask.c_str());
891  lua_setfield(L, -2, "mask");
892 
893  lua_pushinteger(L, tod.color.r);
894  lua_setfield(L, -2, "red");
895  lua_pushinteger(L, tod.color.g);
896  lua_setfield(L, -2, "green");
897  lua_pushinteger(L, tod.color.b);
898  lua_setfield(L, -2, "blue");
899 }
900 
901 // A schedule object is an index with a special metatable.
902 // The global schedule uses index -1
903 void game_lua_kernel::luaW_push_schedule(lua_State* L, int area_index)
904 {
905  lua_newuserdatauv(L, 0, 1);
906  lua_pushinteger(L, area_index);
907  lua_setiuservalue(L, -2, 1);
908  if(luaL_newmetatable(L, "schedule")) {
909  static luaL_Reg const schedule_meta[] {
910  {"__index", &dispatch<&game_lua_kernel::impl_schedule_get>},
911  {"__newindex", &dispatch<&game_lua_kernel::impl_schedule_set>},
912  {"__dir", &dispatch<&game_lua_kernel::impl_schedule_dir>},
913  {"__len", &dispatch<&game_lua_kernel::impl_schedule_len>},
914  { nullptr, nullptr }
915  };
916  luaL_setfuncs(L, schedule_meta, 0);
917  }
918  lua_setmetatable(L, -2);
919 }
920 
921 static int luaW_check_schedule(lua_State* L, int idx)
922 {
923  int save_top = lua_gettop(L);
924  luaL_checkudata(L, idx, "schedule");
925  lua_getiuservalue(L, idx, 1);
926  int i = luaL_checkinteger(L, -1);
927  lua_settop(L, save_top);
928  return i;
929 }
930 
931 struct schedule_tag {
935  auto& tod_man() const { return ref.tod_man(); }
936 };
937 #define SCHEDULE_GETTER(name, type) LATTR_GETTER(name, type, schedule_tag, sched)
938 #define SCHEDULE_SETTER(name, type) LATTR_SETTER(name, type, schedule_tag, sched)
939 #define SCHEDULE_VALID(name) LATTR_VALID(name, schedule_tag, sched)
941 
942 template<> struct lua_object_traits<schedule_tag> {
943  inline static auto metatable = "schedule";
944  inline static schedule_tag get(lua_State* L, int n) {
945  schedule_tag sched{lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)};
946  sched.area_index = luaW_check_schedule(L, n);
947  return sched;
948  }
949 };
950 
952 {
953  int area_index = luaW_check_schedule(L, 1);
954  if(lua_isnumber(L, 2)) {
955  const auto& times = area_index < 0 ? tod_man().times() : tod_man().times(area_index);
956  int i = lua_tointeger(L, 2) - 1;
957  if(i < 0 || i >= static_cast<int>(times.size())) {
958  return luaL_argerror(L, 2, "invalid time of day index");
959  }
960  luaW_push_tod(L, times[i]);
961  return 1;
962  }
963  return scheduleReg.get(L);
964 }
965 
967 {
968  int area_index = luaW_check_schedule(L, 1);
969  const auto& times = area_index < 0 ? tod_man().times() : tod_man().times(area_index);
970  lua_pushinteger(L, times.size());
971  return 1;
972 }
973 
975 {
976  int area_index = luaW_check_schedule(L, 1);
977  if(lua_isnumber(L, 2)) {
978  std::vector<time_of_day> times = area_index < 0 ? tod_man().times() : tod_man().times(area_index);
979  int i = lua_tointeger(L, 2) - 1;
980  if(i < 0 || i >= static_cast<int>(times.size())) {
981  return luaL_argerror(L, 2, "invalid time of day index");
982  }
983  config time_cfg = luaW_checkconfig(L, 3);
984  times[i] = time_of_day(time_cfg);
985  if(area_index < 0) {
986  tod_man().replace_schedule(times);
987  } else {
988  tod_man().replace_local_schedule(times, area_index);
989  }
990  }
991  return scheduleReg.set(L);
992 }
993 
995  return scheduleReg.dir(L);
996 }
997 
998 namespace {
999 SCHEDULE_GETTER("time_of_day", std::string) {
1000  if(sched.area_index >= 0) {
1001  return sched.tod_man().get_area_time_of_day(sched.area_index).id;
1002  }
1003  return sched.tod_man().get_time_of_day().id;
1004 }
1005 
1006 SCHEDULE_SETTER("time_of_day", std::string) {
1007  const auto& times = sched.area_index < 0 ? sched.tod_man().times() : sched.tod_man().times(sched.area_index);
1008  auto iter = std::find_if(times.begin(), times.end(), [&value](const time_of_day& tod) {
1009  return tod.id == value;
1010  });
1011  if(iter == times.end()) {
1012  std::ostringstream err;
1013  err << "invalid time of day ID for ";
1014  if(sched.area_index < 0) {
1015  err << "global schedule";
1016  } else {
1017  const std::string& id = sched.tod_man().get_area_id(sched.area_index);
1018  if(id.empty()) {
1019  const auto& hexes = sched.tod_man().get_area_by_index(sched.area_index);
1020  if(hexes.empty()) {
1021  err << "anonymous empty time area";
1022  } else {
1023  err << "anonymous time area at (" << hexes.begin()->wml_x() << ',' << hexes.begin()->wml_y() << ")";
1024  }
1025  } else {
1026  err << "time area with id=" << id;
1027  }
1028  }
1029  lua_push(L, err.str());
1030  throw lua_error(L);
1031  }
1032  int n = std::distance(times.begin(), iter);
1033  if(sched.area_index < 0) {
1034  sched.tod_man().set_current_time(n);
1035  } else {
1036  sched.tod_man().set_current_time(n, sched.area_index);
1037  }
1038 }
1039 
1040 SCHEDULE_VALID("liminal_bonus") {
1041  return sched.area_index < 0;
1042 }
1043 
1044 SCHEDULE_GETTER("liminal_bonus", utils::optional<int>) {
1045  if(sched.area_index >= 0) return utils::nullopt;
1046  return sched.tod_man().get_max_liminal_bonus();
1047 }
1048 
1049 SCHEDULE_SETTER("liminal_bonus", utils::optional<int>) {
1050  if(sched.area_index >= 0) {
1051  throw luaL_error(L, "liminal_bonus can only be set on the global schedule");
1052  }
1053  if(value) {
1054  sched.tod_man().set_max_liminal_bonus(*value);
1055  } else {
1056  sched.tod_man().reset_max_liminal_bonus();
1057  }
1058 }
1059 
1060 SCHEDULE_VALID("id") {
1061  return sched.area_index >= 0;
1062 }
1063 
1064 SCHEDULE_GETTER("id", utils::optional<std::string>) {
1065  if(sched.area_index < 0) return utils::nullopt;
1066  return sched.tod_man().get_area_id(sched.area_index);
1067 }
1068 
1069 SCHEDULE_SETTER("id", std::string) {
1070  if(sched.area_index < 0) {
1071  throw luaL_error(L, "can't set id of global schedule");
1072  }
1073  sched.tod_man().set_area_id(sched.area_index, value);
1074 }
1075 
1076 SCHEDULE_VALID("hexes") {
1077  return sched.area_index >= 0;
1078 }
1079 
1080 SCHEDULE_GETTER("hexes", utils::optional<std::set<map_location>>) {
1081  if(sched.area_index < 0) return utils::nullopt;
1082  return sched.tod_man().get_area_by_index(sched.area_index);
1083 }
1084 
1085 SCHEDULE_SETTER("hexes", std::set<map_location>) {
1086  if(sched.area_index < 0) {
1087  throw luaL_error(L, "can't set hexes of global schedule");
1088  }
1089  sched.tod_man().replace_area_locations(sched.area_index, value);
1090 }
1091 }
1092 
1093 /**
1094  * Gets details about a terrain.
1095  * - Arg 1: terrain code string.
1096  * - Ret 1: table.
1097  */
1099 {
1100  char const *m = luaL_checkstring(L, 2);
1102  if (t == t_translation::NONE_TERRAIN || !board().map().tdata()->is_known(t)) return 0;
1103  const terrain_type& info = board().map().tdata()->get_terrain_info(t);
1104 
1105  lua_newtable(L);
1106  lua_pushstring(L, info.id().c_str());
1107  lua_setfield(L, -2, "id");
1108  luaW_pushtstring(L, info.name());
1109  lua_setfield(L, -2, "name");
1110  luaW_pushtstring(L, info.editor_name());
1111  lua_setfield(L, -2, "editor_name");
1112  luaW_pushtstring(L, info.description());
1113  lua_setfield(L, -2, "description");
1114  lua_push(L, info.icon_image());
1115  lua_setfield(L, -2, "icon");
1116  lua_push(L, info.editor_image());
1117  lua_setfield(L, -2, "editor_image");
1118  lua_pushinteger(L, info.light_bonus(0));
1119  lua_setfield(L, -2, "light");
1120  lua_pushboolean(L, info.is_village());
1121  lua_setfield(L, -2, "village");
1122  lua_pushboolean(L, info.is_castle());
1123  lua_setfield(L, -2, "castle");
1124  lua_pushboolean(L, info.is_keep());
1125  lua_setfield(L, -2, "keep");
1126  lua_pushinteger(L, info.gives_healing());
1127  lua_setfield(L, -2, "healing");
1128 
1129  // movement alias
1130  lua_newtable(L);
1131  int idx = 1;
1132  for (const auto& terrain : info.mvt_type()) {
1133  const terrain_type& base = board().map().tdata()->get_terrain_info(terrain);
1134  if (!base.id().empty()) {
1135  lua_pushstring(L, t_translation::write_terrain_code(base.number()).c_str());
1136  lua_rawseti(L, -2, idx++);
1137  }
1138  }
1139  lua_setfield(L, -2, "mvt_alias");
1140 
1141  // defense alias
1142  lua_newtable(L);
1143  idx = 1;
1144  for (const auto& terrain : info.def_type()) {
1145  const terrain_type& base = board().map().tdata()->get_terrain_info(terrain);
1146  if (!base.id().empty()) {
1147  lua_pushstring(L, t_translation::write_terrain_code(base.number()).c_str());
1148  lua_rawseti(L, -2, idx++);
1149  }
1150  }
1151  lua_setfield(L, -2, "def_alias");
1152 
1153  return 1;
1154 }
1155 
1156 /**
1157  * Gets a list of known terrain codes.
1158  * - Ret 1: array of terrain codes
1159  */
1161 {
1162  auto codes = board().map().tdata()->list();
1163  std::vector<std::string> terrains;
1164  terrains.reserve(codes.size());
1165  for(auto code : codes) {
1166  terrains.push_back(t_translation::write_terrain_code(code));
1167  }
1168  lua_push(L, terrains);
1169  return 1;
1170 }
1171 
1172 /**
1173  * Gets time of day information.
1174  * - Arg 1: schedule object, location, time area ID, or nil
1175  * - Arg 2: optional turn number
1176  * - Ret 1: table.
1177  */
1178 template<bool consider_illuminates>
1180 {
1181  int for_turn = tod_man().turn();
1183 
1184  if(luaW_tolocation(L, 1, loc)) {
1185  if(!board().map().on_board_with_border(loc)) {
1186  return luaL_argerror(L, 1, "coordinates are not on board");
1187  }
1188  } else if(lua_isstring(L, 1)) {
1189  auto area = tod_man().get_area_by_id(lua_tostring(L, 1));
1190  if(area.empty()) {
1191  return luaL_error(L, "invalid or empty time_area ID");
1192  }
1193  // We just need SOME location in that area, it doesn't matter which one.
1194  loc = *area.begin();
1195  } else if(!lua_isnil(L, 1)) {
1196  auto area = tod_man().get_area_by_index(luaW_check_schedule(L, 1));
1197  if(area.empty()) {
1198  return luaL_error(L, "empty time_area");
1199  }
1200  // We just need SOME location in that area, it doesn't matter which one.
1201  loc = *area.begin();
1202  }
1203 
1204  if(lua_isnumber(L, 2)) {
1205  for_turn = luaL_checkinteger(L, 2);
1206  int number_of_turns = tod_man().number_of_turns();
1207  if(for_turn < 1 || (number_of_turns != -1 && for_turn > number_of_turns)) {
1208  return luaL_argerror(L, 2, "turn number out of range");
1209  }
1210  }
1211 
1212  const time_of_day& tod = consider_illuminates ?
1213  tod_man().get_illuminated_time_of_day(board().units(), board().map(), loc, for_turn) :
1214  tod_man().get_time_of_day(loc, for_turn);
1215 
1216  luaW_push_tod(L, tod);
1217 
1218  return 1;
1219 }
1220 
1221 /**
1222  * Gets the side of a village owner.
1223  * - Arg 1: map location.
1224  * - Ret 1: integer.
1225  */
1227 {
1229  if (!board().map().is_village(loc))
1230  return 0;
1231 
1232  int side = board().village_owner(loc);
1233  if (!side) return 0;
1234  lua_pushinteger(L, side);
1235  return 1;
1236 }
1237 
1238 /**
1239  * Sets the owner of a village.
1240  * - Arg 1: map location.
1241  * - Arg 2: integer for the side or empty to remove ownership.
1242  */
1244 {
1246  if(!board().map().is_village(loc)) {
1247  return 0;
1248  }
1249 
1250  const int old_side_num = board().village_owner(loc);
1251  const int new_side_num = lua_isnoneornil(L, 2) ? 0 : luaL_checkinteger(L, 2);
1252 
1253  team* old_side = nullptr;
1254  team* new_side = nullptr;
1255 
1256  if(old_side_num == new_side_num) {
1257  return 0;
1258  }
1259 
1260  try {
1261  old_side = &board().get_team(old_side_num);
1262  } catch(const std::out_of_range&) {
1263  // old_side_num is invalid, most likely because the village wasn't captured.
1264  old_side = nullptr;
1265  }
1266 
1267  try {
1268  new_side = &board().get_team(new_side_num);
1269  } catch(const std::out_of_range&) {
1270  // new_side_num is invalid.
1271  new_side = nullptr;
1272  }
1273 
1274  // The new side was valid, but already defeated. Do nothing.
1275  if(new_side && board().team_is_defeated(*new_side)) {
1276  return 0;
1277  }
1278 
1279  // Even if the new side is not valid, we still want to remove the village from the old side.
1280  // This covers the case where new_side_num equals 0. The behavior in that case is to simply
1281  // un-assign the village from the old side, which of course we also want to happen if the new
1282  // side IS valid. If the village in question hadn't been captured, this won't fire (old_side
1283  // will be a nullptr).
1284  if(old_side) {
1285  old_side->lose_village(loc);
1286  }
1287 
1288  // If the new side was valid, re-assign the village.
1289  if(new_side) {
1290  new_side->get_village(loc, old_side_num, (luaW_toboolean(L, 3) ? &gamedata() : nullptr));
1291  }
1292 
1293  return 0;
1294 }
1295 
1296 /**
1297  * Returns the currently overed tile.
1298  * - Ret 1: x.
1299  * - Ret 2: y.
1300  */
1302 {
1303  if (!game_display_) {
1304  return 0;
1305  }
1306 
1308  if (!board().map().on_board(loc)) return 0;
1309  lua_pushinteger(L, loc.wml_x());
1310  lua_pushinteger(L, loc.wml_y());
1311  return 2;
1312 }
1313 
1314 /**
1315  * Returns the currently selected tile.
1316  * - Ret 1: x.
1317  * - Ret 2: y.
1318  */
1320 {
1321  if (!game_display_) {
1322  return 0;
1323  }
1324 
1326  if (!board().map().on_board(loc)) return 0;
1327  lua_pushinteger(L, loc.wml_x());
1328  lua_pushinteger(L, loc.wml_y());
1329  return 2;
1330 }
1331 
1332 
1333 /**
1334  * Gets a table for an resource tag.
1335  * - Arg 1: userdata (ignored).
1336  * - Arg 2: string containing id of the desired resource
1337  * - Ret 1: config for the era
1338  */
1339 static int intf_get_resource(lua_State *L)
1340 {
1341  std::string m = luaL_checkstring(L, 1);
1342  if(auto res = game_config_manager::get()->game_config().find_child("resource","id",m)) {
1343  luaW_pushconfig(L, *res);
1344  return 1;
1345  }
1346  else {
1347  return luaL_argerror(L, 1, ("Cannot find resource with id '" + m + "'").c_str());
1348  }
1349 }
1350 
1351 /**
1352  * Gets a table for an era tag.
1353  * - Arg 1: userdata (ignored).
1354  * - Arg 2: string containing id of the desired era
1355  * - Ret 1: config for the era
1356  */
1357 static int intf_get_era(lua_State *L)
1358 {
1359  std::string m = luaL_checkstring(L, 1);
1360  if(auto res = game_config_manager::get()->game_config().find_child("era","id",m)) {
1361  luaW_pushconfig(L, *res);
1362  return 1;
1363  }
1364  else {
1365  return luaL_argerror(L, 1, ("Cannot find era with id '" + m + "'").c_str());
1366  }
1367  return 1;
1368 }
1369 
1370 extern luaW_Registry& gameConfigReg();
1371 static auto& dummy = gameConfigReg(); // just to ensure it's constructed.
1372 
1376  auto& pc() const { return ref.play_controller_; }
1377  auto& gamedata() const { return ref.gamedata(); }
1378  auto& disp() const { return ref.game_display_; }
1379 };
1380 #define GAME_CONFIG_SIMPLE_SETTER(name) \
1381 GAME_CONFIG_SETTER(#name, decltype(game_config::name), game_lua_kernel) { \
1382  (void) k; \
1383  game_config::name = value; \
1384 }
1385 
1386 namespace {
1387 GAME_CONFIG_GETTER("do_healing", bool, game_lua_kernel) {
1388  game_config_glk_tag k2{k.ref};
1389  return k2.pc().gamestate().do_healing_;
1390 }
1391 
1392 GAME_CONFIG_SETTER("do_healing", bool, game_lua_kernel) {
1393  game_config_glk_tag k2{k.ref};
1394  k2.pc().gamestate().do_healing_ = value;}
1395 
1396 GAME_CONFIG_GETTER("theme", std::string, game_lua_kernel) {
1397  game_config_glk_tag k2{k.ref};
1398  return k2.gamedata().get_theme();
1399 }
1400 
1401 GAME_CONFIG_SETTER("theme", std::string, game_lua_kernel) {
1402  game_config_glk_tag k2{k.ref};
1403  k2.gamedata().set_theme(value);
1404  k2.disp()->set_theme(value);
1405 }
1406 
1407 using traits_map = std::map<std::string, config>;
1408 GAME_CONFIG_GETTER("global_traits", traits_map, game_lua_kernel) {
1409  (void)k;
1410  std::map<std::string, config> result;
1411  for(const config& trait : unit_types.traits()) {
1412  //It seems the engine never checks the id field for emptiness or duplicates
1413  //However, the worst that could happen is that the trait read later overwrites the older one,
1414  //and this is not the right place for such checks.
1415  result.emplace(trait["id"], trait);
1416  }
1417  return result;
1418 }
1419 
1428 }
1429 
1430 namespace {
1431  static config find_addon(const std::string& type, const std::string& id)
1432  {
1434  }
1435 }
1436 
1437 static int impl_end_level_data_get(lua_State* L)
1438 {
1439  const end_level_data& data = *static_cast<end_level_data*>(lua_touserdata(L, 1));
1440  const char* m = luaL_checkstring(L, 2);
1441 
1442  return_bool_attrib("linger_mode", data.transient.linger_mode);
1443  return_bool_attrib("reveal_map", data.transient.reveal_map);
1444  return_bool_attrib("carryover_report", data.transient.carryover_report);
1445  return_bool_attrib("prescenario_save", data.prescenario_save);
1446  return_bool_attrib("replay_save", data.replay_save);
1447  return_bool_attrib("proceed_to_next_level", data.proceed_to_next_level);
1448  return_bool_attrib("is_victory", data.is_victory);
1449  return_bool_attrib("is_loss", !data.is_victory);
1450  return_cstring_attrib("result", data.is_victory ? level_result::victory : "loss"); // to match wesnoth.end_level()
1451  return_string_attrib("test_result", data.test_result);
1452  return_cfg_attrib("__cfg", data.to_config_full());
1453 
1454  return 0;
1455 }
1456 
1457 namespace {
1458  struct end_level_committer {
1459  end_level_committer(end_level_data& data, play_controller& pc) : data_(data), pc_(pc) {}
1460  ~end_level_committer() {
1461  pc_.set_end_level_data(data_);
1462  }
1463  private:
1464  end_level_data& data_;
1465  play_controller& pc_;
1466  };
1467 }
1468 
1470 {
1471  end_level_data& data = *static_cast<end_level_data*>(lua_touserdata(L, 1));
1472  const char* m = luaL_checkstring(L, 2);
1473  end_level_committer commit(data, play_controller_);
1474 
1475  modify_bool_attrib("linger_mode", data.transient.linger_mode = value);
1476  modify_bool_attrib("reveal_map", data.transient.reveal_map = value);
1477  modify_bool_attrib("carryover_report", data.transient.carryover_report = value);
1478  modify_bool_attrib("prescenario_save", data.prescenario_save = value);
1479  modify_bool_attrib("replay_save", data.replay_save = value);
1480  modify_string_attrib("test_result", data.test_result = value);
1481 
1482  return 0;
1483 }
1484 
1485 static int impl_end_level_data_collect(lua_State* L)
1486 {
1487  end_level_data* data = static_cast<end_level_data*>(lua_touserdata(L, 1));
1488  data->~end_level_data();
1489  return 0;
1490 }
1491 
1492 static int impl_mp_settings_get(lua_State* L)
1493 {
1494  void* p = lua_touserdata(L, lua_upvalueindex(1));
1495  const mp_game_settings& settings = static_cast<play_controller*>(p)->get_mp_settings();
1496  if(lua_type(L, 2) == LUA_TNUMBER) {
1497  // Simulates a WML table with one [options] child and a variable number of [addon] children
1498  // TODO: Deprecate this -> mp_settings.options and mp_settings.addons
1499  size_t i = luaL_checkinteger(L, 2);
1500  if(i == 1) {
1501  lua_createtable(L, 2, 0);
1502  lua_pushstring(L, "options");
1503  lua_seti(L, -2, 1);
1504  luaW_pushconfig(L, settings.options);
1505  lua_seti(L, -2, 2);
1506  return 1;
1507  } else if(i >= 2) {
1508  i -= 2;
1509  if(i < settings.addons.size()) {
1510  auto iter = settings.addons.begin();
1511  std::advance(iter, i);
1512  config cfg;
1513  iter->second.write(cfg);
1514  cfg["id"] = iter->first;
1515 
1516  lua_createtable(L, 2, 0);
1517  lua_pushstring(L, "addon");
1518  lua_seti(L, -2, 1);
1519  luaW_pushconfig(L, cfg);
1520  lua_seti(L, -2, 2);
1521  return 1;
1522  }
1523  }
1524  } else {
1525  char const *m = luaL_checkstring(L, 2);
1526  return_string_attrib("scenario", settings.name);
1527  return_string_attrib("game_name", settings.name);
1528  return_string_attrib("hash", settings.hash);
1529  return_string_attrib("mp_era_name", settings.mp_era_name);
1530  return_string_attrib("mp_scenario", settings.mp_scenario);
1531  return_string_attrib("mp_scenario_name", settings.mp_scenario_name);
1532  return_string_attrib("mp_campaign", settings.mp_campaign);
1533  return_string_attrib("side_users", utils::join_map(settings.side_users));
1534  return_int_attrib("experience_modifier", settings.xp_modifier);
1535  return_bool_attrib("mp_countdown", settings.mp_countdown);
1536  return_int_attrib("mp_countdown_init_time", settings.mp_countdown_init_time.count());
1537  return_int_attrib("mp_countdown_turn_bonus", settings.mp_countdown_turn_bonus.count());
1538  return_int_attrib("mp_countdown_reservoir_bonus", settings.mp_countdown_reservoir_time.count());
1539  return_int_attrib("mp_countdown_action_bonus", settings.mp_countdown_action_bonus.count());
1540  return_int_attrib("mp_num_turns", settings.num_turns);
1541  return_int_attrib("mp_village_gold", settings.village_gold);
1542  return_int_attrib("mp_village_support", settings.village_support);
1543  return_bool_attrib("mp_fog", settings.fog_game);
1544  return_bool_attrib("mp_shroud", settings.shroud_game);
1545  return_bool_attrib("mp_use_map_settings", settings.use_map_settings);
1546  return_bool_attrib("mp_random_start_time", settings.random_start_time);
1547  return_bool_attrib("observer", settings.allow_observers);
1548  return_bool_attrib("allow_observers", settings.allow_observers);
1549  return_bool_attrib("private_replay", settings.private_replay);
1550  return_bool_attrib("shuffle_sides", settings.shuffle_sides);
1551  return_string_attrib("random_faction_mode", random_faction_mode::get_string(settings.mode));
1552  return_cfgref_attrib("options", settings.options);
1553  if(strcmp(m, "savegame") == 0) {
1554  auto savegame = settings.saved_game;
1555  if(savegame == saved_game_mode::type::no) {
1556  lua_pushboolean(L, false);
1557  } else {
1559  }
1560  return 1;
1561  }
1562  if(strcmp(m, "side_players") == 0) {
1563  lua_push(L, settings.side_users);
1564  return 1;
1565  }
1566  if(strcmp(m, "addons") == 0) {
1567  for(const auto& [id, addon] : settings.addons) {
1568  lua_createtable(L, 0, 4);
1569  lua_push(L, id);
1570  lua_setfield(L, -2, "id");
1571  lua_push(L, addon.name);
1572  lua_setfield(L, -2, "name");
1573  lua_pushboolean(L, addon.required);
1574  lua_setfield(L, -2, "required");
1575  if(addon.min_version) {
1576  luaW_getglobal(L, "wesnoth", "version");
1577  lua_push(L, addon.min_version->str());
1578  lua_call(L, 1, 1);
1579  lua_setfield(L, -2, "min_version");
1580  }
1581  if(addon.version) {
1582  luaW_getglobal(L, "wesnoth", "version");
1583  lua_push(L, addon.version->str());
1584  lua_call(L, 1, 1);
1585  lua_setfield(L, -2, "version");
1586  }
1587  lua_createtable(L, addon.content.size(), 0);
1588  for(const auto& content : addon.content) {
1589  lua_createtable(L, 0, 3);
1590  lua_push(L, content.id);
1591  lua_setfield(L, -2, "id");
1592  lua_push(L, content.name);
1593  lua_setfield(L, -2, "name");
1594  lua_push(L, content.type);
1595  lua_setfield(L, -2, "type");
1596  lua_seti(L, -2, lua_rawlen(L, -2) + 1);
1597  }
1598  lua_setfield(L, -2, "content");
1599  }
1600  return 1;
1601  }
1602  // Deprecated things that were moved out of mp_settings and into game_classification
1603  const game_classification& game = static_cast<play_controller*>(p)->get_classification();
1604  return_string_attrib_deprecated("mp_era", "wesnoth.scenario.mp_settings", INDEFINITE, "1.17", "Use wesnoth.scenario.era.id instead", game.era_id);
1605  return_string_attrib_deprecated("active_mods", "wesnoth.scenario.mp_settings", INDEFINITE, "1.17", "Use wesnoth.scenario.modifications instead (returns an array of modification tables)", utils::join(game.active_mods));
1606  // Expose the raw config; this is a way to ensure any new stuff can be accessed even if someone forgot to add it here.
1607  return_cfgref_attrib("__cfg", settings.to_config());
1608  }
1609  return 0;
1610 }
1611 
1612 static int impl_mp_settings_len(lua_State* L)
1613 {
1614  void* p = lua_touserdata(L, lua_upvalueindex(1));
1615  const mp_game_settings& settings = static_cast<play_controller*>(p)->get_mp_settings();
1616  lua_pushinteger(L, settings.addons.size() + 1);
1617  return 1;
1618 }
1619 
1623  auto& tod_man() const { return ref.tod_man(); }
1624  auto& gamedata() const { return ref.gamedata(); }
1625  auto& pc() const { return ref.play_controller_; }
1626  auto& cls() const { return ref.play_controller_.get_classification(); }
1627  auto end_level_set() const { return &dispatch<&game_lua_kernel::impl_end_level_data_set>; }
1628 };
1629 #define SCENARIO_GETTER(name, type) LATTR_GETTER(name, type, scenario_tag, k)
1630 #define SCENARIO_SETTER(name, type) LATTR_SETTER(name, type, scenario_tag, k)
1631 #define SCENARIO_VALID(name) LATTR_VALID(name, scenario_tag, k)
1633 
1634 template<> struct lua_object_traits<scenario_tag> {
1635  inline static auto metatable = "scenario";
1636  inline static scenario_tag get(lua_State* L, int) {
1637  return lua_kernel_base::get_lua_kernel<game_lua_kernel>(L);
1638  }
1639 };
1640 
1641 namespace {
1642 SCENARIO_GETTER("turns", int) {
1643  return k.tod_man().number_of_turns();
1644 }
1645 
1646 SCENARIO_SETTER("turns", int) {
1647  k.tod_man().set_number_of_turns_by_wml(value);
1648 }
1649 
1650 SCENARIO_GETTER("next", std::string) {
1651  return k.gamedata().next_scenario();
1652 }
1653 
1654 SCENARIO_SETTER("next", std::string) {
1655  k.gamedata().set_next_scenario(value);
1656 }
1657 
1658 SCENARIO_GETTER("id", std::string) {
1659  return k.gamedata().get_id();
1660 }
1661 
1662 SCENARIO_GETTER("name", t_string) {
1663  return k.pc().get_scenario_name();
1664 }
1665 
1666 SCENARIO_GETTER("defeat_music", std::vector<std::string>) {
1667  return k.gamedata().get_defeat_music();
1668 }
1669 
1670 SCENARIO_SETTER("defeat_music", std::vector<std::string>) {
1671  k.gamedata().set_defeat_music(value);
1672 }
1673 
1674 SCENARIO_GETTER("victory_music", std::vector<std::string>) {
1675  return k.gamedata().get_victory_music();
1676 }
1677 
1678 SCENARIO_SETTER("victory_music", std::vector<std::string>) {
1679  k.gamedata().set_victory_music(value);
1680 }
1681 
1682 SCENARIO_GETTER("resources", std::vector<config>) {
1683  std::vector<config> resources;
1684  for(const std::string& rsrc : utils::split(k.pc().get_loaded_resources())) {
1685  resources.push_back(find_addon("resource", rsrc));
1686  }
1687  return resources;
1688 }
1689 
1690 SCENARIO_GETTER("type", std::string) {
1691  return campaign_type::get_string(k.cls().type);
1692 }
1693 
1694 SCENARIO_GETTER("difficulty", std::string) {
1695  return k.cls().difficulty;
1696 }
1697 
1698 SCENARIO_GETTER("show_credits", bool) {
1699  return k.cls().end_credits;
1700 }
1701 
1702 SCENARIO_SETTER("show_credits", bool) {
1703  k.cls().end_credits = value;
1704 }
1705 
1706 SCENARIO_GETTER("end_text", t_string) {
1707  return k.cls().end_text;
1708 }
1709 
1710 SCENARIO_SETTER("end_text", t_string) {
1711  k.cls().end_text = value;
1712 }
1713 
1714 SCENARIO_GETTER("end_text_duration", int) {
1715  return k.cls().end_text_duration.count();
1716 }
1717 
1718 SCENARIO_SETTER("end_text_duration", int) {
1719  k.cls().end_text_duration = std::chrono::milliseconds{value};
1720 }
1721 
1722 SCENARIO_VALID("campaign") {
1723  return !k.cls().campaign.empty();
1724 }
1725 
1726 SCENARIO_GETTER("campaign", utils::optional<config>) {
1727  if(k.cls().campaign.empty()) return utils::nullopt;
1728  return find_addon("campaign", k.cls().campaign);
1729 }
1730 
1731 SCENARIO_GETTER("modifications", std::vector<config>) {
1732  std::vector<config> mods;
1733  for(const std::string& mod : k.cls().active_mods) {
1734  mods.push_back(find_addon("modification", mod));
1735  }
1736  return mods;
1737 }
1738 
1739 SCENARIO_GETTER("end_level_data", lua_index_raw) {
1740  if (!k.pc().is_regular_game_end()) {
1741  lua_pushnil(L);
1742  return lua_index_raw(L);
1743  }
1744  auto data = k.pc().get_end_level_data();
1745  new(L) end_level_data(data);
1746  if(luaL_newmetatable(L, "end level data")) {
1747  static luaL_Reg const callbacks[] {
1748  { "__index", &impl_end_level_data_get},
1749  { "__newindex", k.end_level_set()},
1750  { "__gc", &impl_end_level_data_collect},
1751  { nullptr, nullptr }
1752  };
1753  luaL_setfuncs(L, callbacks, 0);
1754  }
1755  lua_setmetatable(L, -2);
1756  return lua_index_raw(L);
1757 }
1758 
1759 SCENARIO_SETTER("end_level_data", vconfig) {
1761 
1762  data.proceed_to_next_level = value["proceed_to_next_level"].to_bool(true);
1763  data.transient.carryover_report = value["carryover_report"].to_bool(true);
1764  data.prescenario_save = value["save"].to_bool(true);
1765  data.replay_save = value["replay_save"].to_bool(true);
1766  data.transient.linger_mode = value["linger_mode"].to_bool(true) && !k.ref.teams().empty();
1767  data.transient.reveal_map = value["reveal_map"].to_bool(k.pc().reveal_map_default());
1768  data.is_victory = value["result"] == level_result::victory;
1769  data.test_result = value["test_result"].str();
1770  k.pc().set_end_level_data(data);
1771 }
1772 
1773 SCENARIO_VALID("mp_settings") {
1774  return k.cls().is_multiplayer();
1775 }
1776 
1777 SCENARIO_GETTER("mp_settings", lua_index_raw) {
1778  if(!k.cls().is_multiplayer()) {
1779  lua_pushnil(L);
1780  return lua_index_raw(L);
1781  }
1782  lua_newuserdatauv(L, 0, 0);
1783  if(luaL_newmetatable(L, "mp settings")) {
1784  lua_pushlightuserdata(L, &k.pc());
1785  lua_pushcclosure(L, impl_mp_settings_get, 1);
1786  lua_setfield(L, -2, "__index");
1787  lua_pushlightuserdata(L, &k.pc());
1788  lua_pushcclosure(L, impl_mp_settings_len, 1);
1789  lua_setfield(L, -2, "__len");
1790  lua_pushstring(L, "mp settings");
1791  lua_setfield(L, -2, "__metatable");
1792  }
1793  lua_setmetatable(L, -2);
1794  return lua_index_raw(L);
1795 }
1796 
1797 SCENARIO_VALID("era") {
1798  return k.cls().is_multiplayer();
1799 }
1800 
1801 SCENARIO_GETTER("era", utils::optional<config>) {
1802  if(!k.cls().is_multiplayer()) return utils::nullopt;
1803  return find_addon("era", k.cls().era_id);
1804 }
1805 }
1806 
1807 /**
1808  * Gets some scenario data (__index metamethod).
1809  * - Arg 1: userdata (ignored).
1810  * - Arg 2: string containing the name of the property.
1811  * - Ret 1: something containing the attribute.
1812  */
1814 {
1815  DBG_LUA << "impl_scenario_get";
1816  return scenarioReg.get(L);
1817 }
1818 
1819 /**
1820  * Sets some scenario data (__newindex metamethod).
1821  * - Arg 1: userdata (ignored).
1822  * - Arg 2: string containing the name of the property.
1823  * - Arg 3: something containing the attribute.
1824  */
1826 {
1827  DBG_LUA << "impl_scenario_set";
1828  return scenarioReg.set(L);
1829 }
1830 
1831 /**
1832  * Get a list of scenario data (__dir metamethod).
1833  */
1835 {
1836  DBG_LUA << "impl_scenario_dir";
1837  return scenarioReg.dir(L);
1838 }
1839 
1840 /**
1841  converts synced_context::get_synced_state() to a string.
1842 */
1844 {
1845  //maybe return "initial" for game_data::INITIAL?
1846  if(gamedata().phase() == game_data::PRELOAD || gamedata().phase() == game_data::INITIAL)
1847  {
1848  return "preload";
1849  }
1851  {
1853  return "local_choice";
1855  return "synced";
1857  return "unsynced";
1858  default:
1859  throw game::game_error("Found corrupt synced_context::synced_state");
1860  }
1861 }
1862 
1863 struct current_tag {
1866  auto& pc() const { return ref.play_controller_; }
1867  auto ss() const { return ref.synced_state(); }
1868  auto& gd() const { return ref.gamedata(); }
1869  auto& ev() const { return ref.get_event_info(); }
1870  void push_schedule(lua_State* L) const { ref.luaW_push_schedule(L, -1); }
1871 };
1872 #define CURRENT_GETTER(name, type) LATTR_GETTER(name, type, current_tag, k)
1874 
1875 template<> struct lua_object_traits<current_tag> {
1876  inline static auto metatable = "current";
1877  inline static game_lua_kernel& get(lua_State* L, int) {
1878  return lua_kernel_base::get_lua_kernel<game_lua_kernel>(L);
1879  }
1880 };
1881 
1882 namespace {
1883 CURRENT_GETTER("side", int) {
1884  return k.pc().current_side();
1885 }
1886 
1887 CURRENT_GETTER("turn", int) {
1888  return k.pc().turn();
1889 }
1890 
1891 CURRENT_GETTER("synced_state", std::string) {
1892  return k.ss();
1893 }
1894 
1895 CURRENT_GETTER("user_can_invoke_commands", bool) {
1896  return !events::commands_disabled && k.gd().phase() == game_data::TURN_PLAYING;
1897 }
1898 
1899 CURRENT_GETTER("map", lua_index_raw) {
1900  (void)k;
1902  return lua_index_raw(L);
1903 }
1904 
1905 CURRENT_GETTER("schedule", lua_index_raw) {
1906  k.push_schedule(L);
1907  return lua_index_raw(L);
1908 }
1909 
1910 CURRENT_GETTER("user_is_replaying", bool) {
1911  return k.pc().is_replay();
1912 }
1913 
1914 CURRENT_GETTER("event_context", config) {
1915  const game_events::queued_event &ev = k.ev();
1916  config cfg;
1917  cfg["name"] = ev.name;
1918  cfg["id"] = ev.id;
1919  cfg.add_child("data", ev.data);
1920  if (auto weapon = ev.data.optional_child("first")) {
1921  cfg.add_child("weapon", *weapon);
1922  }
1923  if (auto weapon = ev.data.optional_child("second")) {
1924  cfg.add_child("second_weapon", *weapon);
1925  }
1926 
1927  const config::attribute_value di = ev.data["damage_inflicted"];
1928  if(!di.empty()) {
1929  cfg["damage_inflicted"] = di;
1930  }
1931 
1932  if (ev.loc1.valid()) {
1933  cfg["x1"] = ev.loc1.filter_loc().wml_x();
1934  cfg["y1"] = ev.loc1.filter_loc().wml_y();
1935  // 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
1936  cfg["unit_x"] = ev.loc1.wml_x();
1937  cfg["unit_y"] = ev.loc1.wml_y();
1938  }
1939  if (ev.loc2.valid()) {
1940  cfg["x2"] = ev.loc2.filter_loc().wml_x();
1941  cfg["y2"] = ev.loc2.filter_loc().wml_y();
1942  }
1943  return cfg;
1944 }
1945 }
1946 
1947 /**
1948  * Gets some data about current point of game (__index metamethod).
1949  * - Arg 1: userdata (ignored).
1950  * - Arg 2: string containing the name of the property.
1951  * - Ret 1: something containing the attribute.
1952  */
1954 {
1955  return currentReg.get(L);
1956 }
1957 
1958 /**
1959  * Gets a list of date about current point of game (__dir metamethod).
1960  */
1962 {
1963  return currentReg.dir(L);
1964 }
1965 
1966 /**
1967  * Displays a message in the chat window and in the logs.
1968  * - Arg 1: optional message header.
1969  * - Arg 2 (or 1): message.
1970  */
1972 {
1973  t_string m = luaW_checktstring(L, 1);
1974  t_string h = m;
1975  if (lua_isnone(L, 2)) {
1976  h = "Lua";
1977  } else {
1978  m = luaW_checktstring(L, 2);
1979  }
1980  lua_chat(h, m);
1981  LOG_LUA << "Script says: \"" << m << "\"";
1982  return 0;
1983 }
1984 
1986 {
1987  if(!game_display_) {
1988  return 0;
1989  }
1990  double factor = luaL_checknumber(L, 1);
1991  bool relative = luaW_toboolean(L, 2);
1992  if(relative) {
1993  factor *= game_display_->get_zoom_factor();
1994  }
1995  // Passing true explicitly to avoid casting to int.
1996  // Without doing one of the two, the call is ambiguous.
1998  lua_pushnumber(L, game_display_->get_zoom_factor());
1999  return 1;
2000 }
2001 
2002 /**
2003  * Removes all messages from the chat window.
2004  */
2006 {
2007  if (game_display_) {
2009  }
2010  return 0;
2011 }
2012 
2014 {
2015  //note that next_player_number = 1, next_player_number = nteams+1 both set the next team to be the first team
2016  //but the later will make the turn counter change aswell fire turn end events accoringly etc.
2017  if (!lua_isnoneornil(L, 1)) {
2018  int max = 2 * teams().size();
2019  int npn = luaL_checkinteger(L, 1);
2020  if (npn <= 0 || npn > max) {
2021  return luaL_argerror(L, 1, "side number out of range");
2022  }
2024  }
2026  return 0;
2027 }
2028 
2029 /**
2030  * Evaluates a boolean WML conditional.
2031  * - Arg 1: WML table.
2032  * - Ret 1: boolean.
2033  */
2034 static int intf_eval_conditional(lua_State *L)
2035 {
2036  vconfig cond = luaW_checkvconfig(L, 1);
2037  bool b = game_events::conditional_passed(cond);
2038  lua_pushboolean(L, b);
2039  return 1;
2040 }
2041 
2042 
2043 /**
2044  * Finds a path between two locations.
2045  * - Arg 1: source location. (Or Arg 1: unit.)
2046  * - Arg 2: destination.
2047  * - Arg 3: optional cost function or
2048  * table (optional fields: ignore_units, ignore_teleport, max_cost, viewing_side).
2049  * - Ret 1: array of pairs containing path steps.
2050  * - Ret 2: path cost.
2051  */
2053 {
2054  int arg = 1;
2055  map_location src, dst;
2056  const unit* u = nullptr;
2057  int viewing_side = 0;
2058 
2059  if (lua_isuserdata(L, arg))
2060  {
2061  u = &luaW_checkunit(L, arg);
2062  src = u->get_location();
2063  viewing_side = u->side();
2064  ++arg;
2065  }
2066  else
2067  {
2068  src = luaW_checklocation(L, arg);
2070  if (ui.valid()) {
2071  u = ui.get_shared_ptr().get();
2072  viewing_side = u->side();
2073  }
2074  ++arg;
2075  }
2076 
2077  dst = luaW_checklocation(L, arg);
2078 
2079  if (!board().map().on_board(src))
2080  return luaL_argerror(L, 1, "invalid location");
2081  if (!board().map().on_board(dst))
2082  return luaL_argerror(L, arg, "invalid location");
2083  ++arg;
2084 
2085  const gamemap &map = board().map();
2086  bool ignore_units = false, see_all = false, ignore_teleport = false;
2087  double stop_at = 10000;
2088  std::unique_ptr<pathfind::cost_calculator> calc;
2089 
2090  if (lua_istable(L, arg))
2091  {
2092  ignore_units = luaW_table_get_def<bool>(L, arg, "ignore_units", false);
2093  see_all = luaW_table_get_def<bool>(L, arg, "ignore_visibility", false);
2094  ignore_teleport = luaW_table_get_def<bool>(L, arg, "ignore_teleport", false);
2095 
2096  stop_at = luaW_table_get_def<double>(L, arg, "max_cost", stop_at);
2097 
2098 
2099  lua_pushstring(L, "viewing_side");
2100  lua_rawget(L, arg);
2101  if (!lua_isnil(L, -1)) {
2102  int i = luaL_checkinteger(L, -1);
2103  if (i >= 1 && i <= static_cast<int>(teams().size())) viewing_side = i;
2104  else {
2105  // If there's a unit, we have a valid side, so fall back to legacy behaviour.
2106  // If we don't have a unit, legacy behaviour would be a crash, so let's not.
2107  if(u) see_all = true;
2108  deprecated_message("wesnoth.paths.find_path with viewing_side=0 (or an invalid side)", DEP_LEVEL::FOR_REMOVAL, {1, 17, 0}, "To consider fogged and hidden units, use ignore_visibility=true instead.");
2109  }
2110  }
2111  lua_pop(L, 1);
2112 
2113  lua_pushstring(L, "calculate");
2114  lua_rawget(L, arg);
2115  if(lua_isfunction(L, -1)) {
2116  calc.reset(new lua_pathfind_cost_calculator(L, lua_gettop(L)));
2117  }
2118  // Don't pop, the lua_pathfind_cost_calculator requires it to stay on the stack.
2119  }
2120  else if (lua_isfunction(L, arg))
2121  {
2122  deprecated_message("wesnoth.paths.find_path with cost_function as last argument", DEP_LEVEL::FOR_REMOVAL, {1, 17, 0}, "Use calculate=cost_function inside the path options table instead.");
2123  calc.reset(new lua_pathfind_cost_calculator(L, arg));
2124  }
2125 
2126  pathfind::teleport_map teleport_locations;
2127 
2128  if(!ignore_teleport) {
2129  if(viewing_side == 0) {
2130  lua_warning(L, "wesnoth.paths.find_path: ignore_teleport=false requires a valid viewing_side; continuing with ignore_teleport=true", false);
2131  ignore_teleport = true;
2132  } else {
2133  teleport_locations = pathfind::get_teleport_locations(*u, board().get_team(viewing_side), see_all, ignore_units);
2134  }
2135  }
2136 
2137  if (!calc) {
2138  if(!u) {
2139  return luaL_argerror(L, 1, "unit not found OR custom cost function not provided");
2140  }
2141 
2142  calc.reset(new pathfind::shortest_path_calculator(*u, board().get_team(viewing_side),
2143  teams(), map, ignore_units, false, see_all));
2144  }
2145 
2146  pathfind::plain_route res = pathfind::a_star_search(src, dst, stop_at, *calc, map.w(), map.h(),
2147  &teleport_locations);
2148 
2149  int nb = res.steps.size();
2150  lua_createtable(L, nb, 0);
2151  for (int i = 0; i < nb; ++i)
2152  {
2153  luaW_pushlocation(L, res.steps[i]);
2154  lua_rawseti(L, -2, i + 1);
2155  }
2156  lua_pushinteger(L, res.move_cost);
2157 
2158  return 2;
2159 }
2160 
2161 /**
2162  * Finds all the locations reachable by a unit.
2163  * - Arg 1: source location OR unit.
2164  * - Arg 2: optional table (optional fields: ignore_units, ignore_teleport, additional_turns, viewing_side).
2165  * - Ret 1: array of triples (coordinates + remaining movement).
2166  */
2168 {
2169  int arg = 1;
2170  const unit* u = nullptr;
2171 
2172  if (lua_isuserdata(L, arg))
2173  {
2174  u = &luaW_checkunit(L, arg);
2175  ++arg;
2176  }
2177  else
2178  {
2181  if (!ui.valid())
2182  return luaL_argerror(L, 1, "unit not found");
2183  u = ui.get_shared_ptr().get();
2184  ++arg;
2185  }
2186 
2187  int viewing_side = u->side();
2188  bool ignore_units = false, see_all = false, ignore_teleport = false;
2189  int additional_turns = 0;
2190 
2191  if (lua_istable(L, arg))
2192  {
2193  ignore_units = luaW_table_get_def<bool>(L, arg, "ignore_units", false);
2194  see_all = luaW_table_get_def<bool>(L, arg, "ignore_visibility", false);
2195  ignore_teleport = luaW_table_get_def<bool>(L, arg, "ignore_teleport", false);
2196  additional_turns = luaW_table_get_def<int>(L, arg, "max_cost", additional_turns);
2197 
2198  lua_pushstring(L, "viewing_side");
2199  lua_rawget(L, arg);
2200  if (!lua_isnil(L, -1)) {
2201  int i = luaL_checkinteger(L, -1);
2202  if (i >= 1 && i <= static_cast<int>(teams().size())) viewing_side = i;
2203  else {
2204  // If there's a unit, we have a valid side, so fall back to legacy behaviour.
2205  // If we don't have a unit, legacy behaviour would be a crash, so let's not.
2206  if(u) see_all = true;
2207  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.");
2208  }
2209  }
2210  lua_pop(L, 1);
2211  }
2212 
2213  const team& viewing_team = board().get_team(viewing_side);
2214 
2215  pathfind::paths res(*u, ignore_units, !ignore_teleport,
2216  viewing_team, additional_turns, see_all, ignore_units);
2217 
2218  int nb = res.destinations.size();
2219  lua_createtable(L, nb, 0);
2220  for (int i = 0; i < nb; ++i)
2221  {
2223  luaW_push_namedtuple(L, {"x", "y", "moves_left"});
2224  lua_pushinteger(L, s.curr.wml_x());
2225  lua_rawseti(L, -2, 1);
2226  lua_pushinteger(L, s.curr.wml_y());
2227  lua_rawseti(L, -2, 2);
2228  lua_pushinteger(L, s.move_left);
2229  lua_rawseti(L, -2, 3);
2230  lua_rawseti(L, -2, i + 1);
2231  }
2232 
2233  return 1;
2234 }
2235 
2236 /**
2237  * Finds all the locations for which a given unit would remove the fog (if there was fog on the map).
2238  *
2239  * - Arg 1: source location OR unit.
2240  * - Ret 1: array of triples (coordinates + remaining vision points).
2241  */
2243 {
2244  int arg = 1;
2245  const unit* u = nullptr;
2246 
2247  if (lua_isuserdata(L, arg))
2248  {
2249  u = &luaW_checkunit(L, arg);
2250  ++arg;
2251  }
2252  else
2253  {
2256  if (!ui.valid())
2257  return luaL_argerror(L, 1, "unit not found");
2258  u = ui.get_shared_ptr().get();
2259  ++arg;
2260  }
2261 
2262  if(!u)
2263  {
2264  return luaL_error(L, "wesnoth.find_vision_range: requires a valid unit");
2265  }
2266 
2267  std::map<map_location, int> jamming_map;
2268  actions::create_jamming_map(jamming_map, resources::gameboard->get_team(u->side()));
2269  pathfind::vision_path res(*u, u->get_location(), jamming_map);
2270 
2271  lua_createtable(L, res.destinations.size() + res.edges.size(), 0);
2272  for(const auto& d : res.destinations) {
2273  luaW_push_namedtuple(L, {"x", "y", "vision_left"});
2274  lua_pushinteger(L, d.curr.wml_x());
2275  lua_rawseti(L, -2, 1);
2276  lua_pushinteger(L, d.curr.wml_y());
2277  lua_rawseti(L, -2, 2);
2278  lua_pushinteger(L, d.move_left);
2279  lua_rawseti(L, -2, 3);
2280  lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2281  }
2282  for(const auto& e : res.edges) {
2283  luaW_push_namedtuple(L, {"x", "y", "vision_left"});
2284  lua_pushinteger(L, e.wml_x());
2285  lua_rawseti(L, -2, 1);
2286  lua_pushinteger(L, e.wml_y());
2287  lua_rawseti(L, -2, 2);
2288  lua_pushinteger(L, -1);
2289  lua_rawseti(L, -2, 3);
2290  lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
2291  }
2292  return 1;
2293 }
2294 
2295 template<typename T> // This is only a template so I can avoid typing out the long typename. >_>
2296 static int load_fake_units(lua_State* L, int arg, T& fake_units)
2297 {
2298  for (int i = 1, i_end = lua_rawlen(L, arg); i <= i_end; ++i)
2299  {
2300  map_location src;
2301  lua_rawgeti(L, arg, i);
2302  int entry = lua_gettop(L);
2303  if (!lua_istable(L, entry)) {
2304  goto error;
2305  }
2306 
2307  if (!luaW_tolocation(L, entry, src)) {
2308  goto error;
2309  }
2310 
2311  lua_rawgeti(L, entry, 3);
2312  if (!lua_isnumber(L, -1)) {
2313  lua_getfield(L, entry, "side");
2314  if (!lua_isnumber(L, -1)) {
2315  goto error;
2316  }
2317  }
2318  int side = lua_tointeger(L, -1);
2319 
2320  lua_rawgeti(L, entry, 4);
2321  if (!lua_isstring(L, -1)) {
2322  lua_getfield(L, entry, "type");
2323  if (!lua_isstring(L, -1)) {
2324  goto error;
2325  }
2326  }
2327  std::string unit_type = lua_tostring(L, -1);
2328 
2329  fake_units.emplace_back(src, side, unit_type);
2330 
2331  lua_settop(L, entry - 1);
2332  }
2333  return 0;
2334 error:
2335  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");
2336 }
2337 
2338 /**
2339  * Is called with one or more units and builds a cost map.
2340  * - Arg 1: source location. (Or Arg 1: unit. Or Arg 1: table containing a filter)
2341  * - Arg 2: optional array of tables with 4 elements (coordinates + side + unit type string)
2342  * - Arg 3: optional table (optional fields: ignore_units, ignore_teleport, viewing_side, debug).
2343  * - Arg 4: optional table: standard location filter.
2344  * - Ret 1: array of triples (coordinates + array of tuples(summed cost + reach counter)).
2345  */
2347 {
2348  int arg = 1;
2349  unit* unit = luaW_tounit(L, arg, true);
2351  luaW_tovconfig(L, arg, filter);
2352 
2353  std::vector<const ::unit*> real_units;
2354  typedef std::vector<std::tuple<map_location, int, std::string>> unit_type_vector;
2355  unit_type_vector fake_units;
2356 
2357 
2358  if (unit) // 1. arg - unit
2359  {
2360  real_units.push_back(unit);
2361  }
2362  else if (!filter.null()) // 1. arg - filter
2363  {
2364  for(const ::unit* match : unit_filter(filter).all_matches_on_map()) {
2365  if(match->get_location().valid()) {
2366  real_units.push_back(match);
2367  }
2368  }
2369  }
2370  else // 1. arg - coordinates
2371  {
2374  if (ui.valid())
2375  {
2376  real_units.push_back(&(*ui));
2377  }
2378  }
2379  ++arg;
2380 
2381  if (lua_istable(L, arg)) // 2. arg - optional types
2382  {
2383  load_fake_units(L, arg, fake_units);
2384  ++arg;
2385  }
2386 
2387  if(real_units.empty() && fake_units.empty())
2388  {
2389  return luaL_argerror(L, 1, "unit(s) not found");
2390  }
2391 
2392  int viewing_side = 0;
2393  bool ignore_units = true, see_all = true, ignore_teleport = false, debug = false, use_max_moves = false;
2394 
2395  if (lua_istable(L, arg)) // 4. arg - options
2396  {
2397  lua_pushstring(L, "ignore_units");
2398  lua_rawget(L, arg);
2399  if (!lua_isnil(L, -1))
2400  {
2401  ignore_units = luaW_toboolean(L, -1);
2402  }
2403  lua_pop(L, 1);
2404 
2405  lua_pushstring(L, "ignore_teleport");
2406  lua_rawget(L, arg);
2407  if (!lua_isnil(L, -1))
2408  {
2409  ignore_teleport = luaW_toboolean(L, -1);
2410  }
2411  lua_pop(L, 1);
2412 
2413  lua_pushstring(L, "viewing_side");
2414  lua_rawget(L, arg);
2415  if (!lua_isnil(L, -1))
2416  {
2417  int i = luaL_checkinteger(L, -1);
2418  if (i >= 1 && i <= static_cast<int>(teams().size()))
2419  {
2420  viewing_side = i;
2421  see_all = false;
2422  }
2423  }
2424 
2425  lua_pushstring(L, "debug");
2426  lua_rawget(L, arg);
2427  if (!lua_isnil(L, -1))
2428  {
2429  debug = luaW_toboolean(L, -1);
2430  }
2431  lua_pop(L, 1);
2432 
2433  lua_pushstring(L, "use_max_moves");
2434  lua_rawget(L, arg);
2435  if (!lua_isnil(L, -1))
2436  {
2437  use_max_moves = luaW_toboolean(L, -1);
2438  }
2439  lua_pop(L, 1);
2440  ++arg;
2441  }
2442 
2443  // 5. arg - location filter
2445  std::set<map_location> location_set;
2446  luaW_tovconfig(L, arg, filter);
2447  if (filter.null())
2448  {
2449  filter = vconfig(config(), true);
2450  }
2451  filter_context & fc = game_state_;
2452  const terrain_filter t_filter(filter, &fc, false);
2453  t_filter.get_locations(location_set, true);
2454  ++arg;
2455 
2456  // build cost_map
2457  const team& viewing_team = viewing_side
2458  ? board().get_team(viewing_side)
2459  : board().teams()[0];
2460 
2461  pathfind::full_cost_map cost_map(
2462  ignore_units, !ignore_teleport, viewing_team, see_all, ignore_units);
2463 
2464  for (const ::unit* const u : real_units)
2465  {
2466  cost_map.add_unit(*u, use_max_moves);
2467  }
2468  for (const unit_type_vector::value_type& fu : fake_units)
2469  {
2470  const unit_type* ut = unit_types.find(std::get<2>(fu));
2471  cost_map.add_unit(std::get<0>(fu), ut, std::get<1>(fu));
2472  }
2473 
2474  if (debug)
2475  {
2476  if (game_display_) {
2478  for (const map_location& loc : location_set)
2479  {
2480  std::stringstream s;
2481  s << cost_map.get_pair_at(loc).first;
2482  s << " / ";
2483  s << cost_map.get_pair_at(loc).second;
2484  game_display_->labels().set_label(loc, s.str());
2485  }
2486  }
2487  }
2488 
2489  // create return value
2490  lua_createtable(L, location_set.size(), 0);
2491  int counter = 1;
2492  for (const map_location& loc : location_set)
2493  {
2494  luaW_push_namedtuple(L, {"x", "y", "cost", "reach"});
2495 
2496  lua_pushinteger(L, loc.wml_x());
2497  lua_rawseti(L, -2, 1);
2498 
2499  lua_pushinteger(L, loc.wml_y());
2500  lua_rawseti(L, -2, 2);
2501 
2502  lua_pushinteger(L, cost_map.get_pair_at(loc).first);
2503  lua_rawseti(L, -2, 3);
2504 
2505  lua_pushinteger(L, cost_map.get_pair_at(loc).second);
2506  lua_rawseti(L, -2, 4);
2507 
2508  lua_rawseti(L, -2, counter);
2509  ++counter;
2510  }
2511  return 1;
2512 }
2513 
2514 const char* labelKey = "floating label";
2515 
2516 static int* luaW_check_floating_label(lua_State* L, int idx)
2517 {
2518  return reinterpret_cast<int*>(luaL_checkudata(L, idx, labelKey));
2519 }
2520 
2521 static int impl_floating_label_getmethod(lua_State* L)
2522 {
2523  const char* m = luaL_checkstring(L, 2);
2524  return_bool_attrib("valid", *luaW_check_floating_label(L, 1) != 0);
2525  return luaW_getmetafield(L, 1, m);
2526 }
2527 
2529 {
2530  int* handle = luaW_check_floating_label(L, 1);
2531  std::chrono::milliseconds fade{luaL_optinteger(L, 2, -1)};
2532  if(*handle != 0) {
2533  // Passing -1 as the second argument means it uses the fade time that was set when the label was created
2535  }
2536  *handle = 0;
2537  return 0;
2538 }
2539 
2541 {
2542  int* handle = luaW_check_floating_label(L, 1);
2543  if(*handle != 0) {
2544  font::move_floating_label(*handle, luaL_checknumber(L, 2), luaL_checknumber(L, 3));
2545  }
2546  return 0;
2547 }
2548 
2549 /**
2550  * Arg 1: text - string
2551  * Arg 2: options table
2552  * - size: font size
2553  * - max_width: max width for word wrapping
2554  * - color: font color
2555  * - bgcolor: background color
2556  * - bgalpha: background opacity
2557  * - duration: display duration (integer or the string "unlimited")
2558  * - fade_time: duration of fade-out
2559  * - location: screen offset
2560  * - valign: vertical alignment and anchoring - "top", "center", or "bottom"
2561  * - halign: horizontal alignment and anchoring - "left", "center", or "right"
2562  * Returns: label handle
2563  */
2564 int game_lua_kernel::intf_set_floating_label(lua_State* L, bool spawn)
2565 {
2566  t_string text = luaW_checktstring(L, 1);
2567  int size = font::SIZE_SMALL;
2568  int width = 0;
2569  double width_ratio = 0;
2570  color_t color = font::LABEL_COLOR, bgcolor{0, 0, 0, 0};
2571  int lifetime = 2'000, fadeout = 100;
2572  font::ALIGN alignment = font::ALIGN::CENTER_ALIGN, vertical_alignment = font::ALIGN::CENTER_ALIGN;
2573  // This is actually a relative screen location in pixels, but map_location already supports
2574  // everything needed to read in a pair of coordinates.
2575  // Depending on the chosen alignment, it may be relative to centre, an edge centre, or a corner.
2576  map_location loc{0, 0, wml_loc()};
2577  if(lua_istable(L, 2)) {
2578  if(luaW_tableget(L, 2, "size")) {
2579  size = luaL_checkinteger(L, -1);
2580  }
2581  if(luaW_tableget(L, 2, "max_width")) {
2582  int found_number;
2583  width = lua_tointegerx(L, -1, &found_number);
2584  if(!found_number) {
2585  auto value = luaW_tostring(L, -1);
2586  try {
2587  if(!value.empty() && value.back() == '%') {
2588  value.remove_suffix(1);
2589  width_ratio = std::stoi(std::string(value)) / 100.0;
2590  } else throw std::invalid_argument(value.data());
2591  } catch(std::invalid_argument&) {
2592  return luaL_argerror(L, -1, "max_width should be integer or percentage");
2593  }
2594 
2595  }
2596  }
2597  if(luaW_tableget(L, 2, "color")) {
2598  if(lua_isstring(L, -1)) {
2599  color = color_t::from_hex_string(lua_tostring(L, -1));
2600  } else {
2601  auto vec = lua_check<std::vector<int>>(L, -1);
2602  if(vec.size() != 3) {
2603  int idx = lua_absindex(L, -1);
2604  if(luaW_tableget(L, idx, "r") && luaW_tableget(L, idx, "g") && luaW_tableget(L, idx, "b")) {
2605  color.r = luaL_checkinteger(L, -3);
2606  color.g = luaL_checkinteger(L, -2);
2607  color.b = luaL_checkinteger(L, -1);
2608  } else {
2609  return luaL_error(L, "floating label text color should be a hex string, an array of 3 integers, or a table with r,g,b keys");
2610  }
2611  } else {
2612  color.r = vec[0];
2613  color.g = vec[1];
2614  color.b = vec[2];
2615  }
2616  }
2617  }
2618  if(luaW_tableget(L, 2, "bgcolor")) {
2619  if(lua_isstring(L, -1)) {
2620  bgcolor = color_t::from_hex_string(lua_tostring(L, -1));
2621  } else {
2622  auto vec = lua_check<std::vector<int>>(L, -1);
2623  if(vec.size() != 3) {
2624  int idx = lua_absindex(L, -1);
2625  if(luaW_tableget(L, idx, "r") && luaW_tableget(L, idx, "g") && luaW_tableget(L, idx, "b")) {
2626  bgcolor.r = luaL_checkinteger(L, -3);
2627  bgcolor.g = luaL_checkinteger(L, -2);
2628  bgcolor.b = luaL_checkinteger(L, -1);
2629  } else {
2630  return luaL_error(L, "floating label background color should be a hex string, an array of 3 integers, or a table with r,g,b keys");
2631  }
2632  } else {
2633  bgcolor.r = vec[0];
2634  bgcolor.g = vec[1];
2635  bgcolor.b = vec[2];
2636  }
2637  bgcolor.a = ALPHA_OPAQUE;
2638  }
2639  if(luaW_tableget(L, 2, "bgalpha")) {
2640  bgcolor.a = luaL_checkinteger(L, -1);
2641  }
2642  }
2643  if(luaW_tableget(L, 2, "duration")) {
2644  int found_number;
2645  lifetime = lua_tointegerx(L, -1, &found_number);
2646  if(!found_number) {
2647  auto value = luaW_tostring(L, -1);
2648  if(value == "unlimited") {
2649  lifetime = -1;
2650  } else {
2651  return luaL_argerror(L, -1, "duration should be integer or 'unlimited'");
2652  }
2653  }
2654  }
2655  if(luaW_tableget(L, 2, "fade_time")) {
2656  fadeout = lua_tointeger(L, -1);
2657  }
2658  if(luaW_tableget(L, 2, "location")) {
2659  loc = luaW_checklocation(L, -1);
2660  }
2661  if(luaW_tableget(L, 2, "halign")) {
2662  static const char* options[] = {"left", "center", "right"};
2663  alignment = font::ALIGN(luaL_checkoption(L, -1, nullptr, options));
2664  }
2665  if(luaW_tableget(L, 2, "valign")) {
2666  static const char* options[] = {"top", "center", "bottom"};
2667  vertical_alignment = font::ALIGN(luaL_checkoption(L, -1, nullptr, options));
2668  }
2669  }
2670 
2671  int* handle = nullptr;
2672  if(spawn) {
2673  // Creating a new label, allocate a new handle
2674  handle = new(L)int();
2675  } else {
2676  // First argument is the label handle
2678  }
2679  int handle_idx = lua_gettop(L);
2680 
2681  if(*handle != 0) {
2683  }
2684 
2685  const SDL_Rect rect = game_display_->map_outside_area();
2686  if(width_ratio > 0) {
2687  width = static_cast<int>(std::round(rect.w * width_ratio));
2688  }
2689  int x = 0, y = 0;
2690  switch(alignment) {
2692  x = rect.x + loc.wml_x();
2693  break;
2695  x = rect.x + rect.w / 2 + loc.wml_x();
2696  break;
2698  x = rect.x + rect.w - loc.wml_x();
2699  break;
2700  }
2701  switch(vertical_alignment) {
2702  case font::ALIGN::LEFT_ALIGN: // top
2703  y = rect.y + loc.wml_y();
2704  break;
2706  y = rect.y + rect.h / 2 + loc.wml_y();
2707  break;
2708  case font::ALIGN::RIGHT_ALIGN: // bottom
2709  // The size * 1.5 adjustment avoids the text being cut off if placed at y = 0
2710  // This is necessary because the text is positioned by the top edge but we want it to
2711  // seem like it's positioned by the bottom edge.
2712  // This wouldn't work for multiline text, but we don't expect that to be common in this API anyway.
2713  y = rect.y + rect.h - loc.wml_y() - static_cast<int>(size * 1.5);
2714  break;
2715  }
2716 
2717  using std::chrono::milliseconds;
2718  font::floating_label flabel(text);
2719  flabel.set_font_size(size);
2720  flabel.set_color(color);
2721  flabel.set_bg_color(bgcolor);
2722  flabel.set_alignment(alignment);
2723  flabel.set_position(x, y);
2724  flabel.set_lifetime(milliseconds{lifetime}, milliseconds{fadeout});
2725  flabel.set_clip_rect(rect);
2726 
2727  // Adjust fallback width (map area) to avoid text escaping when location != 0
2728  if(width > 0) {
2729  flabel.set_width(std::min(width, rect.w - loc.wml_x()));
2730  } else {
2731  flabel.set_width(rect.w - loc.wml_x());
2732  }
2733 
2734  *handle = font::add_floating_label(flabel);
2735  lua_settop(L, handle_idx);
2736  if(luaL_newmetatable(L, labelKey)) {
2737  // Initialize the metatable
2738  static const luaL_Reg methods[] = {
2739  {"remove", &dispatch<&game_lua_kernel::intf_remove_floating_label>},
2740  {"move", &dispatch<&game_lua_kernel::intf_move_floating_label>},
2741  {"replace", &dispatch2<&game_lua_kernel::intf_set_floating_label, false>},
2742  {"__index", &impl_floating_label_getmethod},
2743  { nullptr, nullptr }
2744  };
2745  luaL_setfuncs(L, methods, 0);
2746  luaW_table_set(L, -1, "__metatable", std::string(labelKey));
2747  }
2748  lua_setmetatable(L, handle_idx);
2749  lua_settop(L, handle_idx);
2750  return 1;
2751 }
2752 
2754 {
2755  if(game_display_) {
2757  }
2758 
2759  resources::whiteboard->on_kill_unit();
2760 }
2761 
2762 /**
2763  * Places a unit on the map.
2764  * - Arg 1: (optional) location.
2765  * - Arg 2: Unit (WML table or proxy), or nothing/nil to delete.
2766  * OR
2767  * - Arg 1: Unit (WML table or proxy)
2768  * - Arg 2: (optional) location
2769  * - Arg 3: (optional) boolean
2770  */
2772 {
2773  if(map_locked_) {
2774  return luaL_error(L, "Attempted to move a unit while the map is locked");
2775  }
2776 
2777  map_location loc;
2778  if (luaW_tolocation(L, 2, loc)) {
2779  if (!map().on_board(loc)) {
2780  return luaL_argerror(L, 2, "invalid location");
2781  }
2782  }
2783 
2784  if((luaW_isunit(L, 1))) {
2785  lua_unit& u = *luaW_checkunit_ref(L, 1);
2786  if(u.on_map() && u->get_location() == loc) {
2787  return 0;
2788  }
2789  if (!loc.valid()) {
2790  loc = u->get_location();
2791  if (!map().on_board(loc))
2792  return luaL_argerror(L, 1, "invalid location");
2793  }
2794 
2796  u.put_map(loc);
2797  u.get_shared()->anim_comp().set_standing();
2798  } else if(!lua_isnoneornil(L, 1)) {
2799  const vconfig* vcfg = nullptr;
2800  config cfg = luaW_checkconfig(L, 1, vcfg);
2801  if (!map().on_board(loc)) {
2802  loc.set_wml_x(cfg["x"].to_int());
2803  loc.set_wml_y(cfg["y"].to_int());
2804  if (!map().on_board(loc))
2805  return luaL_argerror(L, 2, "invalid location");
2806  }
2807 
2808  unit_ptr u = unit::create(cfg, true, vcfg);
2809  units().erase(loc);
2811  u->set_location(loc);
2812  units().insert(u);
2813  }
2814 
2815  // Fire event if using the deprecated version or if the final argument is not false
2816  // If the final boolean argument is omitted, the actual final argument (the unit or location) will always yield true.
2817  if(luaW_toboolean(L, -1)) {
2818  play_controller_.pump().fire("unit_placed", loc);
2819  }
2820  return 0;
2821 }
2822 
2823 /**
2824  * Erases a unit from the map
2825  * - Arg 1: Unit to erase OR Location to erase unit
2826  */
2828 {
2829  if(map_locked_) {
2830  return luaL_error(L, "Attempted to remove a unit while the map is locked");
2831  }
2832  map_location loc;
2833 
2834  if(luaW_isunit(L, 1)) {
2835  lua_unit& u = *luaW_checkunit_ref(L, 1);
2836  if (u.on_map()) {
2837  loc = u->get_location();
2838  if (!map().on_board(loc)) {
2839  return luaL_argerror(L, 1, "invalid location");
2840  }
2841  } else if (int side = u.on_recall_list()) {
2842  team &t = board().get_team(side);
2843  // Should it use underlying ID instead?
2844  t.recall_list().erase_if_matches_id(u->id());
2845  } else {
2846  return luaL_argerror(L, 1, "can't erase private units");
2847  }
2848  } else if (luaW_tolocation(L, 1, loc)) {
2849  if (!map().on_board(loc)) {
2850  return luaL_argerror(L, 1, "invalid location");
2851  }
2852  } else {
2853  return luaL_argerror(L, 1, "expected unit or location");
2854  }
2855 
2856  units().erase(loc);
2857  resources::whiteboard->on_kill_unit();
2858  return 0;
2859 }
2860 
2861 /**
2862  * Puts a unit on a recall list.
2863  * - Arg 1: WML table or unit.
2864  * - Arg 2: (optional) side.
2865  */
2867 {
2868  if(map_locked_) {
2869  return luaL_error(L, "Attempted to move a unit while the map is locked");
2870  }
2871  lua_unit *lu = nullptr;
2872  unit_ptr u = unit_ptr();
2873  int side = lua_tointeger(L, 2);
2874  if (static_cast<unsigned>(side) > teams().size()) side = 0;
2875 
2876  if(luaW_isunit(L, 1)) {
2877  lu = luaW_checkunit_ref(L, 1);
2878  u = lu->get_shared();
2879  if(lu->on_recall_list() && lu->on_recall_list() == side) {
2880  return luaL_argerror(L, 1, "unit already on recall list");
2881  }
2882  } else {
2883  const vconfig* vcfg = nullptr;
2884  config cfg = luaW_checkconfig(L, 1, vcfg);
2885  u = unit::create(cfg, true, vcfg);
2886  }
2887 
2888  if (!side) {
2889  side = u->side();
2890  } else {
2891  u->set_side(side);
2892  }
2893  team &t = board().get_team(side);
2894  // Avoid duplicates in the recall list.
2895  std::size_t uid = u->underlying_id();
2896  t.recall_list().erase_by_underlying_id(uid);
2897  t.recall_list().add(u);
2898  if (lu) {
2899  if (lu->on_map()) {
2900  units().erase(u->get_location());
2901  resources::whiteboard->on_kill_unit();
2902  u->anim_comp().clear_haloes();
2903  }
2904  lu->lua_unit::~lua_unit();
2905  new(lu) lua_unit(side, uid);
2906  }
2907 
2908  return 0;
2909 }
2910 
2911 /**
2912  * Extracts a unit from the map or a recall list and gives it to Lua.
2913  * - Arg 1: unit userdata.
2914  */
2916 {
2917  if(map_locked_) {
2918  return luaL_error(L, "Attempted to remove a unit while the map is locked");
2919  }
2920  lua_unit* lu = luaW_checkunit_ref(L, 1);
2921  unit_ptr u = lu->get_shared();
2922 
2923  if (lu->on_map()) {
2924  u = units().extract(u->get_location());
2925  assert(u);
2926  u->anim_comp().clear_haloes();
2927  } else if (int side = lu->on_recall_list()) {
2928  team &t = board().get_team(side);
2929  unit_ptr v = u->clone();
2930  t.recall_list().erase_if_matches_id(u->id());
2931  u = v;
2932  } else {
2933  return 0;
2934  }
2935 
2936  lu->lua_unit::~lua_unit();
2937  new(lu) lua_unit(u);
2938  return 0;
2939 }
2940 
2941 /**
2942  * Finds a vacant tile.
2943  * - Arg 1: location.
2944  * - Arg 2: optional unit for checking movement type.
2945  * - Rets 1,2: location.
2946  */
2948 {
2950 
2951  unit_ptr u;
2952  if (!lua_isnoneornil(L, 2)) {
2953  if(luaW_isunit(L, 2)) {
2954  u = luaW_checkunit_ptr(L, 2, false);
2955  } else {
2956  const vconfig* vcfg = nullptr;
2957  config cfg = luaW_checkconfig(L, 2, vcfg);
2958  u = unit::create(cfg, false, vcfg);
2959  }
2960  }
2961 
2963 
2964  if (!res.valid()) return 0;
2965  lua_pushinteger(L, res.wml_x());
2966  lua_pushinteger(L, res.wml_y());
2967  return 2;
2968 }
2969 
2970 /**
2971  * Floats some text on the map.
2972  * - Arg 1: location.
2973  * - Arg 2: string.
2974  * - Arg 3: color.
2975  */
2977 {
2979  color_t color = font::LABEL_COLOR;
2980 
2981  t_string text = luaW_checktstring(L, 2);
2982  if (!lua_isnoneornil(L, 3)) {
2983  color = color_t::from_rgb_string(luaL_checkstring(L, 3));
2984  }
2985 
2986  if (game_display_) {
2987  game_display_->float_label(loc, text, color);
2988  }
2989  return 0;
2990 }
2991 
2992 /**
2993  * Creates a unit from its WML description.
2994  * - Arg 1: WML table.
2995  * - Ret 1: unit userdata.
2996  */
2997 static int intf_create_unit(lua_State *L)
2998 {
2999  const vconfig* vcfg = nullptr;
3000  config cfg = luaW_checkconfig(L, 1, vcfg);
3001  unit_ptr u = unit::create(cfg, true, vcfg);
3002  luaW_pushunit(L, u);
3003  return 1;
3004 }
3005 
3006 /**
3007  * Copies a unit.
3008  * - Arg 1: unit userdata.
3009  * - Ret 1: unit userdata.
3010  */
3011 static int intf_copy_unit(lua_State *L)
3012 {
3013  unit& u = luaW_checkunit(L, 1);
3014  luaW_pushunit(L, u.clone());
3015  return 1;
3016 }
3017 
3018 /**
3019  * Returns unit resistance against a given attack type.
3020  * - Arg 1: unit userdata.
3021  * - Arg 2: string containing the attack type.
3022  * - Arg 3: boolean indicating if attacker.
3023  * - Arg 4: optional location.
3024  * - Ret 1: integer.
3025  */
3026 static int intf_unit_resistance(lua_State *L)
3027 {
3028  const unit& u = luaW_checkunit(L, 1);
3029  char const *m = luaL_checkstring(L, 2);
3030  bool a = false;
3032 
3033  if(lua_isboolean(L, 3)) {
3034  a = luaW_toboolean(L, 3);
3035  if(!lua_isnoneornil(L, 4)) {
3036  loc = luaW_checklocation(L, 4);
3037  }
3038  } else if(!lua_isnoneornil(L, 3)) {
3039  loc = luaW_checklocation(L, 3);
3040  }
3041 
3042  lua_pushinteger(L, 100 - u.resistance_against(m, a, loc));
3043  return 1;
3044 }
3045 
3046 /**
3047  * Returns unit movement cost on a given terrain.
3048  * - Arg 1: unit userdata.
3049  * - Arg 2: string containing the terrain type.
3050  * - Ret 1: integer.
3051  */
3052 static int intf_unit_movement_cost(lua_State *L)
3053 {
3054  const unit& u = luaW_checkunit(L, 1);
3056  map_location loc;
3057  if(luaW_tolocation(L, 2, loc)) {
3058  t = static_cast<const game_board*>(resources::gameboard)->map()[loc];
3059  } else if(lua_isstring(L, 2)) {
3060  char const *m = luaL_checkstring(L, 2);
3062  } else return luaW_type_error(L, 2, "location or terrain string");
3063  lua_pushinteger(L, u.movement_cost(t));
3064  return 1;
3065 }
3066 
3067 /**
3068  * Returns unit vision cost on a given terrain.
3069  * - Arg 1: unit userdata.
3070  * - Arg 2: string containing the terrain type.
3071  * - Ret 1: integer.
3072  */
3073 static int intf_unit_vision_cost(lua_State *L)
3074 {
3075  const unit& u = luaW_checkunit(L, 1);
3077  map_location loc;
3078  if(luaW_tolocation(L, 2, loc)) {
3079  t = static_cast<const game_board*>(resources::gameboard)->map()[loc];
3080  } else if(lua_isstring(L, 2)) {
3081  char const *m = luaL_checkstring(L, 2);
3083  } else return luaW_type_error(L, 2, "location or terrain string");
3084  lua_pushinteger(L, u.vision_cost(t));
3085  return 1;
3086 }
3087 
3088 /**
3089  * Returns unit jamming cost on a given terrain.
3090  * - Arg 1: unit userdata.
3091  * - Arg 2: string containing the terrain type.
3092  * - Ret 1: integer.
3093  */
3094 static int intf_unit_jamming_cost(lua_State *L)
3095 {
3096  const unit& u = luaW_checkunit(L, 1);
3098  map_location loc;
3099  if(luaW_tolocation(L, 2, loc)) {
3100  t = static_cast<const game_board*>(resources::gameboard)->map()[loc];
3101  } else if(lua_isstring(L, 2)) {
3102  char const *m = luaL_checkstring(L, 2);
3104  } else return luaW_type_error(L, 2, "location or terrain string");
3105  lua_pushinteger(L, u.jamming_cost(t));
3106  return 1;
3107 }
3108 
3109 /**
3110  * Returns unit defense on a given terrain.
3111  * - Arg 1: unit userdata.
3112  * - Arg 2: string containing the terrain type.
3113  * - Ret 1: integer.
3114  */
3115 static int intf_unit_defense(lua_State *L)
3116 {
3117  const unit& u = luaW_checkunit(L, 1);
3119  map_location loc;
3120  if(luaW_tolocation(L, 2, loc)) {
3121  t = static_cast<const game_board*>(resources::gameboard)->map()[loc];
3122  } else if(lua_isstring(L, 2)) {
3123  char const *m = luaL_checkstring(L, 2);
3125  } else return luaW_type_error(L, 2, "location or terrain string");
3126  lua_pushinteger(L, 100 - u.defense_modifier(t));
3127  return 1;
3128 }
3129 
3130 /**
3131  * Returns true if the unit has the given ability enabled.
3132  * - Arg 1: unit userdata.
3133  * - Arg 2: string.
3134  * - Ret 1: boolean.
3135  */
3137 {
3138  const unit& u = luaW_checkunit(L, 1);
3139  char const *m = luaL_checkstring(L, 2);
3140  lua_pushboolean(L, u.get_ability_bool(m));
3141  return 1;
3142 }
3143 
3144 /**
3145  * Changes a unit to the given unit type.
3146  * - Arg 1: unit userdata.
3147  * - Arg 2: unit type name
3148  * - Arg 3: (optional) unit variation name
3149  */
3150 static int intf_transform_unit(lua_State *L)
3151 {
3152  unit& u = luaW_checkunit(L, 1);
3153  char const *m = luaL_checkstring(L, 2);
3154  const unit_type *utp = unit_types.find(m);
3155  if (!utp) return luaL_argerror(L, 2, "unknown unit type");
3156  if(lua_isstring(L, 3)) {
3157  const std::string& m2 = lua_tostring(L, 3);
3158  if(!utp->has_variation(m2)) return luaL_argerror(L, 2, "unknown unit variation");
3159  utp = &utp->get_variation(m2);
3160  }
3161  u.advance_to(*utp);
3162 
3163  return 0;
3164 }
3165 
3166 /**
3167  * Puts a table at the top of the stack with some combat result.
3168  */
3169 static void luaW_pushsimdata(lua_State *L, const combatant &cmb)
3170 {
3171  int n = cmb.hp_dist.size();
3172  lua_createtable(L, 0, 4);
3173  lua_pushnumber(L, cmb.poisoned);
3174  lua_setfield(L, -2, "poisoned");
3175  lua_pushnumber(L, cmb.slowed);
3176  lua_setfield(L, -2, "slowed");
3177  lua_pushnumber(L, cmb.untouched);
3178  lua_setfield(L, -2, "untouched");
3179  lua_pushnumber(L, cmb.average_hp());
3180  lua_setfield(L, -2, "average_hp");
3181  lua_createtable(L, n, 0);
3182  for (int i = 0; i < n; ++i) {
3183  lua_pushnumber(L, cmb.hp_dist[i]);
3184  lua_rawseti(L, -2, i);
3185  }
3186  lua_setfield(L, -2, "hp_chance");
3187 }
3188 
3189 /**
3190  * Puts a table at the top of the stack with information about the combatants' weapons.
3191  */
3192 static void luaW_pushsimweapon(lua_State *L, const battle_context_unit_stats &bcustats)
3193 {
3194 
3195  lua_createtable(L, 0, 16);
3196 
3197  lua_pushnumber(L, bcustats.num_blows);
3198  lua_setfield(L, -2, "num_blows");
3199  lua_pushnumber(L, bcustats.damage);
3200  lua_setfield(L, -2, "damage");
3201  lua_pushnumber(L, bcustats.chance_to_hit);
3202  lua_setfield(L, -2, "chance_to_hit");
3203  lua_pushboolean(L, bcustats.poisons);
3204  lua_setfield(L, -2, "poisons");
3205  lua_pushboolean(L, bcustats.slows);
3206  lua_setfield(L, -2, "slows");
3207  lua_pushboolean(L, bcustats.petrifies);
3208  lua_setfield(L, -2, "petrifies");
3209  lua_pushboolean(L, bcustats.plagues);
3210  lua_setfield(L, -2, "plagues");
3211  lua_pushstring(L, bcustats.plague_type.c_str());
3212  lua_setfield(L, -2, "plague_type");
3213  lua_pushnumber(L, bcustats.rounds);
3214  lua_setfield(L, -2, "rounds");
3215  lua_pushboolean(L, bcustats.firststrike);
3216  lua_setfield(L, -2, "firststrike");
3217  lua_pushboolean(L, bcustats.drains);
3218  lua_setfield(L, -2, "drains");
3219  lua_pushnumber(L, bcustats.drain_constant);
3220  lua_setfield(L, -2, "drain_constant");
3221  lua_pushnumber(L, bcustats.drain_percent);
3222  lua_setfield(L, -2, "drain_percent");
3223 
3224 
3225  //if we called simulate_combat without giving an explicit weapon this can be useful.
3226  lua_pushnumber(L, bcustats.attack_num);
3227  lua_setfield(L, -2, "attack_num"); // DEPRECATED
3228  lua_pushnumber(L, bcustats.attack_num + 1);
3229  lua_setfield(L, -2, "number");
3230  //this is nullptr when there is no counter weapon
3231  if(bcustats.weapon != nullptr)
3232  {
3233  lua_pushstring(L, bcustats.weapon->id().c_str());
3234  lua_setfield(L, -2, "name");
3235  luaW_pushweapon(L, bcustats.weapon);
3236  lua_setfield(L, -2, "weapon");
3237  }
3238 
3239 }
3240 
3241 /**
3242  * Simulates a combat between two units.
3243  * - Arg 1: attacker userdata.
3244  * - Arg 2: optional weapon index.
3245  * - Arg 3: defender userdata.
3246  * - Arg 4: optional weapon index.
3247  *
3248  * - Ret 1: attacker results.
3249  * - Ret 2: defender results.
3250  * - Ret 3: info about the attacker weapon.
3251  * - Ret 4: info about the defender weapon.
3252  */
3254 {
3255  int arg_num = 1, att_w = -1, def_w = -1;
3256 
3257  unit_const_ptr att = luaW_checkunit(L, arg_num).shared_from_this();
3258  ++arg_num;
3259  if (lua_isnumber(L, arg_num)) {
3260  att_w = lua_tointeger(L, arg_num) - 1;
3261  if (att_w < 0 || att_w >= static_cast<int>(att->attacks().size()))
3262  return luaL_argerror(L, arg_num, "weapon index out of bounds");
3263  ++arg_num;
3264  }
3265 
3266  unit_const_ptr def = luaW_checkunit(L, arg_num).shared_from_this();
3267  ++arg_num;
3268  if (lua_isnumber(L, arg_num)) {
3269  def_w = lua_tointeger(L, arg_num) - 1;
3270  if (def_w < 0 || def_w >= static_cast<int>(def->attacks().size()))
3271  return luaL_argerror(L, arg_num, "weapon index out of bounds");
3272  ++arg_num;
3273  }
3274 
3275  battle_context context(units(), att->get_location(),
3276  def->get_location(), att_w, def_w, 0.0, nullptr, att, def);
3277 
3280  luaW_pushsimweapon(L, context.get_attacker_stats());
3281  luaW_pushsimweapon(L, context.get_defender_stats());
3282  return 4;
3283 }
3284 
3285 /**
3286  * Plays a sound, possibly repeated.
3287  * - Arg 1: string.
3288  * - Arg 2: optional integer.
3289  */
3291 {
3292  if (play_controller_.is_skipping_replay()) return 0;
3293  char const *m = luaL_checkstring(L, 1);
3294  int repeats = luaL_optinteger(L, 2, 0);
3295  sound::play_sound(m, sound::SOUND_FX, repeats);
3296  return 0;
3297 }
3298 
3299 /**
3300  * Sets an achievement as being completed.
3301  * - Arg 1: string - content_for.
3302  * - Arg 2: string - id.
3303  */
3305 {
3306  const char* content_for = luaL_checkstring(L, 1);
3307  const char* id = luaL_checkstring(L, 2);
3308 
3309  for(achievement_group& group : game_config_manager::get()->get_achievements()) {
3310  if(group.content_for_ == content_for) {
3311  for(achievement& achieve : group.achievements_) {
3312  if(achieve.id_ == id) {
3313  // already achieved
3314  if(achieve.achieved_) {
3315  return 0;
3316  }
3317  // found the achievement - mark it as completed
3318  if(!play_controller_.is_replay()) {
3319  prefs::get().set_achievement(content_for, id);
3320  }
3321  achieve.achieved_ = true;
3322  // progressable achievements can also check for current progress equals -1
3323  if(achieve.max_progress_ != 0) {
3324  achieve.current_progress_ = -1;
3325  }
3326  if(achieve.sound_path_ != "") {
3328  }
3329  // show the achievement popup
3330  luaW_getglobal(L, "gui", "show_popup");
3331  luaW_pushtstring(L, achieve.name_completed_);
3333  lua_pushstring(L, achieve.icon_completed_.c_str());
3334  luaW_pcall(L, 3, 0, 0);
3335  return 0;
3336  }
3337  }
3338  // achievement not found - existing achievement group but non-existing achievement id
3339  ERR_LUA << "Achievement " << id << " not found for achievement group " << content_for;
3340  return 0;
3341  }
3342  }
3343 
3344  // achievement group not found
3345  ERR_LUA << "Achievement group " << content_for << " not found";
3346  return 0;
3347 }
3348 
3349 /**
3350  * Returns whether an achievement has been completed.
3351  * - Arg 1: string - content_for.
3352  * - Arg 2: string - id.
3353  * - Ret 1: boolean.
3354  */
3356 {
3357  const char* content_for = luaL_checkstring(L, 1);
3358  const char* id = luaL_checkstring(L, 2);
3359 
3360  if(resources::controller->is_networked_mp() && synced_context::is_synced()) {
3361  ERR_LUA << "Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3362  lua_pushboolean(L, false);
3363  } else {
3364  lua_pushboolean(L, prefs::get().achievement(content_for, id));
3365  }
3366 
3367  return 1;
3368 }
3369 
3370 /**
3371  * Returns information on a single achievement, or no data if the achievement is not found.
3372  * - Arg 1: string - content_for.
3373  * - Arg 2: string - id.
3374  * - Ret 1: WML table returned by the function.
3375  */
3377 {
3378  const char* content_for = luaL_checkstring(L, 1);
3379  const char* id = luaL_checkstring(L, 2);
3380 
3381  config cfg;
3382  for(const auto& group : game_config_manager::get()->get_achievements()) {
3383  if(group.content_for_ == content_for) {
3384  for(const auto& achieve : group.achievements_) {
3385  if(achieve.id_ == id) {
3386  // found the achievement - return it as a config
3387  cfg["id"] = achieve.id_;
3388  cfg["name"] = achieve.name_;
3389  cfg["name_completed"] = achieve.name_completed_;
3390  cfg["description"] = achieve.description_;
3391  cfg["description_completed"] = achieve.description_completed_;
3392  cfg["icon"] = achieve.icon_;
3393  cfg["icon_completed"] = achieve.icon_completed_;
3394  cfg["hidden"] = achieve.hidden_;
3395  cfg["achieved"] = achieve.achieved_;
3396  cfg["max_progress"] = achieve.max_progress_;
3397  cfg["current_progress"] = achieve.current_progress_;
3398 
3399  for(const auto& sub_ach : achieve.sub_achievements_) {
3400  config& sub = cfg.add_child("sub_achievement");
3401  sub["id"] = sub_ach.id_;
3402  sub["description"] = sub_ach.description_;
3403  sub["icon"] = sub_ach.icon_;
3404  sub["achieved"] = sub_ach.achieved_;
3405  }
3406 
3407  luaW_pushconfig(L, cfg);
3408  return 1;
3409  }
3410  }
3411  // return empty config - existing achievement group but non-existing achievement id
3412  ERR_LUA << "Achievement " << id << " not found for achievement group " << content_for;
3413  luaW_pushconfig(L, cfg);
3414  return 1;
3415  }
3416  }
3417  // return empty config - non-existing achievement group
3418  ERR_LUA << "Achievement group " << content_for << " not found";
3419  luaW_pushconfig(L, cfg);
3420  return 1;
3421 }
3422 
3423 /**
3424  * Progresses the provided achievement.
3425  * - Arg 1: string - content_for.
3426  * - Arg 2: string - achievement id.
3427  * - Arg 3: int - the amount to progress the achievement.
3428  * - Arg 4: int - the limit the achievement can progress by
3429  * - Ret 1: int - the achievement's current progress after adding amount or -1 if not a progressable achievement (including if it's already achieved)
3430  * - Ret 2: int - the achievement's max progress or -1 if not a progressable achievement
3431  */
3433 {
3434  const char* content_for = luaL_checkstring(L, 1);
3435  const char* id = luaL_checkstring(L, 2);
3436  int amount = luaL_checkinteger(L, 3);
3437  int limit = luaL_optinteger(L, 4, 999999999);
3438 
3439  for(achievement_group& group : game_config_manager::get()->get_achievements()) {
3440  if(group.content_for_ == content_for) {
3441  for(achievement& achieve : group.achievements_) {
3442  if(achieve.id_ == id) {
3443  // check that this is a progressable achievement
3444  if(achieve.max_progress_ == 0 || achieve.sub_achievements_.size() > 0) {
3445  ERR_LUA << "Attempted to progress achievement " << id << " for achievement group " << content_for << ", is not a progressible achievement.";
3446  lua_pushinteger(L, -1);
3447  lua_pushinteger(L, -1);
3448  return 2;
3449  }
3450 
3451  if(!achieve.achieved_) {
3452  int progress = 0;
3453  if(!play_controller_.is_replay()) {
3454  progress = prefs::get().progress_achievement(content_for, id, limit, achieve.max_progress_, amount);
3455  }
3456  if(progress >= achieve.max_progress_) {
3458  achieve.current_progress_ = -1;
3459  } else {
3460  achieve.current_progress_ = progress;
3461  }
3462  lua_pushinteger(L, progress);
3463  } else {
3464  lua_pushinteger(L, -1);
3465  }
3466  lua_pushinteger(L, achieve.max_progress_);
3467 
3468  return 2;
3469  }
3470  }
3471  // achievement not found - existing achievement group but non-existing achievement id
3472  lua_push(L, "Achievement " + std::string(id) + " not found for achievement group " + content_for);
3473  return lua_error(L);
3474  }
3475  }
3476 
3477  // achievement group not found
3478  lua_push(L, "Achievement group " + std::string(content_for) + " not found");
3479  return lua_error(L);
3480 }
3481 
3482 /**
3483  * Returns whether an achievement has been completed.
3484  * - Arg 1: string - content_for.
3485  * - Arg 2: string - achievement id.
3486  * - Arg 3: string - sub-achievement id
3487  * - Ret 1: boolean.
3488  */
3490 {
3491  const char* content_for = luaL_checkstring(L, 1);
3492  const char* id = luaL_checkstring(L, 2);
3493  const char* sub_id = luaL_checkstring(L, 3);
3494 
3495  if(resources::controller->is_networked_mp() && synced_context::is_synced()) {
3496  ERR_LUA << "Returning false for whether a player has completed an achievement due to being networked multiplayer.";
3497  lua_pushboolean(L, false);
3498  } else {
3499  lua_pushboolean(L, prefs::get().sub_achievement(content_for, id, sub_id));
3500  }
3501 
3502  return 1;
3503 }
3504 
3505 /**
3506  * Marks a single sub-achievement as completed.
3507  * - Arg 1: string - content_for.
3508  * - Arg 2: string - achievement id.
3509  * - Arg 3: string - sub-achievement id
3510  */
3512 {
3513  const char* content_for = luaL_checkstring(L, 1);
3514  const char* id = luaL_checkstring(L, 2);
3515  const char* sub_id = luaL_checkstring(L, 3);
3516 
3517  for(achievement_group& group : game_config_manager::get()->get_achievements()) {
3518  if(group.content_for_ == content_for) {
3519  for(achievement& achieve : group.achievements_) {
3520  if(achieve.id_ == id) {
3521  // the whole achievement is already completed
3522  if(achieve.achieved_) {
3523  return 0;
3524  }
3525 
3526  for(sub_achievement& sub_ach : achieve.sub_achievements_) {
3527  if(sub_ach.id_ == sub_id) {
3528  // this particular sub-achievement is already achieved
3529  if(sub_ach.achieved_) {
3530  return 0;
3531  } else {
3532  if(!play_controller_.is_replay()) {
3533  prefs::get().set_sub_achievement(content_for, id, sub_id);
3534  }
3535  sub_ach.achieved_ = true;
3536  achieve.current_progress_++;
3537  if(achieve.current_progress_ == achieve.max_progress_) {
3539  }
3540  return 0;
3541  }
3542  }
3543  }
3544  // sub-achievement not found - existing achievement group and achievement but non-existing sub-achievement id
3545  lua_push(L, "Sub-achievement " + std::string(id) + " not found for achievement" + id + " in achievement group " + content_for);
3546  return lua_error(L);
3547  }
3548  }
3549  // achievement not found - existing achievement group but non-existing achievement id
3550  lua_push(L, "Achievement " + std::string(id) + " not found for achievement group " + content_for);
3551  return lua_error(L);
3552  }
3553  }
3554 
3555  // achievement group not found
3556  lua_push(L, "Achievement group " + std::string(content_for) + " not found");
3557  return lua_error(L);
3558 }
3559 
3560 /**
3561  * Scrolls to given tile.
3562  * - Arg 1: location.
3563  * - Arg 2: boolean preventing scroll to fog.
3564  * - Arg 3: boolean specifying whether to warp instantly.
3565  * - Arg 4: boolean specifying whether to skip if already onscreen
3566  */
3568 {
3570  bool check_fogged = luaW_toboolean(L, 2);
3572  ? luaW_toboolean(L, 3)
3575  : luaW_toboolean(L, 3)
3578  ;
3579  if (game_display_) {
3580  game_display_->scroll_to_tile(loc, scroll, check_fogged);
3581  }
3582  return 0;
3583 }
3584 
3585 /**
3586  * Selects and highlights the given location on the map.
3587  * - Arg 1: location.
3588  * - Args 2,3: booleans
3589  */
3591 {
3592  events::command_disabler command_disabler;
3593  if(lua_isnoneornil(L, 1)) {
3595  return 0;
3596  }
3597  const map_location loc = luaW_checklocation(L, 1);
3598  if(!map().on_board(loc)) return luaL_argerror(L, 1, "not on board");
3599  bool highlight = true;
3600  if(!lua_isnoneornil(L, 2))
3601  highlight = luaW_toboolean(L, 2);
3602  const bool fire_event = luaW_toboolean(L, 3);
3604  loc, false, highlight, fire_event);
3605  return 0;
3606 }
3607 
3608 /**
3609  * Deselects any highlighted hex on the map.
3610  * No arguments or return values
3611  */
3613 {
3614  if(game_display_) {
3616  }
3617 
3618  return 0;
3619 }
3620 
3621 /**
3622  * Return true if a replay is in progress but the player has chosen to skip it
3623  */
3625 {
3627  if (!skipping) {
3628  skipping = game_state_.events_manager_->pump().context_skip_messages();
3629  }
3630  lua_pushboolean(L, skipping);
3631  return 1;
3632 }
3633 
3634 /**
3635  * Set whether to skip messages
3636  * Arg 1 (optional) - boolean
3637  */
3639 {
3640  bool skip = true;
3641  if (!lua_isnone(L, 1)) {
3642  skip = luaW_toboolean(L, 1);
3643  }
3644  game_state_.events_manager_->pump().context_skip_messages(skip);
3645  return 0;
3646 }
3647 
3648 namespace
3649 {
3650  struct lua_synchronize : mp_sync::user_choice
3651  {
3652  lua_State *L;
3653  int user_choice_index;
3654  int random_choice_index;
3655  int ai_choice_index;
3656  std::string desc;
3657  lua_synchronize(lua_State *l, const std::string& descr, int user_index, int random_index = 0, int ai_index = 0)
3658  : L(l)
3659  , user_choice_index(user_index)
3660  , random_choice_index(random_index)
3661  , ai_choice_index(ai_index != 0 ? ai_index : user_index)
3662  , desc(descr)
3663  {}
3664 
3665  virtual config query_user(int side) const override
3666  {
3667  bool is_local_ai = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).board().get_team(side).is_local_ai();
3668  config cfg;
3669  query_lua(side, is_local_ai ? ai_choice_index : user_choice_index, cfg);
3670  return cfg;
3671  }
3672 
3673  virtual config random_choice(int side) const override
3674  {
3675  config cfg;
3676  if(random_choice_index != 0 && lua_isfunction(L, random_choice_index)) {
3677  query_lua(side, random_choice_index, cfg);
3678  }
3679  return cfg;
3680  }
3681 
3682  virtual std::string description() const override
3683  {
3684  return desc;
3685  }
3686 
3687  void query_lua(int side, int function_index, config& cfg) const
3688  {
3689  lua_pushvalue(L, function_index);
3690  lua_pushnumber(L, side);
3691  if (luaW_pcall(L, 1, 1, false)) {
3692  if(!luaW_toconfig(L, -1, cfg)) {
3693  static const char* msg = "function returned to wesnoth.sync.[multi_]evaluate a table which was partially invalid";
3694  lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).log_error(msg);
3695  lua_warning(L, msg, false);
3696  }
3697  }
3698  }
3699  //Although lua's sync_choice can show a dialog, (and will in most cases)
3700  //we return false to enable other possible things that do not contain UI things.
3701  //it's in the responsibility of the umc dev to not show dialogs during prestart events.
3702  virtual bool is_visible() const override { return false; }
3703  };
3704 }//unnamed namespace for lua_synchronize
3705 
3706 /**
3707  * Ensures a value is synchronized among all the clients.
3708  * - Arg 1: optional string specifying the type id of the choice.
3709  * - Arg 2: function to compute the value, called if the client is the master.
3710  * - Arg 3: optional function, called instead of the first function if the user is not human.
3711  * - Arg 4: optional integer specifying, on which side the function should be evaluated.
3712  * - Ret 1: WML table returned by the function.
3713  */
3714 static int intf_synchronize_choice(lua_State *L)
3715 {
3716  std::string tagname = "input";
3717  t_string desc = _("input");
3718  int human_func = 0;
3719  int ai_func = 0;
3720  int side_for;
3721 
3722  int nextarg = 1;
3723  if(!lua_isfunction(L, nextarg) && luaW_totstring(L, nextarg, desc) ) {
3724  ++nextarg;
3725  }
3726  if(lua_isfunction(L, nextarg)) {
3727  human_func = nextarg++;
3728  }
3729  else {
3730  return luaL_argerror(L, nextarg, "expected a function");
3731  }
3732  if(lua_isfunction(L, nextarg)) {
3733  ai_func = nextarg++;
3734  }
3735  side_for = lua_tointeger(L, nextarg);
3736 
3737  config cfg = mp_sync::get_user_choice(tagname, lua_synchronize(L, desc, human_func, 0, ai_func), side_for);
3738  luaW_pushconfig(L, cfg);
3739  return 1;
3740 }
3741 /**
3742  * Ensures a value is synchronized among all the clients.
3743  * - Arg 1: optional string the id of this type of user input, may only contain characters a-z and '_'
3744  * - Arg 2: function to compute the value, called if the client is the master.
3745  * - Arg 3: an optional function to compute the value, if the side was null/empty controlled.
3746  * - Arg 4: an array of integers specifying, on which side the function should be evaluated.
3747  * - Ret 1: a map int -> WML tabls.
3748  */
3749 static int intf_synchronize_choices(lua_State *L)
3750 {
3751  std::string tagname = "input";
3752  t_string desc = _("input");
3753  int human_func = 0;
3754  int null_func = 0;
3755  std::vector<int> sides_for;
3756 
3757  int nextarg = 1;
3758  if(!lua_isfunction(L, nextarg) && luaW_totstring(L, nextarg, desc) ) {
3759  ++nextarg;
3760  }
3761  if(lua_isfunction(L, nextarg)) {
3762  human_func = nextarg++;
3763  }
3764  else {
3765  return luaL_argerror(L, nextarg, "expected a function");
3766  }
3767  if(lua_isfunction(L, nextarg)) {
3768  null_func = nextarg++;
3769  };
3770  sides_for = lua_check<std::vector<int>>(L, nextarg++);
3771 
3772  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())));
3773  return 1;
3774 }
3775 
3776 
3777 /**
3778  * Calls a function in an unsynced context (this specially means that all random calls used by that function will be unsynced).
3779  * This is usually used together with an unsynced if like 'if controller != network'
3780  * - Arg 1: function that will be called during the unsynced context.
3781  */
3782 static int intf_do_unsynced(lua_State *L)
3783 {
3784  set_scontext_unsynced sync;
3785  lua_pushvalue(L, 1);
3786  luaW_pcall(L, 0, 0, false);
3787  return 0;
3788 }
3789 
3790 /**
3791  * Gets all the locations matching a given filter.
3792  * - Arg 1: WML table.
3793  * - Arg 2: Optional reference unit (teleport_unit)
3794  * - Ret 1: array of integer pairs.
3795  */
3797 {
3799 
3800  std::set<map_location> res;
3801  filter_context & fc = game_state_;
3802  const terrain_filter t_filter(filter, &fc, false);
3803  if(luaW_isunit(L, 2)) {
3804  t_filter.get_locations(res, *luaW_tounit(L, 2), true);
3805  } else {
3806  t_filter.get_locations(res, true);
3807  }
3808 
3809  luaW_push_locationset(L, res);
3810  return 1;
3811 }
3812 
3813 /**
3814  * Matches a location against the given filter.
3815  * - Arg 1: location.
3816  * - Arg 2: WML table.
3817  * - Arg 3: Optional reference unit (teleport_unit)
3818  * - Ret 1: boolean.
3819  */
3821 {
3823  vconfig filter = luaW_checkvconfig(L, 2, true);
3824 
3825  if (filter.null()) {
3826  lua_pushboolean(L, true);
3827  return 1;
3828  }
3829 
3830  filter_context & fc = game_state_;
3831  const terrain_filter t_filter(filter, &fc, false);
3832  if(luaW_isunit(L, 3)) {
3833  lua_pushboolean(L, t_filter.match(loc, *luaW_tounit(L, 3)));
3834  } else {
3835  lua_pushboolean(L, t_filter.match(loc));
3836  }
3837  return 1;
3838 }
3839 
3840 
3841 
3842 /**
3843  * Matches a side against the given filter.
3844  * - Args 1: side number.
3845  * - Arg 2: WML table.
3846  * - Ret 1: boolean.
3847  */
3849 {
3850  vconfig filter = luaW_checkvconfig(L, 2, true);
3851 
3852  if (filter.null()) {
3853  lua_pushboolean(L, true);
3854  return 1;
3855  }
3856 
3857  filter_context & fc = game_state_;
3858  side_filter s_filter(filter, &fc);
3859 
3860  if(team* t = luaW_toteam(L, 1)) {
3861  lua_pushboolean(L, s_filter.match(*t));
3862  } else {
3863  unsigned side = luaL_checkinteger(L, 1) - 1;
3864  if (side >= teams().size()) return 0;
3865  lua_pushboolean(L, s_filter.match(side + 1));
3866  }
3867  return 1;
3868 }
3869 
3871 {
3872  int team_i;
3873  if(team* t = luaW_toteam(L, 1)) {
3874  team_i = t->side();
3875  } else {
3876  team_i = luaL_checkinteger(L, 1);
3877  }
3878  std::string flag = luaL_optlstring(L, 2, "", nullptr);
3879  std::string color = luaL_optlstring(L, 3, "", nullptr);
3880 
3881  if(flag.empty() && color.empty()) {
3882  return 0;
3883  }
3884  if(team_i < 1 || static_cast<std::size_t>(team_i) > teams().size()) {
3885  return luaL_error(L, "set_side_id: side number %d out of range", team_i);
3886  }
3887  team& side = board().get_team(team_i);
3888 
3889  if(!color.empty()) {
3890  side.set_color(color);
3891  }
3892  if(!flag.empty()) {
3893  side.set_flag(flag);
3894  }
3895 
3897  return 0;
3898 }
3899 
3900 static int intf_modify_ai(lua_State *L, const char* action)
3901 {
3902  int side_num;
3903  if(team* t = luaW_toteam(L, 1)) {
3904  side_num = t->side();
3905  } else {
3906  side_num = luaL_checkinteger(L, 1);
3907  }
3908  std::string path = luaL_checkstring(L, 2);
3909  config cfg {
3910  "action", action,
3911  "path", path
3912  };
3913  if(strcmp(action, "delete") == 0) {
3915  return 0;
3916  }
3917  config component = luaW_checkconfig(L, 3);
3918  std::size_t len = std::string::npos, open_brak = path.find_last_of('[');
3919  std::size_t dot = path.find_last_of('.');
3920  if(open_brak != len) {
3921  len = open_brak - dot - 1;
3922  }
3923  cfg.add_child(path.substr(dot + 1, len), component);
3925  return 0;
3926 }
3927 
3928 static int intf_switch_ai(lua_State *L)
3929 {
3930  int side_num;
3931  if(team* t = luaW_toteam(L, 1)) {
3932  side_num = t->side();
3933  } else {
3934  side_num = luaL_checkinteger(L, 1);
3935  }
3936  if(lua_isstring(L, 2)) {
3937  std::string file = luaL_checkstring(L, 2);
3938  if(!ai::manager::get_singleton().add_ai_for_side_from_file(side_num, file)) {
3939  std::string err = formatter() << "Could not load AI for side " << side_num << " from file " << file;
3940  lua_pushlstring(L, err.c_str(), err.length());
3941  return lua_error(L);
3942  }
3943  } else {
3945  }
3946  return 0;
3947 }
3948 
3949 static int intf_append_ai(lua_State *L)
3950 {
3951  int side_num;
3952  if(team* t = luaW_toteam(L, 1)) {
3953  side_num = t->side();
3954  } else {
3955  side_num = luaL_checkinteger(L, 1);
3956  }
3957  config cfg = luaW_checkconfig(L, 2);
3958  if(!cfg.has_child("ai")) {
3959  cfg = config {"ai", cfg};
3960  }
3961  bool added_dummy_stage = false;
3962  if(!cfg.mandatory_child("ai").has_child("stage")) {
3963  added_dummy_stage = true;
3964  cfg.mandatory_child("ai").add_child("stage", config {"name", "empty"});
3965  }
3967  if(added_dummy_stage) {
3968  cfg.remove_children("stage", [](const config& stage_cfg) { return stage_cfg["name"] == "empty"; });
3969  }
3971  return 0;
3972 }
3973 
3975 {
3976  unsigned i = luaL_checkinteger(L, 1);
3977  if(i < 1 || i > teams().size()) return 0;
3978  luaW_pushteam(L, board().get_team(i));
3979  return 1;
3980 }
3981 
3982 /**
3983  * Returns a proxy table array for all sides matching the given SSF.
3984  * - Arg 1: SSF
3985  * - Ret 1: proxy table array
3986  */
3988 {
3989  LOG_LUA << "intf_get_sides called: this = " << std::hex << this << std::dec << " myname = " << my_name();
3990  std::vector<int> sides;
3991  const vconfig ssf = luaW_checkvconfig(L, 1, true);
3992  if(ssf.null()) {
3993  for(const team& t : teams()) {
3994  sides.push_back(t.side());
3995  }
3996  } else {
3997  filter_context & fc = game_state_;
3998 
3999  side_filter filter(ssf, &fc);
4000  sides = filter.get_teams();
4001  }
4002 
4003  lua_settop(L, 0);
4004  lua_createtable(L, sides.size(), 0);
4005  unsigned index = 1;
4006  for(int side : sides) {
4007  luaW_pushteam(L, board().get_team(side));
4008  lua_rawseti(L, -2, index);
4009  ++index;
4010  }
4011 
4012  return 1;
4013 }
4014 
4015 /**
4016  * Adds a modification to a unit.
4017  * - Arg 1: unit.
4018  * - Arg 2: string.
4019  * - Arg 3: WML table.
4020  * - Arg 4: (optional) Whether to add to [modifications] - default true
4021  */
4022 static int intf_add_modification(lua_State *L)
4023 {
4024  unit& u = luaW_checkunit(L, 1);
4025  char const *m = luaL_checkstring(L, 2);
4026  std::string sm = m;
4027  if (sm == "advance") { // Maintain backwards compatibility
4028  sm = "advancement";
4029  deprecated_message("\"advance\" modification type", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use \"advancement\" instead.");
4030  }
4031  if (sm != "advancement" && sm != "object" && sm != "trait") {
4032  return luaL_argerror(L, 2, "unknown modification type");
4033  }
4034  bool write_to_mods = true;
4035  if (!lua_isnone(L, 4)) {
4036  write_to_mods = luaW_toboolean(L, 4);
4037  }
4038  if(sm.empty()) {
4039  write_to_mods = false;
4040  }
4041 
4042  config cfg = luaW_checkconfig(L, 3);
4043  u.add_modification(sm, cfg, !write_to_mods);
4044  return 0;
4045 }
4046 
4047 /**
4048  * Removes modifications from a unit
4049  * - Arg 1: unit
4050  * - Arg 2: table (filter as [filter_wml])
4051  * - Arg 3: type of modification (default "object")
4052  */
4053 static int intf_remove_modifications(lua_State *L)
4054 {
4055  unit& u = luaW_checkunit(L, 1);
4056  config filter = luaW_checkconfig(L, 2);
4057  std::vector<std::string> tags;
4058  if(lua_isstring(L, 3)) {
4059  tags.push_back(lua_check<std::string>(L, 3));
4060  } else if (lua_istable(L, 3)){
4061  tags = lua_check<std::vector<std::string>>(L, 3);
4062  } else {
4063  tags.push_back("object");
4064  }
4065  //TODO
4066  if(filter.attribute_count() == 1 && filter.all_children_count() == 0 && filter.attribute_range().front().first == "duration") {
4067  u.expire_modifications(filter["duration"]);
4068  } else {
4069  for(const std::string& tag : tags) {
4070  for(config& obj : u.get_modifications().child_range(tag)) {
4071  if(obj.matches(filter)) {
4072  obj["duration"] = "now";
4073  }
4074  }
4075  }
4076  u.expire_modifications("now");
4077  }
4078  return 0;
4079 }
4080 
4081 /**
4082  * Advances a unit if the unit has enough xp.
4083  * - Arg 1: unit.
4084  * - Arg 2: optional boolean whether to animate the advancement.
4085  * - Arg 3: optional boolean whether to fire advancement events.
4086  */
4087 static int intf_advance_unit(lua_State *L)
4088 {
4089  events::command_disabler command_disabler;
4090  unit& u = luaW_checkunit(L, 1, true);
4092  if(lua_isboolean(L, 2)) {
4093  par.animate(luaW_toboolean(L, 2));
4094  }
4095  if(lua_isboolean(L, 3)) {
4096  par.fire_events(luaW_toboolean(L, 3));
4097  }
4098  advance_unit_at(par);
4099  return 0;
4100 }
4101 
4102 
4103 /**
4104  * Adds a new known unit type to the help system.
4105  * - Arg 1: string.
4106  */
4107 static int intf_add_known_unit(lua_State *L)
4108 {
4109  char const *ty = luaL_checkstring(L, 1);
4110  if(!unit_types.find(ty))
4111  {
4112  std::stringstream ss;
4113  ss << "unknown unit type: '" << ty << "'";
4114  return luaL_argerror(L, 1, ss.str().c_str());
4115  }
4116  prefs::get().encountered_units().insert(ty);
4117  return 0;
4118 }
4119 
4120 /**
4121  * Adds an overlay on a tile.
4122  * - Arg 1: location.
4123  * - Arg 2: WML table.
4124  */
4126 {
4128  vconfig cfg = luaW_checkvconfig(L, 2);
4129  const vconfig &ssf = cfg.child("filter_team");
4130 
4131  std::string team_name;
4132  if (!ssf.null()) {
4133  const std::vector<int>& teams = side_filter(ssf, &game_state_).get_teams();
4134  std::vector<std::string> team_names;
4135  std::transform(teams.begin(), teams.end(), std::back_inserter(team_names),
4136  [&](int team) { return game_state_.get_disp_context().get_team(team).team_name(); });
4137  team_name = utils::join(team_names);
4138  } else {
4139  team_name = cfg["team_name"].str();
4140  }
4141 
4142  if (game_display_) {
4144  cfg["image"],
4145  cfg["halo"],
4146  team_name,
4147  cfg["name"], // Name is treated as the ID
4148  cfg["visible_in_fog"].to_bool(true),
4149  cfg["submerge"].to_double(0),
4150  cfg["z_order"].to_double(0)
4151  ));
4152  }
4153  return 0;
4154 }
4155 
4156 /**
4157  * Removes an overlay from a tile.
4158  * - Arg 1: location.
4159  * - Arg 2: optional string.
4160  */
4162 {
4164  char const *m = lua_tostring(L, 2);
4165 
4166  if (m) {
4167  if (game_display_) {
4169  }
4170  } else {
4171  if (game_display_) {
4173  }
4174  }
4175  return 0;
4176 }
4177 
4179 {
4181  const int nargs = lua_gettop(L);
4182  if(nargs < 2 || nargs > 3) {
4183  return luaL_error(L, "Wrong number of arguments to ai.log_replay() - should be 2 or 3 arguments.");
4184  }
4185  const std::string key = nargs == 2 ? luaL_checkstring(L, 1) : luaL_checkstring(L, 2);
4186  config cfg;
4187  if(nargs == 2) {
4188  recorder.add_log_data(key, luaL_checkstring(L, 2));
4189  } else if(luaW_toconfig(L, 3, cfg)) {
4190  recorder.add_log_data(luaL_checkstring(L, 1), key, cfg);
4191  } else if(!lua_isstring(L, 3)) {
4192  return luaL_argerror(L, 3, "accepts only string or config");
4193  } else {
4194  recorder.add_log_data(luaL_checkstring(L, 1), key, luaL_checkstring(L, 3));
4195  }
4196  return 0;
4197 }
4198 
4200 {
4201  lua_event_filter(game_lua_kernel& lk, int idx, const config& args) : lk(lk), args_(args)
4202  {
4203  ref_ = lk.save_wml_event(idx);
4204  }
4205  bool operator()(const game_events::queued_event& event_info) const override
4206  {
4207  bool result;
4208  return lk.run_wml_event(ref_, args_, event_info, &result) && result;
4209  }
4211  {
4213  }
4214  void serialize(config& cfg) const override {
4215  cfg.add_child("filter_lua")["code"] = "<function>";
4216  }
4217 private:
4219  int ref_;
4221 };
4222 
4223 static std::string read_event_name(lua_State* L, int idx)
4224 {
4225  if(lua_isstring(L, idx)) {
4226  return lua_tostring(L, idx);
4227  } else {
4228  return utils::join(lua_check<std::vector<std::string>>(L, idx));
4229  }
4230 }
4231 
4232 /**
4233  * Add undo actions for the current active event
4234  * Arg 1: Either a table of ActionWML or a function to call
4235  * Arg 2: (optional) If Arg 1 is a function, this is a WML table that will be passed to it
4236  */
4238 {
4239  config cfg;
4240  if(luaW_toconfig(L, 1, cfg)) {
4242  } else {
4243  luaW_toconfig(L, 2, cfg);
4245  }
4246  return 0;
4247 }
4248 
4249 /** Add a new event handler
4250  * Arg 1: Table of options.
4251  * name: Event to handle, as a string or list of strings
4252  * id: Event ID
4253  * menu_item: True if this is a menu item (an ID is required); this means removing the menu item will automatically remove this event. Default false.
4254  * first_time_only: Whether this event should fire again after the first time; default true.
4255  * priority: Number that determines execution order. Events execute in order of decreasing priority, and secondarily in order of addition.
4256  * filter: Event filters as a config with filter tags, a table of the form {filter_type = filter_contents}, or a function
4257  * filter_args: Arbitrary data that will be passed to the filter, if it is a function. Ignored if the filter is specified as WML or a table.
4258  * content: The content of the event. This is a WML table passed verbatim into the event when it fires. If no function is specified, it will be interpreted as ActionWML.
4259  * action: The function to call when the event triggers. Defaults to wesnoth.wml_actions.command.
4260  *
4261  * Lua API: wesnoth.game_events.add
4262  */
4264 {
4266  using namespace std::literals;
4267  std::string name, id = luaW_table_get_def(L, 1, "id", ""s);
4268  bool repeat = !luaW_table_get_def(L, 1, "first_time_only", true), is_menu_item = luaW_table_get_def(L, 1, "menu_item", false);
4269  double priority = luaW_table_get_def(L, 1, "priority", 0.);
4270  if(luaW_tableget(L, 1, "name")) {
4271  name = read_event_name(L, -1);
4272  } else if(is_menu_item) {
4273  if(id.empty()) {
4274  return luaL_argerror(L, 1, "non-empty id is required for a menu item");
4275  }
4276  name = "menu item " + id;
4277  }
4278  if(id.empty() && name.empty()) {
4279  return luaL_argerror(L, 1, "either a name or id is required");
4280  }
4281  auto new_handler = man.add_event_handler_from_lua(name, id, repeat, priority, is_menu_item);
4282  if(new_handler.valid()) {
4283  bool has_lua_filter = false;
4284  new_handler->set_arguments(luaW_table_get_def(L, 1, "content", config{"__empty_lua_event", true}));
4285 
4286  if(luaW_tableget(L, 1, "filter")) {
4287  int filterIdx = lua_gettop(L);
4288  config filters;
4289  if(!luaW_toconfig(L, filterIdx, filters)) {
4290  if(lua_isfunction(L, filterIdx)) {
4291  int fcnIdx = lua_absindex(L, -1);
4292  new_handler->add_filter(std::make_unique<lua_event_filter>(*this, fcnIdx, luaW_table_get_def(L, 1, "filter_args", config())));
4293  has_lua_filter = true;
4294  } else {
4295 #define READ_ONE_FILTER(key, tag) \
4296  do { \
4297  if(luaW_tableget(L, filterIdx, key)) { \
4298  if(lua_isstring(L, -1)) { \
4299  filters.add_child("insert_tag", config{ \
4300  "name", tag, \
4301  "variable", luaL_checkstring(L, -1) \
4302  }); \
4303  } else { \
4304  filters.add_child(tag, luaW_checkconfig(L, -1)); \
4305  } \
4306  } \
4307  } while(false);
4308  READ_ONE_FILTER("condition", "filter_condition");
4309  READ_ONE_FILTER("side", "filter_side");
4310  READ_ONE_FILTER("unit", "filter");
4311  READ_ONE_FILTER("attack", "filter_attack");
4312  READ_ONE_FILTER("second_unit", "filter_second");
4313  READ_ONE_FILTER("second_attack", "filter_second_attack");
4314 #undef READ_ONE_FILTER
4315  if(luaW_tableget(L, filterIdx, "formula")) {
4316  filters["filter_formula"] = luaL_checkstring(L, -1);
4317  }
4318  }
4319  }
4320  new_handler->read_filters(filters);
4321  }
4322 
4323  if(luaW_tableget(L, 1, "action")) {
4324  new_handler->set_event_ref(save_wml_event(-1), has_preloaded_);
4325  } else {
4326  if(has_lua_filter) {
4327  // This just sets the appropriate flags so the engine knows it cannot be serialized.
4328  // The register_wml_event call will override the actual event_ref so just pass LUA_NOREF here.
4329  new_handler->set_event_ref(LUA_NOREF, has_preloaded_);
4330  }
4331  new_handler->register_wml_event(*this);
4332  }
4333  }
4334  return 0;
4335 }
4336 
4337 /**
4338  * Upvalue 1: The event function
4339  * Upvalue 2: The undo function
4340  * Arg 1: The event content
4341  */
4343 {
4344  lua_pushvalue(L, lua_upvalueindex(1));
4345  lua_push(L, 1);
4346  luaW_pcall(L, 1, 0);
4347  game_state_.undo_stack_->add_custom<actions::undo_event>(lua_upvalueindex(2), config(), get_event_info());
4348  return 0;
4349 }
4350 
4351 /** Add a new event handler
4352  * Arg 1: Event to handle, as a string or list of strings; or menu item ID if this is a menu item
4353  * Arg 2: The function to call when the event triggers
4354  * Arg 3: (optional) Event priority
4355  * Arg 4: (optional, non-menu-items only) The function to call when the event is undone
4356  *
4357  * Lua API:
4358  * - wesnoth.game_events.add_repeating
4359  * - wesnoth.game_events.add_menu
4360  */
4361 template<bool is_menu_item>
4363 {
4365  bool repeat = true;
4366  std::string name = read_event_name(L, 1), id;
4367  double priority = luaL_optnumber(L, 3, 0.);
4368  if(name.empty()) {
4369  return luaL_argerror(L, 1, "must not be empty");
4370  }
4371  if(is_menu_item) {
4372  id = name;
4373  name = "menu item " + name;
4374  } else if(lua_absindex(L, -1) > 2 && lua_isfunction(L, -1)) {
4375  // If undo is provided as a separate function, link them together into a single function
4376  // The function can be either the 3rd or 4th argument.
4377  lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_undoable_event>, 2);
4378  }
4379  auto new_handler = man.add_event_handler_from_lua(name, id, repeat, priority, is_menu_item);
4380  if(new_handler.valid()) {
4381  // An event with empty arguments is not added, so set some dummy arguments
4382  new_handler->set_arguments(config{"__quick_lua_event", true});
4383  new_handler->set_event_ref(save_wml_event(2), has_preloaded_);
4384  }
4385  return 0;
4386 }
4387 
4388 /** Add a new event handler
4389  * Arg: A full event specification as a WML config
4390  *
4391  * WML API: [event]
4392  */
4394 {
4396  vconfig cfg(luaW_checkvconfig(L, 1));
4397  bool delayed_variable_substitution = cfg["delayed_variable_substitution"].to_bool(true);
4398  if(delayed_variable_substitution) {
4399  man.add_event_handler_from_wml(cfg.get_config(), *this);
4400  } else {
4402  }
4403  return 0;
4404 }
4405 
4407 {
4408  game_state_.events_manager_->remove_event_handler(luaL_checkstring(L, 1));
4409  return 0;
4410 }
4411 
4413 {
4414  if (game_display_) {
4415  game_display_->adjust_color_overlay(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), luaL_checkinteger(L, 3));
4417  }
4418  return 0;
4419 }
4420 
4422 {
4423  if(game_display_) {
4424  auto color = game_display_->get_color_overlay();
4425  lua_pushinteger(L, color.r);
4426  lua_pushinteger(L, color.g);
4427  lua_pushinteger(L, color.b);
4428  return 3;
4429  }
4430  return 0;
4431 }
4432 
4434 {
4435  if(game_display_) {
4436  auto vec = lua_check<std::vector<uint8_t>>(L, 1);
4437  if(vec.size() != 4) {
4438  return luaW_type_error(L, 1, "array of 4 integers");
4439  }
4440  color_t fade{vec[0], vec[1], vec[2], vec[3]};
4441  game_display_->fade_to(fade, std::chrono::milliseconds{luaL_checkinteger(L, 2)});
4442  }
4443  return 0;
4444 }
4445 
4446 /**
4447  * Delays engine for a while.
4448  * - Arg 1: integer.
4449  * - Arg 2: boolean (optional).
4450  */
4452 {
4453  if(gamedata().phase() == game_data::PRELOAD || gamedata().phase() == game_data::PRESTART || gamedata().phase() == game_data::INITIAL) {
4454  //don't call play_slice if the game ui is not active yet.
4455  return 0;
4456  }
4457  events::command_disabler command_disabler;
4458  using namespace std::chrono_literals;
4459  std::chrono::milliseconds delay{luaL_checkinteger(L, 1)};
4460  if(delay == 0ms) {
4462  return 0;
4463  }
4464  if(luaW_toboolean(L, 2) && game_display_ && game_display_->turbo_speed() > 0) {
4465  delay /= game_display_->turbo_speed();
4466  }
4467  const auto end_time = std::chrono::steady_clock::now() + delay;
4468  do {
4470  std::this_thread::sleep_for(10ms);
4471  } while (std::chrono::steady_clock::now() < end_time);
4472  return 0;
4473 }
4474 
4476 {
4477  // TODO: Support color = {r = 0, g = 0, b = 0}
4478  if (game_display_) {
4479  vconfig cfg(luaW_checkvconfig(L, 1));
4480 
4481  game_display &screen = *game_display_;
4482 
4483  terrain_label label(screen.labels(), cfg.get_config());
4484 
4485  screen.labels().set_label(label.location(), label.text(), label.creator(), label.team_name(), label.color(),
4486  label.visible_in_fog(), label.visible_in_shroud(), label.immutable(), label.category(), label.tooltip());
4487  }
4488  return 0;
4489 }
4490 
4492 {
4493  if (game_display_) {
4495  std::string team_name;
4496 
4497  // If there's only one parameter and it's a table, check if it contains team_name
4498  if(lua_gettop(L) == 1 && lua_istable(L, 1)) {
4499  using namespace std::literals;
4500  team_name = luaW_table_get_def(L, 1, "team_name", ""sv);
4501  } else {
4502  team_name = luaL_optstring(L, 2, "");
4503  }
4504 
4505  game_display_->labels().set_label(loc, "", -1, team_name);
4506  }
4507  return 0;
4508 }
4509 
4511 {
4512  if(game_display_) {
4513  game_display &screen = *game_display_;
4514  auto loc = luaW_checklocation(L, 1);
4515  const terrain_label* label = nullptr;
4516  switch(lua_type(L, 2)) {
4517  // Missing 2nd argument - get global label
4518  case LUA_TNONE: case LUA_TNIL:
4519  label = screen.labels().get_label(loc, "");
4520  break;
4521  // Side number - get label belonging to that side's team
4522  case LUA_TNUMBER:
4523  if(size_t n = luaL_checkinteger(L, 2); n > 0 && n <= teams().size()) {
4524  label = screen.labels().get_label(loc, teams().at(n - 1).team_name());
4525  }
4526  break;
4527  // String - get label belonging to the team with that name
4528  case LUA_TSTRING:
4529  label = screen.labels().get_label(loc, luaL_checkstring(L, 2));
4530  break;
4531  // Side userdata - get label belonging to that side's team
4532  case LUA_TUSERDATA:
4533  label = screen.labels().get_label(loc, luaW_checkteam(L, 2).team_name());
4534  break;
4535  }
4536  if(label) {
4537  config cfg;
4538  label->write(cfg);
4539  luaW_pushconfig(L, cfg);
4540  return 1;
4541  }
4542  }
4543  return 0;
4544 }
4545 
4547 {
4548  if (game_display_) {
4549  game_display & screen = *game_display_;
4550 
4551  vconfig cfg(luaW_checkvconfig(L, 1));
4552  bool clear_shroud(luaW_toboolean(L, 2));
4553 
4554  // We do this twice so any applicable redraws happen both before and after
4555  // any events caused by redrawing shroud are fired
4556  bool result = screen.maybe_rebuild();
4557  if (!result) {
4558  screen.invalidate_all();
4559  }
4560 
4561  if (clear_shroud) {
4563  for (const int side : filter.get_teams()){
4564  actions::clear_shroud(side);
4565  }
4566  screen.recalculate_minimap();
4567  }
4568 
4569  result = screen.maybe_rebuild();
4570  if (!result) {
4571  screen.invalidate_all();
4572  }
4573  }
4574  return 0;
4575 }
4576 
4577 /**
4578  * Lua frontend to the modify_ai functionality
4579  * - Arg 1: config.
4580  */
4581 static int intf_modify_ai_old(lua_State *L)
4582 {
4583  config cfg;
4584  luaW_toconfig(L, 1, cfg);
4585  int side = cfg["side"].to_int();
4587  return 0;
4588 }
4589 
4590 static int cfun_exec_candidate_action(lua_State *L)
4591 {
4592  bool exec = luaW_toboolean(L, -1);
4593  lua_pop(L, 1);
4594 
4595  lua_getfield(L, -1, "ca_ptr");
4596 
4597  ai::candidate_action *ca = static_cast<ai::candidate_action*>(lua_touserdata(L, -1));
4598  lua_pop(L, 2);
4599  if (exec) {
4600  ca->execute();
4601  return 0;
4602  }
4603  lua_pushnumber(L, ca->evaluate());
4604  return 1;
4605 }
4606 
4607 static int cfun_exec_stage(lua_State *L)
4608 {
4609  lua_getfield(L, -1, "stg_ptr");
4610  ai::stage *stg = static_cast<ai::stage*>(lua_touserdata(L, -1));
4611  lua_pop(L, 2);
4612  stg->play_stage();
4613  return 0;
4614 }
4615 
4616 static void push_component(lua_State *L, ai::component* c, const std::string &ct = "")
4617 {
4618  lua_createtable(L, 0, 0); // Table for a component
4619 
4620  lua_pushstring(L, "name");
4621  lua_pushstring(L, c->get_name().c_str());
4622  lua_rawset(L, -3);
4623 
4624  lua_pushstring(L, "engine");
4625  lua_pushstring(L, c->get_engine().c_str());
4626  lua_rawset(L, -3);
4627 
4628  lua_pushstring(L, "id");
4629  lua_pushstring(L, c->get_id().c_str());
4630  lua_rawset(L, -3);
4631 
4632  if (ct == "candidate_action") {
4633  lua_pushstring(L, "ca_ptr");
4634  lua_pushlightuserdata(L, c);
4635  lua_rawset(L, -3);
4636 
4637  lua_pushstring(L, "exec");
4638  lua_pushcclosure(L, &cfun_exec_candidate_action, 0);
4639  lua_rawset(L, -3);
4640  }
4641 
4642  if (ct == "stage") {
4643  lua_pushstring(L, "stg_ptr");
4644  lua_pushlightuserdata(L, c);
4645  lua_rawset(L, -3);
4646 
4647  lua_pushstring(L, "exec");
4648  lua_pushcclosure(L, &cfun_exec_stage, 0);
4649  lua_rawset(L, -3);
4650  }
4651 
4652 
4653  std::vector<std::string> c_types = c->get_children_types();
4654 
4655  for (std::vector<std::string>::const_iterator t = c_types.begin(); t != c_types.end(); ++t)
4656  {
4657  std::vector<ai::component*> children = c->get_children(*t);
4658  std::string type = *t;
4659  if (type == "aspect" || type == "goal" || type == "engine")
4660  {
4661  continue;
4662  }
4663 
4664  lua_pushstring(L, type.c_str());
4665  lua_createtable(L, 0, 0); // this table will be on top of the stack during recursive calls
4666 
4667  for (std::vector<ai::component*>::const_iterator i = children.begin(); i != children.end(); ++i)
4668  {
4669  lua_pushstring(L, (*i)->get_name().c_str());
4670  push_component(L, *i, type);
4671  lua_rawset(L, -3);
4672 
4673  //if (type == "candidate_action")
4674  //{
4675  // ai::candidate_action *ca = dynamic_cast<ai::candidate_action*>(*i);
4676  // ca->execute();
4677  //}
4678  }
4679 
4680  lua_rawset(L, -3); // setting the child table
4681  }
4682 
4683 
4684 }
4685 
4686 /**
4687  * Debug access to the ai tables
4688  * - Arg 1: int
4689  * - Ret 1: ai table
4690  */
4691 static int intf_debug_ai(lua_State *L)
4692 {
4693  if (!game_config::debug) { // This function works in debug mode only
4694  return 0;
4695  }
4696  int side;
4697  if(team* t = luaW_toteam(L, 1)) {
4698  side = t->side();
4699  } else {
4700  side = luaL_checkinteger(L, 1);
4701  }
4702  lua_pop(L, 1);
4703 
4705 
4706  // Bad, but works
4707  std::vector<ai::component*> engines = c->get_children("engine");
4708  ai::engine_lua* lua_engine = nullptr;
4709  for (std::vector<ai::component*>::const_iterator i = engines.begin(); i != engines.end(); ++i)
4710  {
4711  if ((*i)->get_name() == "lua")
4712  {
4713  lua_engine = dynamic_cast<ai::engine_lua *>(*i);
4714  }
4715  }
4716 
4717  // Better way, but doesn't work
4718  //ai::component* e = ai::manager::get_singleton().get_active_ai_holder_for_side_dbg(side).get_component(c, "engine[lua]");
4719  //ai::engine_lua* lua_engine = dynamic_cast<ai::engine_lua *>(e);
4720 
4721  if (lua_engine == nullptr)
4722  {
4723  //no lua engine is defined for this side.
4724  //so set up a dummy engine
4725 
4726  ai::ai_composite * ai_ptr = dynamic_cast<ai::ai_composite *>(c);
4727 
4728  assert(ai_ptr);
4729 
4730  ai::ai_context& ai_context = ai_ptr->get_ai_context();
4732 
4733  lua_engine = new ai::engine_lua(ai_context, cfg);
4734  LOG_LUA << "Created new dummy lua-engine for debug_ai().";
4735 
4736  //and add the dummy engine as a component
4737  //to the manager, so we could use it later
4738  cfg.add_child("engine", lua_engine->to_config());
4739  ai::component_manager::add_component(c, "engine[]", cfg);
4740  }
4741 
4742  lua_engine->push_ai_table(); // stack: [-1: ai_context]
4743 
4744  lua_pushstring(L, "components");
4745  push_component(L, c); // stack: [-1: component tree; -2: ai context]
4746  lua_rawset(L, -3);
4747 
4748  return 1;
4749 }
4750 
4751 /** Allow undo sets the flag saying whether the event has mutated the game to false. */
4753 {
4754  bool allow;
4755  t_string reason;
4756  // The extra iststring is required to prevent totstring from converting a bool value
4757  if(luaW_iststring(L, 1) && luaW_totstring(L, 1, reason)) {
4758  allow = false;
4759  } else {
4760  allow = luaW_toboolean(L, 1);
4761  luaW_totstring(L, 2, reason);
4762  }
4763  gamedata().set_allow_end_turn(allow, reason);
4764  return 0;
4765 }
4766 
4767 /** Allow undo sets the flag saying whether the event has mutated the game to false. */
4769 {
4770  if(lua_isboolean(L, 1)) {
4772  }
4773  else {
4775  }
4776  return 0;
4777 }
4778 
4780 {
4782  return 0;
4783 }
4784 
4785 /** Adding new time_areas dynamically with Standard Location Filters.
4786  * Arg 1: Area ID
4787  * Arg 2: Area locations (either a filter or a list of locations)
4788  * Arg 3: (optional) Area schedule - WML table with [time] tags and optional current_time=
4789  */
4791 {
4792  log_scope("time_area");
4793 
4794  std::string id;
4795  std::set<map_location> locs;
4796  config times;
4797 
4798  if(lua_gettop(L) == 1) {
4799  vconfig cfg = luaW_checkvconfig(L, 1);
4800  deprecated_message("Single-argument wesnoth.map.place_area is deprecated. Instead, pass ID, filter, and schedule as three separate arguments.", DEP_LEVEL::INDEFINITE, {1, 17, 0});
4801  id = cfg["id"].str();
4802  const terrain_filter filter(cfg, &game_state_, false);
4803  filter.get_locations(locs, true);
4804  times = cfg.get_parsed_config();
4805  } else {
4806  id = luaL_checkstring(L, 1);
4807  if(!lua_isnoneornil(L, 3))
4808  times = luaW_checkconfig(L, 3);
4809  vconfig cfg{config()};
4810  if(luaW_tovconfig(L, 2, cfg)) {
4811  // Second argument is a location filter
4812  const terrain_filter filter(cfg, &game_state_, false);
4813  filter.get_locations(locs, true);
4814  } else {
4815  // Second argument is an array of locations
4816  luaW_check_locationset(L, 2);
4817  }
4818  }
4819 
4820  tod_man().add_time_area(id, locs, times);
4821  LOG_LUA << "Lua inserted time_area '" << id << "'";
4822  return 0;
4823 }
4824 
4825 /** Removing new time_areas dynamically with Standard Location Filters. */
4827 {
4828  log_scope("remove_time_area");
4829 
4830  const char * id = luaL_checkstring(L, 1);
4831  tod_man().remove_time_area(id);
4832  LOG_LUA << "Lua removed time_area '" << id << "'";
4833 
4834  return 0;
4835 }
4836 
4838 {
4839  map_location loc;
4840  if(luaW_tolocation(L, 1, loc)) {
4841  int area_index = tod_man().get_area_on_hex(loc).first;
4842  if(area_index < 0) {
4843  lua_pushnil(L);
4844  return 1;
4845  }
4846  luaW_push_schedule(L, area_index);
4847  return 1;
4848  } else {
4849  std::string area_id = luaL_checkstring(L, 1);
4850  const auto& area_ids = tod_man().get_area_ids();
4851  if(auto iter = std::find(area_ids.begin(), area_ids.end(), area_id); iter == area_ids.end()) {
4852  lua_pushnil(L);
4853  return 1;
4854  } else {
4855  luaW_push_schedule(L, std::distance(area_ids.begin(), iter));
4856  return 1;
4857  }
4858  }
4859 }
4860 
4861 /** Replacing the current time of day schedule. */
4863 {
4864  map_location loc;
4865  if(luaL_testudata(L, 1, "schedule")) {
4866  // Replace the global schedule with a time area's schedule
4867  // Replacing the global schedule with the global schedule
4868  // is also supported but obviously a no-op
4869  int area = luaW_check_schedule(L, 1);
4870  if(area >= 0) tod_man().replace_schedule(tod_man().times(area));
4871  } else {
4872  vconfig cfg = luaW_checkvconfig(L, 1);
4873 
4874  if(cfg.get_children("time").empty()) {
4875  ERR_LUA << "attempted to to replace ToD schedule with empty schedule";
4876  } else {
4878  if (game_display_) {
4880  }
4881  LOG_LUA << "replaced ToD schedule";
4882  }
4883  }
4884  return 0;
4885 }
4886 
4888 {
4889  if (game_display_) {
4890  point scroll_to(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2));
4891  game_display_->scroll(scroll_to, true);
4892 
4893  lua_remove(L, 1);
4894  lua_remove(L, 1);
4895  lua_push(L, 25);
4896  intf_delay(L);
4897  }
4898 
4899  return 0;
4900 }
4901 
4902 namespace {
4903  struct lua_report_generator : reports::generator
4904  {
4905  lua_State *mState;
4906  std::string name;
4907  lua_report_generator(lua_State *L, const std::string &n)
4908  : mState(L), name(n) {}
4909  virtual config generate(const reports::context & rc);
4910  };
4911 
4912  config lua_report_generator::generate(const reports::context & /*rc*/)
4913  {
4914  lua_State *L = mState;
4915  config cfg;
4916  if (!luaW_getglobal(L, "wesnoth", "interface", "game_display", name))
4917  return cfg;
4918  if (!luaW_pcall(L, 0, 1)) return cfg;
4919  luaW_toconfig(L, -1, cfg);
4920  lua_pop(L, 1);
4921  return cfg;
4922  }
4923 }//unnamed namespace for lua_report_generator
4924 
4925 /**
4926  * Executes its upvalue as a theme item generator.
4927  */
4928 int game_lua_kernel::impl_theme_item(lua_State *L, const std::string& m)
4929 {
4931  luaW_pushconfig(L, reports_.generate_report(m.c_str(), temp_context , true));
4932  return 1;
4933 }
4934 
4935 /**
4936  * Creates a field of the theme_items table and returns it (__index metamethod).
4937  */
4939 {
4940  char const *m = luaL_checkstring(L, 2);
4941  lua_cpp::push_closure(L, std::bind(&game_lua_kernel::impl_theme_item, this, std::placeholders::_1, std::string(m)), 0);
4942  lua_pushvalue(L, 2);
4943  lua_pushvalue(L, -2);
4944  lua_rawset(L, 1);
4945  reports_.register_generator(m, new lua_report_generator(L, m));
4946  return 1;
4947 }
4948 
4949 /**
4950  * Sets a field of the theme_items table (__newindex metamethod).
4951  */
4953 {
4954  char const *m = luaL_checkstring(L, 2);
4955  lua_pushvalue(L, 2);
4956  lua_pushvalue(L, 3);
4957  lua_rawset(L, 1);
4958  reports_.register_generator(m, new lua_report_generator(L, m));
4959  return 0;
4960 }
4961 
4962 /**
4963  * Get all available theme_items (__dir metamethod).
4964  */
4966 {
4968  return 1;
4969 }
4970 
4971 /**
4972  * Gets all the WML variables currently set.
4973  * - Ret 1: WML table
4974  */
4976  luaW_pushconfig(L, gamedata().get_variables());
4977  return 1;
4978 }
4979 
4980 /**
4981  * Teeleports a unit to a location.
4982  * Arg 1: unit
4983  * Arg 2: target location
4984  * Arg 3: bool (ignore_passability)
4985  * Arg 4: bool (clear_shroud)
4986  * Arg 5: bool (animate)
4987  */
4989 {
4990  events::command_disabler command_disabler;
4991  unit_ptr u = luaW_checkunit_ptr(L, 1, true);
4993  bool check_passability = !luaW_toboolean(L, 3);
4994  bool clear_shroud = luaW_toboolean(L, 4);
4995  bool animate = luaW_toboolean(L, 5);
4996 
4997  if (dst == u->get_location() || !map().on_board(dst)) {
4998  return 0;
4999  }
5000  const map_location vacant_dst = find_vacant_tile(dst, pathfind::VACANT_ANY, check_passability ? u.get() : nullptr);
5001  if (!map().on_board(vacant_dst)) {
5002  return 0;
5003  }
5004  // Clear the destination hex before the move (so the animation can be seen).
5005  actions::shroud_clearer clearer;
5006  if ( clear_shroud ) {
5007  clearer.clear_dest(vacant_dst, *u);
5008  }
5009 
5010  map_location src_loc = u->get_location();
5011 
5012  std::vector<map_location> teleport_path;
5013  teleport_path.push_back(src_loc);
5014  teleport_path.push_back(vacant_dst);
5015  unit_display::move_unit(teleport_path, u, animate);
5016 
5017  units().move(src_loc, vacant_dst);
5019 
5020  u = units().find(vacant_dst).get_shared_ptr();
5021  u->anim_comp().set_standing();
5022 
5023  if ( clear_shroud ) {
5024  // Now that the unit is visibly in position, clear the shroud.
5025  clearer.clear_unit(vacant_dst, *u);
5026  }
5027 
5028  if (map().is_village(vacant_dst)) {
5029  actions::get_village(vacant_dst, u->side());
5030  }
5031 
5032  game_display_->invalidate_unit_after_move(src_loc, vacant_dst);
5033 
5034  // Sighted events.
5035  clearer.fire_events();
5036  return 0;
5037 }
5038 
5039 /**
5040  * Logs a message
5041  * Arg 1: (optional) Logger; "wml" for WML errors or deprecations
5042  * Arg 2: Message
5043  * Arg 3: Whether to print to chat (always true if arg 1 is "wml")
5044  */
5045 int game_lua_kernel::intf_log(lua_State *L)
5046 {
5047  const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) : "";
5048  const std::string& msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
5049 
5050  if(logger == "wml" || logger == "WML") {
5051  lg::log_to_chat() << msg << '\n';
5052  ERR_WML << msg;
5053  } else {
5054  bool in_chat = luaW_toboolean(L, -1);
5055  game_state_.events_manager_->pump().put_wml_message(logger,msg,in_chat);
5056  }
5057  return 0;
5058 }
5059 
5060 int game_lua_kernel::intf_get_fog_or_shroud(lua_State *L, bool fog)
5061 {
5062  team& t = luaW_checkteam(L, 1, board());
5064  lua_pushboolean(L, fog ? t.fogged(loc) : t.shrouded(loc));
5065  return 1;
5066 }
5067 
5068 /**
5069  * Implements the lifting and resetting of fog via WML.
5070  * Keeping affect_normal_fog as false causes only the fog override to be affected.
5071  * Otherwise, fog lifting will be implemented similar to normal sight (cannot be
5072  * individually reset and ends at the end of the turn), and fog resetting will, in
5073  * addition to removing overrides, extend the specified teams' normal fog to all
5074  * hexes.
5075  *
5076  * Arg 1: (optional) Side number, or list of side numbers
5077  * Arg 2: List of locations; each is a two-element array or a table with x and y keys
5078  * Arg 3: (optional) boolean
5079  */
5080 int game_lua_kernel::intf_toggle_fog(lua_State *L, const bool clear)
5081 {
5082  bool affect_normal_fog = false;
5083  if(lua_isboolean(L, -1)) {
5084  affect_normal_fog = luaW_toboolean(L, -1);
5085  }
5086  std::set<int> sides;
5087  if(team* t = luaW_toteam(L, 1)) {
5088  sides.insert(t->side());
5089  } else if(lua_isnumber(L, 1)) {
5090  sides.insert(lua_tointeger(L, 1));
5091  } else if(lua_istable(L, 1) && lua_istable(L, 2)) {
5092  const auto& v = lua_check<std::vector<int>>(L, 1);
5093  sides.insert(v.begin(), v.end());
5094  } else {
5095  for(const team& t : teams()) {
5096  sides.insert(t.side()+1);
5097  }
5098  }
5099  const auto& locs = luaW_check_locationset(L, lua_istable(L, 2) ? 2 : 1);
5100 
5101  for(const int &side_num : sides) {
5102  if(side_num < 1 || static_cast<std::size_t>(side_num) > teams().size()) {
5103  continue;
5104  }
5105  team &t = board().get_team(side_num);
5106  if(!clear) {
5107  // Extend fog.
5108  t.remove_fog_override(locs);
5109  if(affect_normal_fog) {
5110  t.refog();
5111  }
5112  } else if(!affect_normal_fog) {
5113  // Force the locations clear of fog.
5114  t.add_fog_override(locs);
5115  } else {
5116  // Simply clear fog from the locations.
5117  for(const map_location &hex : locs) {
5118  t.clear_fog(hex);
5119  }
5120  }
5121  }
5122 
5123  // Flag a screen update.
5126  return 0;
5127 }
5128 
5129 // Invokes a synced command
5130 static int intf_invoke_synced_command(lua_State* L)
5131 {
5132  const std::string name = luaL_checkstring(L, 1);
5133  auto it = synced_command::registry().find(name);
5134  config cmd;
5135  if(it == synced_command::registry().end()) {
5136  // Custom command
5137  if(!luaW_getglobal(L, "wesnoth", "custom_synced_commands", name)) {
5138  return luaL_argerror(L, 1, "Unknown synced command");
5139  }
5140  config& cmd_tag = cmd.child_or_add("custom_command");
5141  cmd_tag["name"] = name;
5142  if(!lua_isnoneornil(L, 2)) {
5143  cmd_tag.add_child("data", luaW_checkconfig(L, 2));
5144  }
5145  } else {
5146  // Built-in command
5147  cmd.add_child(name, luaW_checkconfig(L, 2));
5148  }
5149  // Now just forward to the WML action.
5150  luaW_getglobal(L, "wesnoth", "wml_actions", "do_command");
5151  luaW_pushconfig(L, cmd);
5152  luaW_pcall(L, 1, 0);
5153  return 0;
5154 }
5155 
5159 };
5160 #define CALLBACK_GETTER(name, type) LATTR_GETTER(name, lua_index_raw, callbacks_tag, ) { lua_pushcfunction(L, &impl_null_callback<type>); return lua_index_raw(L); }
5162 
5163 template<typename Ret>
5164 static int impl_null_callback(lua_State* L) {
5165  if constexpr(std::is_same_v<Ret, void>) return 0;
5166  else lua_push(L, Ret());
5167  return 1;
5168 };
5169 
5170 template<> struct lua_object_traits<callbacks_tag> {
5171  inline static auto metatable = "game_events";
5172  inline static game_lua_kernel& get(lua_State* L, int) {
5173  return lua_kernel_base::get_lua_kernel<game_lua_kernel>(L);
5174  }
5175 };
5176 
5177 namespace {
5178 CALLBACK_GETTER("on_event", void);
5179 CALLBACK_GETTER("on_load", void);
5180 CALLBACK_GETTER("on_save", config);
5181 CALLBACK_GETTER("on_mouse_action", void);
5182 CALLBACK_GETTER("on_mouse_button", bool);
5183 CALLBACK_GETTER("on_mouse_move", void);
5184 }
5185 
5186 static int impl_game_events_dir(lua_State* L) {
5187  return callbacksReg.dir(L);
5188 }
5189 
5190 static int impl_game_events_get(lua_State* L) {
5191  return callbacksReg.get(L);
5192 }
5193 
5194 template<typename Ret = void>
5195 static bool impl_get_callback(lua_State* L, const std::string& name) {
5196  int top = lua_gettop(L);
5197  if(!luaW_getglobal(L, "wesnoth", "game_events")) {
5198  return false;
5199  }
5200  lua_getfield(L, -1, name.c_str()); // calls impl_game_events_get
5201  lua_pushcfunction(L, &impl_null_callback<Ret>);
5202  if(lua_rawequal(L, -1, -2)) {
5203  lua_settop(L, top);
5204  return false;
5205  }
5206  lua_pop(L, 1);
5207  lua_remove(L, -2);
5208  return true;
5209 }
5210 
5211 // END CALLBACK IMPLEMENTATION
5212 
5214  return game_state_.board_;
5215 }
5216 
5218  return game_state_.board_.units();
5219 }
5220 
5221 std::vector<team> & game_lua_kernel::teams() {
5222  return game_state_.board_.teams();
5223 }
5224 
5226  return game_state_.board_.map();
5227 }
5228 
5230  return game_state_.gamedata_;
5231 }
5232 
5234  return game_state_.tod_manager_;
5235 }
5236 
5238  return *queued_events_.top();
5239 }
5240 
5241 
5243  : lua_kernel_base()
5244  , game_display_(nullptr)
5245  , game_state_(gs)
5246  , play_controller_(pc)
5247  , reports_(reports_object)
5248  , level_lua_()
5249  , EVENT_TABLE(LUA_NOREF)
5250  , queued_events_()
5251  , map_locked_(0)
5252 {
5253  static game_events::queued_event default_queued_event("_from_lua", "", map_location(), map_location(), config());
5254  queued_events_.push(&default_queued_event);
5255 
5256  lua_State *L = mState;
5257 
5258  cmd_log_ << "Registering game-specific wesnoth lib functions...\n";
5259 
5260  // Put some callback functions in the scripting environment.
5261  static luaL_Reg const callbacks[] {
5262  { "add_known_unit", &intf_add_known_unit },
5263  { "get_era", &intf_get_era },
5264  { "get_resource", &intf_get_resource },
5265  { "modify_ai", &intf_modify_ai_old },
5266  { "cancel_action", &dispatch<&game_lua_kernel::intf_cancel_action > },
5267  { "log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
5268  { "log", &dispatch<&game_lua_kernel::intf_log > },
5269  { "redraw", &dispatch<&game_lua_kernel::intf_redraw > },
5270  { "simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
5271  { nullptr, nullptr }
5272  };lua_getglobal(L, "wesnoth");
5273  if (!lua_istable(L,-1)) {
5274  lua_newtable(L);
5275  }
5276  luaL_setfuncs(L, callbacks, 0);
5277 
5278  lua_setglobal(L, "wesnoth");
5279 
5280  lua_getglobal(L, "gui");
5281  lua_pushcfunction(L, &dispatch<&game_lua_kernel::intf_gamestate_inspector>);
5282  lua_setfield(L, -2, "show_inspector");
5283  lua_pushcfunction(L, &lua_gui2::intf_show_recruit_dialog);
5284  lua_setfield(L, -2, "show_recruit_dialog");
5285  lua_pushcfunction(L, &lua_gui2::intf_show_recall_dialog);
5286  lua_setfield(L, -2, "show_recall_dialog");
5287  lua_pop(L, 1);
5288 
5290  // Create the unit_test module
5291  lua_newtable(L);
5292  static luaL_Reg const test_callbacks[] {
5293  { "fire_wml_menu_item", &dispatch<&game_lua_kernel::intf_fire_wml_menu_item> },
5294  { nullptr, nullptr }
5295  };
5296  luaL_setfuncs(L, test_callbacks, 0);
5297  lua_setglobal(L, "unit_test");
5298  }
5299 
5300  // Create the getside metatable.
5302 
5303  // Create the gettype metatable.
5305 
5306  //Create the getrace metatable
5308 
5309  //Create the unit metatables
5312 
5313  // Create the vconfig metatable.
5315 
5316  // Create the unit_types table
5318 
5319  // Create the terrainmap metatables
5321 
5322  // Create the terrain_types table
5323  cmd_log_ << "Adding terrain_types table...\n";
5324  lua_getglobal(L, "wesnoth");
5325  lua_newuserdatauv(L, 0, 0);
5326  lua_createtable(L, 0, 2);
5327  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_get_terrain_info>);
5328  lua_setfield(L, -2, "__index");
5329  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_get_terrain_list>);
5330  lua_setfield(L, -2, "__dir");
5331  lua_pushstring(L, "terrain types");
5332  lua_setfield(L, -2, "__metatable");
5333  lua_setmetatable(L, -2);
5334  lua_setfield(L, -2, "terrain_types");
5335  lua_pop(L, 1);
5336 
5337  // Create the ai elements table.
5338  cmd_log_ << "Adding ai elements table...\n";
5339 
5341 
5342  // Create the current variable with its metatable.
5343  cmd_log_ << "Adding wesnoth current table...\n";
5344 
5345  lua_getglobal(L, "wesnoth");
5346  lua_newuserdatauv(L, 0, 0);
5347  lua_createtable(L, 0, 2);
5348  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_get>);
5349  lua_setfield(L, -2, "__index");
5350  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_dir>);
5351  lua_setfield(L, -2, "__dir");
5352  lua_pushboolean(L, true);
5353  lua_setfield(L, -2, "__dir_tablelike");
5354  lua_pushstring(L, "current config");
5355  lua_setfield(L, -2, "__metatable");
5356  lua_setmetatable(L, -2);
5357  lua_setfield(L, -2, "current");
5358  lua_pop(L, 1);
5359 
5360  // Add functions to the WML module
5361  lua_getglobal(L, "wml");
5362  static luaL_Reg const wml_callbacks[] {
5363  {"tovconfig", &lua_common::intf_tovconfig},
5364  {"eval_conditional", &intf_eval_conditional},
5365  // These aren't actually part of the API - they're used internally by the variable metatable.
5366  { "get_variable", &dispatch<&game_lua_kernel::intf_get_variable>},
5367  { "set_variable", &dispatch<&game_lua_kernel::intf_set_variable>},
5368  { "get_all_vars", &dispatch<&game_lua_kernel::intf_get_all_vars>},
5369  { nullptr, nullptr }
5370  };
5371  luaL_setfuncs(L, wml_callbacks, 0);
5372  lua_pop(L, 1);
5373 
5374  // Add functions to the map module
5375  luaW_getglobal(L, "wesnoth", "map");
5376  static luaL_Reg const map_callbacks[] {
5377  // Map methods
5378  {"terrain_mask", &intf_terrain_mask},
5379  {"on_board", &intf_on_board},
5380  {"on_border", &intf_on_border},
5381  {"iter", &intf_terrainmap_iter},
5382  // Village operations
5383  {"get_owner", &dispatch<&game_lua_kernel::intf_get_village_owner>},
5384  {"set_owner", &dispatch<&game_lua_kernel::intf_set_village_owner>},
5385  // Label operations
5386  {"add_label", &dispatch<&game_lua_kernel::intf_add_label>},
5387  {"remove_label", &dispatch<&game_lua_kernel::intf_remove_label>},
5388  {"get_label", &dispatch<&game_lua_kernel::intf_get_label>},
5389  // Time area operations
5390  {"place_area", &dispatch<&game_lua_kernel::intf_add_time_area>},
5391  {"remove_area", &dispatch<&game_lua_kernel::intf_remove_time_area>},
5392  {"get_area", &dispatch<&game_lua_kernel::intf_get_time_area>},
5393  // Filters
5394  {"find", &dispatch<&game_lua_kernel::intf_get_locations>},
5395  {"matches", &dispatch<&game_lua_kernel::intf_match_location>},
5396  {"replace_if_failed", intf_replace_if_failed},
5397  { nullptr, nullptr }
5398  };
5399  luaL_setfuncs(L, map_callbacks, 0);
5400  lua_pop(L, 1);
5401 
5402  // Create the units module
5403  cmd_log_ << "Adding units module...\n";
5404  static luaL_Reg const unit_callbacks[] {
5405  {"advance", &intf_advance_unit},
5406  {"clone", &intf_copy_unit},
5407  {"erase", &dispatch<&game_lua_kernel::intf_erase_unit>},
5408  {"extract", &dispatch<&game_lua_kernel::intf_extract_unit>},
5409  {"matches", &dispatch<&game_lua_kernel::intf_match_unit>},
5410  {"select", &dispatch<&game_lua_kernel::intf_select_unit>},
5411  {"to_map", &dispatch<&game_lua_kernel::intf_put_unit>},
5412  {"to_recall", &dispatch<&game_lua_kernel::intf_put_recall_unit>},
5413  {"transform", &intf_transform_unit},
5414  {"teleport", &dispatch<&game_lua_kernel::intf_teleport>},
5415 
5416  {"ability", &dispatch<&game_lua_kernel::intf_unit_ability>},
5417  {"defense_on", &intf_unit_defense},
5418  {"jamming_on", &intf_unit_jamming_cost},
5419  {"movement_on", &intf_unit_movement_cost},
5420  {"resistance_against", intf_unit_resistance},
5421  {"vision_on", &intf_unit_vision_cost},
5422 
5423  {"add_modification", &intf_add_modification},
5424  {"remove_modifications", &intf_remove_modifications},
5425  // Static functions
5426  {"create", &intf_create_unit},
5427  {"find_on_map", &dispatch<&game_lua_kernel::intf_get_units>},
5428  {"find_on_recall", &dispatch<&game_lua_kernel::intf_get_recall_units>},
5429  {"get", &dispatch<&game_lua_kernel::intf_get_unit>},
5430  {"get_hovered", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
5431  {"create_animator", &dispatch<&game_lua_kernel::intf_create_animator>},
5432  {"create_weapon", intf_create_attack},
5433 
5434  { nullptr, nullptr }
5435  };
5436  lua_getglobal(L, "wesnoth");
5437  lua_newtable(L);
5438  luaL_setfuncs(L, unit_callbacks, 0);
5439  lua_setfield(L, -2, "units");
5440  lua_pop(L, 1);
5441 
5442  // Create sides module
5443  cmd_log_ << "Adding sides module...\n";
5444  static luaL_Reg const side_callbacks[] {
5445  { "is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy> },
5446  { "matches", &dispatch<&game_lua_kernel::intf_match_side> },
5447  { "set_id", &dispatch<&game_lua_kernel::intf_set_side_id> },
5448  { "append_ai", &intf_append_ai },
5449  { "debug_ai", &intf_debug_ai },
5450  { "switch_ai", &intf_switch_ai },
5451  // Static functions
5452  { "find", &dispatch<&game_lua_kernel::intf_get_sides> },
5453  { "get", &dispatch<&game_lua_kernel::intf_get_side> },
5454  { "create", &dispatch<&game_lua_kernel::intf_create_side> },
5455  // Shroud operations
5456  {"place_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, true>},
5457  {"remove_shroud", &dispatch2<&game_lua_kernel::intf_toggle_shroud, false>},
5458  {"override_shroud", &dispatch<&game_lua_kernel::intf_override_shroud>},
5459  {"is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false>},
5460  // Fog operations
5461  {"place_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false>},
5462  {"remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true>},
5463  {"is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true>},
5464  { nullptr, nullptr }
5465  };
5466  std::vector<lua_cpp::Reg> const cpp_side_callbacks {
5467  {"add_ai_component", std::bind(intf_modify_ai, std::placeholders::_1, "add")},
5468  {"delete_ai_component", std::bind(intf_modify_ai, std::placeholders::_1, "delete")},
5469  {"change_ai_component", std::bind(intf_modify_ai, std::placeholders::_1, "change")},
5470  {nullptr, nullptr}
5471  };
5472 
5473  lua_getglobal(L, "wesnoth");
5474  lua_newtable(L);
5475  luaL_setfuncs(L, side_callbacks, 0);
5476  lua_cpp::set_functions(L, cpp_side_callbacks);
5477  lua_setfield(L, -2, "sides");
5478  lua_pop(L, 1);
5479 
5480  // Create the interface module
5481  cmd_log_ << "Adding interface module...\n";
5482  static luaL_Reg const intf_callbacks[] {
5483  {"add_hex_overlay", &dispatch<&game_lua_kernel::intf_add_tile_overlay>},
5484  {"remove_hex_overlay", &dispatch<&game_lua_kernel::intf_remove_tile_overlay>},
5485  {"get_color_adjust", &dispatch<&game_lua_kernel::intf_get_color_adjust>},
5486  {"color_adjust", &dispatch<&game_lua_kernel::intf_color_adjust>},
5487  {"screen_fade", &dispatch<&game_lua_kernel::intf_screen_fade>},
5488  {"delay", &dispatch<&game_lua_kernel::intf_delay>},
5489  {"deselect_hex", &dispatch<&game_lua_kernel::intf_deselect_hex>},
5490  {"highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex>},
5491  {"float_label", &dispatch<&game_lua_kernel::intf_float_label>},
5492  {"get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit>},
5493  {"get_hovered_hex", &dispatch<&game_lua_kernel::intf_get_mouseover_tile>},
5494  {"get_selected_hex", &dispatch<&game_lua_kernel::intf_get_selected_tile>},
5495  {"lock", &dispatch<&game_lua_kernel::intf_lock_view>},
5496  {"is_locked", &dispatch<&game_lua_kernel::intf_view_locked>},
5497  {"scroll", &dispatch<&game_lua_kernel::intf_scroll>},
5498  {"scroll_to_hex", &dispatch<&game_lua_kernel::intf_scroll_to_tile>},
5499  {"skip_messages", &dispatch<&game_lua_kernel::intf_skip_messages>},
5500  {"is_skipping_messages", &dispatch<&game_lua_kernel::intf_is_skipping_messages>},
5501  {"zoom", &dispatch<&game_lua_kernel::intf_zoom>},
5502  {"clear_menu_item", &dispatch<&game_lua_kernel::intf_clear_menu_item>},
5503  {"set_menu_item", &dispatch<&game_lua_kernel::intf_set_menu_item>},
5504  {"allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn>},
5505  {"clear_chat_messages", &dispatch<&game_lua_kernel::intf_clear_messages>},
5506  {"end_turn", &dispatch<&game_lua_kernel::intf_end_turn>},
5507  {"get_viewing_side", &intf_get_viewing_side},
5508  {"add_chat_message", &dispatch<&game_lua_kernel::intf_message>},
5509  {"add_overlay_text", &dispatch2<&game_lua_kernel::intf_set_floating_label, true>},
5510  {"handle_user_interact", &intf_handle_user_interact},
5511  { nullptr, nullptr }
5512  };
5513  lua_getglobal(L, "wesnoth");
5514  lua_newtable(L);
5515  luaL_setfuncs(L, intf_callbacks, 0);
5516  lua_setfield(L, -2, "interface");
5517  lua_pop(L, 1);
5518 
5519  // Create the achievements module
5520  cmd_log_ << "Adding achievements module...\n";
5521  static luaL_Reg const achievement_callbacks[] {
5522  { "set", &dispatch<&game_lua_kernel::intf_set_achievement> },
5523  { "has", &dispatch<&game_lua_kernel::intf_has_achievement> },
5524  { "get", &dispatch<&game_lua_kernel::intf_get_achievement> },
5525  { "progress", &dispatch<&game_lua_kernel::intf_progress_achievement> },
5526  { "has_sub_achievement", &dispatch<&game_lua_kernel::intf_has_sub_achievement> },
5527  { "set_sub_achievement", &dispatch<&game_lua_kernel::intf_set_sub_achievement> },
5528  { nullptr, nullptr }
5529  };
5530  lua_getglobal(L, "wesnoth");
5531  lua_newtable(L);
5532  luaL_setfuncs(L, achievement_callbacks, 0);
5533  lua_setfield(L, -2, "achievements");
5534  lua_pop(L, 1);
5535 
5536  // Create the audio module
5537  cmd_log_ << "Adding audio module...\n";
5538  static luaL_Reg const audio_callbacks[] {
5539  { "play", &dispatch<&game_lua_kernel::intf_play_sound > },
5540  { nullptr, nullptr }
5541  };
5542  lua_getglobal(L, "wesnoth");
5543  lua_newtable(L);
5544  luaL_setfuncs(L, audio_callbacks, 0);
5545  lua_setfield(L, -2, "audio");
5546  lua_pop(L, 1);
5547 
5548  // Create the paths module
5549  cmd_log_ << "Adding paths module...\n";
5550  static luaL_Reg const path_callbacks[] {
5551  { "find_cost_map", &dispatch<&game_lua_kernel::intf_find_cost_map > },
5552  { "find_path", &dispatch<&game_lua_kernel::intf_find_path > },
5553  { "find_reach", &dispatch<&game_lua_kernel::intf_find_reach > },
5554  { "find_vacant_hex", &dispatch<&game_lua_kernel::intf_find_vacant_tile > },
5555  { "find_vision_range", &dispatch<&game_lua_kernel::intf_find_vision_range > },
5556  { nullptr, nullptr }
5557  };
5558  lua_getglobal(L, "wesnoth");
5559  lua_newtable(L);
5560  luaL_setfuncs(L, path_callbacks, 0);
5561  lua_setfield(L, -2, "paths");
5562  lua_pop(L, 1);
5563 
5564  // Create the sync module
5565  cmd_log_ << "Adding sync module...\n";
5566  static luaL_Reg const sync_callbacks[] {
5567  { "invoke_command", &intf_invoke_synced_command },
5568  { "run_unsynced", &intf_do_unsynced },
5569  { "evaluate_single", &intf_synchronize_choice },
5570  { "evaluate_multiple", &intf_synchronize_choices },
5571  { nullptr, nullptr }
5572  };
5573  lua_getglobal(L, "wesnoth");
5574  lua_newtable(L);
5575  luaL_setfuncs(L, sync_callbacks, 0);
5576  lua_setfield(L, -2, "sync");
5577  lua_pop(L, 1);
5578 
5579  // Create the schedule module
5580  cmd_log_ << "Adding schedule module...\n";
5581  static luaL_Reg const schedule_callbacks[] {
5582  { "get_time_of_day", &dispatch<&game_lua_kernel::intf_get_time_of_day<false>>},
5583  { "get_illumination", &dispatch<&game_lua_kernel::intf_get_time_of_day<true>>},
5584  { "replace", &dispatch<&game_lua_kernel::intf_replace_schedule>},
5585  { nullptr, nullptr }
5586  };
5587  lua_getglobal(L, "wesnoth");
5588  lua_newtable(L);
5589  luaL_setfuncs(L, schedule_callbacks, 0);
5590  lua_createtable(L, 0, 2);
5591  lua_setmetatable(L, -2);
5592  lua_setfield(L, -2, "schedule");
5593  lua_pop(L, 1);
5594 
5595  // Create the playlist table with its metatable
5597 
5598  // Create the wml_actions table.
5599  cmd_log_ << "Adding wml_actions table...\n";
5600 
5601  lua_getglobal(L, "wesnoth");
5602  lua_newtable(L);
5603  lua_setfield(L, -2, "wml_actions");
5604  lua_pop(L, 1);
5605 
5606  // Create the wml_conditionals table.
5607  cmd_log_ << "Adding wml_conditionals table...\n";
5608 
5609  lua_getglobal(L, "wesnoth");
5610  lua_newtable(L);
5611  lua_setfield(L, -2, "wml_conditionals");
5612  lua_pop(L, 1);
5616 
5617  // Create the effects table.
5618  cmd_log_ << "Adding effects table...\n";
5619 
5620  lua_getglobal(L, "wesnoth");
5621  lua_newtable(L);
5622  lua_setfield(L, -2, "effects");
5623  lua_pop(L, 1);
5624 
5625  // Create the custom_synced_commands table.
5626  cmd_log_ << "Adding custom_synced_commands table...\n";
5627 
5628  lua_getglobal(L, "wesnoth");
5629  lua_newtable(L);
5630  lua_setfield(L, -2, "custom_synced_commands");
5631  lua_pop(L, 1);
5632 
5633  // Create the game_events table.
5634  cmd_log_ << "Adding game_events module...\n";
5635  static luaL_Reg const event_callbacks[] {
5636  { "add", &dispatch<&game_lua_kernel::intf_add_event> },
5637  { "add_repeating", &dispatch<&game_lua_kernel::intf_add_event_simple<false>> },
5638  { "add_menu", &dispatch<&game_lua_kernel::intf_add_event_simple<true>> },
5639  { "add_wml", &dispatch<&game_lua_kernel::intf_add_event_wml> },
5640  { "remove", &dispatch<&game_lua_kernel::intf_remove_event> },
5641  { "fire", &dispatch2<&game_lua_kernel::intf_fire_event, false> },
5642  { "fire_by_id", &dispatch2<&game_lua_kernel::intf_fire_event, true> },
5643  { "add_undo_actions", &dispatch<&game_lua_kernel::intf_add_undo_actions> },
5644  { "set_undoable", &dispatch<&game_lua_kernel::intf_allow_undo > },
5645  { nullptr, nullptr }
5646  };
5647  lua_getglobal(L, "wesnoth");
5648  lua_newtable(L);
5649  luaL_setfuncs(L, event_callbacks, 0);
5650  lua_createtable(L, 0, 2);
5651  lua_pushcfunction(L, &impl_game_events_dir);
5652  lua_setfield(L, -2, "__dir");
5653  lua_pushcfunction(L, &impl_game_events_get);
5654  lua_setfield(L, -2, "__index");
5655  lua_pushstring(L, "game_events");
5656  lua_setfield(L, -2, "__metatable");
5657  lua_setmetatable(L, -2);
5658  lua_setfield(L, -2, "game_events");
5659  lua_pop(L, 1);
5660 
5661  // Create the theme_items table.
5662  cmd_log_ << "Adding game_display table...\n";
5663 
5664  luaW_getglobal(L, "wesnoth", "interface");
5665  lua_newtable(L);
5666  lua_createtable(L, 0, 2);
5667  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_get>);
5668  lua_setfield(L, -2, "__index");
5669  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_set>);
5670  lua_setfield(L, -2, "__newindex");
5671  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_dir>);
5672  lua_setfield(L, -2, "__dir");
5673  lua_setmetatable(L, -2);
5674  lua_setfield(L, -2, "game_display");
5675  lua_pop(L, 1);
5676 
5677  // Create the scenario table.
5678  cmd_log_ << "Adding scenario table...\n";
5679 
5680  luaW_getglobal(L, "wesnoth");
5681  lua_newtable(L);
5682  lua_createtable(L, 0, 2);
5683  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_get>);
5684  lua_setfield(L, -2, "__index");
5685  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_set>);
5686  lua_setfield(L, -2, "__newindex");
5687  lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_scenario_dir>);
5688  lua_setfield(L, -2, "__dir");
5689  lua_setmetatable(L, -2);
5690  lua_setfield(L, -2, "scenario");
5691  lua_pop(L, 1);
5692 
5693  lua_settop(L, 0);
5694 
5695  for(const auto& handler : game_events::wml_action::registry())
5696  {
5697  set_wml_action(handler.first, handler.second);
5698  }
5699  luaW_getglobal(L, "wesnoth", "effects");
5700  for(const std::string& effect : unit::builtin_effects) {
5701  lua_pushstring(L, effect.c_str());
5703  lua_rawset(L, -3);
5704  }
5705  lua_settop(L, 0);
5706 
5707  // Set up the registry table for event handlers
5708  lua_newtable(L);
5709  EVENT_TABLE = luaL_ref(L, LUA_REGISTRYINDEX);
5710 }
5711 
5713 {
5714  lua_State *L = mState;
5715  assert(level_lua_.empty());
5717 
5718  //Create the races table.
5719  cmd_log_ << "Adding races table...\n";
5720 
5721  lua_settop(L, 0);
5722  lua_getglobal(L, "wesnoth");
5723  luaW_pushracetable(L);
5724  lua_setfield(L, -2, "races");
5725  lua_pop(L, 1);
5726 
5727  // Execute the preload scripts.
5728  cmd_log_ << "Running preload scripts...\n";
5729 
5731  for (const config &cfg : game_lua_kernel::preload_scripts) {
5732  run_lua_tag(cfg);
5733  }
5734  for (const config &cfg : level_lua_.child_range("lua")) {
5735  run_lua_tag(cfg);
5736  }
5737 }
5738 
5740  game_display_ = gd;
5741 }
5742 
5743 /**
5744  * These are the child tags of [scenario] (and the like) that are handled
5745  * elsewhere (in the C++ code).
5746  * Any child tags not in this list will be passed to Lua's on_load event.
5747  */
5748 static bool is_handled_file_tag(std::string_view s)
5749 {
5750  // Make sure this is sorted, since we binary_search!
5751  using namespace std::literals::string_view_literals;
5752  static constexpr std::array handled_file_tags {
5753  "color_palette"sv,
5754  "color_range"sv,
5755  "display"sv,
5756  "end_level_data"sv,
5757  "era"sv,
5758  "event"sv,
5759  "generator"sv,
5760  "label"sv,
5761  "lua"sv,
5762  "map"sv,
5763  "menu_item"sv,
5764  "modification"sv,
5765  "modify_unit_type"sv,
5766  "music"sv,
5767  "options"sv,
5768  "side"sv,
5769  "sound_source"sv,
5770  "story"sv,
5771  "terrain_graphics"sv,
5772  "time"sv,
5773  "time_area"sv,
5774  "tunnel"sv,
5775  "undo_stack"sv,
5776  "variables"sv
5777  };
5778 
5779  return std::binary_search(handled_file_tags.begin(), handled_file_tags.end(), s);
5780 }
5781 
5782 /**
5783  * Executes the game_events.on_load function and passes to it all the
5784  * scenario tags not yet handled.
5785  */
5787 {
5788  lua_State *L = mState;
5789 
5790  if(!impl_get_callback(L, "on_load"))
5791  return;
5792 
5793  lua_newtable(L);
5794  int k = 1;
5795  for(const auto [child_key, child_cfg] : level.all_children_view())
5796  {
5797  if (is_handled_file_tag(child_key)) continue;
5798  lua_createtable(L, 2, 0);
5799  lua_pushstring(L, child_key.c_str());
5800  lua_rawseti(L, -2, 1);
5801  luaW_pushconfig(L, child_cfg);
5802  lua_rawseti(L, -2, 2);
5803  lua_rawseti(L, -2, k++);
5804  }
5805 
5806  luaW_pcall(L, 1, 0, true);
5807 }
5808 
5809 /**
5810  * Executes the game_events.on_save function and adds to @a cfg the
5811  * returned tags. Also flushes the [lua] tags.
5812  */
5814 {
5815  lua_State *L = mState;
5816 
5817  if(!impl_get_callback<config>(L, "on_save"))
5818  return;
5819 
5820  if (!luaW_pcall(L, 0, 1, false))
5821  return;
5822 
5823  config v;
5824  luaW_toconfig(L, -1, v);
5825  lua_pop(L, 1);
5826 
5827  // Make a copy of the source tag names. Since splice is a destructive operation,
5828  // we can't guarantee that the view will remain valid during iteration.
5829  const auto temp = v.child_name_view();
5830  const std::vector<std::string> src_tags(temp.begin(), temp.end());
5831 
5832  for(const auto& key : src_tags) {
5833  if(is_handled_file_tag(key)) {
5834  /*
5835  * It seems the only tags appearing in the config v variable here
5836  * are the core-lua-handled (currently [item] and [objectives])
5837  * and the extra UMC ones.
5838  */
5839  const std::string m = "Tag is already used: [" + key + "]";
5840  log_error(m.c_str());
5841  continue;
5842  } else {
5843  cfg.splice_children(v, key);
5844  }
5845  }
5846 }
5847 
5848 /**
5849  * Executes the game_events.on_event function.
5850  * Returns false if there was no lua handler for this event
5851  */
5853 {
5854  lua_State *L = mState;
5855 
5856  if(!impl_get_callback(L, "on_event"))
5857  return false;
5858 
5859  queued_event_context dummy(&ev, queued_events_);
5860  lua_pushstring(L, ev.name.c_str());
5861  luaW_pcall(L, 1, 0, false);
5862  return true;
5863 }
5864 
5865 void game_lua_kernel::custom_command(const std::string& name, const config& cfg)
5866 {
5867  lua_State *L = mState;
5868 
5869  if (!luaW_getglobal(L, "wesnoth", "custom_synced_commands", name)) {
5870  return;
5871  }
5872  luaW_pushconfig(L, cfg);
5873  luaW_pcall(L, 1, 0, false);
5874 }
5875 
5876 /**
5877  * Applies its upvalue as an effect
5878  * Arg 1: The unit to apply to
5879  * Arg 3: The [effect] tag contents
5880  * Arg 3: If false, only build description
5881  * Return: The description of the effect
5882  */
5884 {
5885  std::string which_effect = lua_tostring(L, lua_upvalueindex(1));
5886  bool need_apply = luaW_toboolean(L, lua_upvalueindex(2));
5887  // Argument 1 is the implicit "self" argument, which isn't needed here
5888  lua_unit u(luaW_checkunit(L, 2));
5889  config cfg = luaW_checkconfig(L, 3);
5890 
5891  // The times= key is supposed to be ignored by the effect function.
5892  // However, just in case someone doesn't realize this, we will set it to 1 here.
5893  cfg["times"] = 1;
5894 
5895  if(need_apply) {
5896  u->apply_builtin_effect(which_effect, cfg);
5897  return 0;
5898  } else {
5899  std::string description = u->describe_builtin_effect(which_effect, cfg);
5900  lua_pushstring(L, description.c_str());
5901  return 1;
5902  }
5903 }
5904 
5905 /**
5906 * Registers a function for use as an effect handler.
5907 */
5909 {
5910  lua_State *L = mState;
5911 
5912  // The effect name is at the top of the stack
5913  int str_i = lua_gettop(L);
5914  lua_newtable(L); // The functor table
5915  lua_newtable(L); // The functor metatable
5916  lua_pushstring(L, "__call");
5917  lua_pushvalue(L, str_i);
5918  lua_pushboolean(L, true);
5919  lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
5920  lua_rawset(L, -3); // Set the call metafunction
5921  lua_pushstring(L, "__descr");
5922  lua_pushvalue(L, str_i);
5923  lua_pushboolean(L, false);
5924  lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
5925  lua_rawset(L, -3); // Set the descr "metafunction"
5926  lua_setmetatable(L, -2); // Apply the metatable to the functor table
5927 }
5928 
5929 
5930 /**
5931  * Executes its upvalue as a wml action.
5932  */
5934 {
5936  (lua_touserdata(L, lua_upvalueindex(1)));
5937 
5938  vconfig vcfg = luaW_checkvconfig(L, 1);
5939  h(get_event_info(), vcfg);
5940  return 0;
5941 }
5942 
5943 /**
5944  * Registers a function for use as an action handler.
5945  */
5947 {
5948  lua_State *L = mState;
5949 
5950  lua_getglobal(L, "wesnoth");
5951  lua_pushstring(L, "wml_actions");
5952  lua_rawget(L, -2);
5953  lua_pushstring(L, cmd.c_str());
5954  lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
5955  lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_wml_action>, 1);
5956  lua_rawset(L, -3);
5957  lua_pop(L, 2);
5958 }
5959 
5960 using wml_conditional_handler = bool(*)(const vconfig&);
5961 
5962 /**
5963  * Executes its upvalue as a wml condition and returns the result.
5964  */
5965 static int cfun_wml_condition(lua_State *L)
5966 {
5968  (lua_touserdata(L, lua_upvalueindex(1)));
5969 
5970  vconfig vcfg = luaW_checkvconfig(L, 1);
5971  lua_pushboolean(L, h(vcfg));
5972  return 1;
5973 }
5974 
5975 /**
5976  * Registers a function for use as a conditional handler.
5977  */
5979 {
5980  lua_State *L = mState;
5981 
5982  lua_getglobal(L, "wesnoth");
5983  lua_pushstring(L, "wml_conditionals");
5984  lua_rawget(L, -2);
5985  lua_pushstring(L, cmd.c_str());
5986  lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
5987  lua_pushcclosure(L, &cfun_wml_condition, 1);
5988  lua_rawset(L, -3);
5989  lua_pop(L, 2);
5990 }
5991 
5992 /**
5993  * Runs a command from an event handler.
5994  * @return true if there is a handler for the command.
5995  * @note @a cfg should be either volatile or long-lived since the Lua
5996  * code may grab it for an arbitrary long time.
5997  */
5998 bool game_lua_kernel::run_wml_action(const std::string& cmd, const vconfig& cfg,
5999  const game_events::queued_event& ev)
6000 {
6001  lua_State *L = mState;
6002 
6003 
6004  if (!luaW_getglobal(L, "wesnoth", "wml_actions", cmd))
6005  return false;
6006 
6007  queued_event_context dummy(&ev, queued_events_);
6008  luaW_pushvconfig(L, cfg);
6009  luaW_pcall(L, 1, 0, true);
6010  return true;
6011 }
6012 
6013 
6014 /**
6015  * Evaluates a WML conidition.
6016  *
6017  * @returns Whether the condition passed.
6018  * @note @a cfg should be either volatile or long-lived since the Lua
6019  * code may grab it for an arbitrarily long time.
6020  */
6021 bool game_lua_kernel::run_wml_conditional(const std::string& cmd, const vconfig& cfg)
6022 {
6023  lua_State* L = mState;
6024 
6025  // If an invalid coniditional tag is used, consider it a pass.
6026  if(!luaW_getglobal(L, "wesnoth", "wml_conditionals", cmd)) {
6027  lg::log_to_chat() << "unknown conditional wml: [" << cmd << "]\n";
6028  ERR_WML << "unknown conditional wml: [" << cmd << "]";
6029  return true;
6030  }
6031 
6032  luaW_pushvconfig(L, cfg);
6033 
6034  // Any runtime error is considered a fail.
6035  if(!luaW_pcall(L, 1, 1, true)) {
6036  return false;
6037  }
6038 
6039  bool b = luaW_toboolean(L, -1);
6040 
6041  lua_pop(L, 1);
6042  return b;
6043 }
6044 
6045 static int intf_run_event_wml(lua_State* L)
6046 {
6047  int argIdx = lua_gettop(L);
6048  if(!luaW_getglobal(L, "wesnoth", "wml_actions", "command")) {
6049  return luaL_error(L, "wesnoth.wml_actions.command is missing");
6050  }
6051  lua_pushvalue(L, argIdx);
6052  lua_call(L, 1, 0);
6053  return 0;
6054 }
6055 
6057 {
6058  lua_State* L = mState;
6059  lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
6060  int evtIdx = lua_gettop(L);
6061  ON_SCOPE_EXIT(L) {
6062  lua_pop(L, 1);
6063  };
6064  lua_pushcfunction(L, intf_run_event_wml);
6065  return luaL_ref(L, evtIdx);
6066 }
6067 
6068 int game_lua_kernel::save_wml_event(const std::string& name, const std::string& id, const std::string& code)
6069 {
6070  lua_State* L = mState;
6071  lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
6072  int evtIdx = lua_gettop(L);
6073  ON_SCOPE_EXIT(L) {
6074  lua_pop(L, 1);
6075  };
6076  std::ostringstream lua_name;
6077  lua_name << "event ";
6078  if(name.empty()) {
6079  lua_name << "<anon>";
6080  } else {
6081  lua_name << name;
6082  }
6083  if(!id.empty()) {
6084  lua_name << "[id=" << id << "]";
6085  }
6086  if(!load_string(code.c_str(), lua_name.str())) {
6087  ERR_LUA << "Failed to register WML event: " << lua_name.str();
6088  return LUA_NOREF;
6089  }
6090  return luaL_ref(L, evtIdx);
6091 }
6092 
6094 {
6095  lua_State* L = mState;
6096  idx = lua_absindex(L, idx);
6097  lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
6098  int evtIdx = lua_gettop(L);
6099  ON_SCOPE_EXIT(L) {
6100  lua_pop(L, 1);
6101  };
6102  lua_pushvalue(L, idx);
6103  return luaL_ref(L, evtIdx);
6104 }
6105 
6107 {
6108  lua_State* L = mState;
6109  lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
6110  luaL_unref(L, -1, ref);
6111  lua_pop(L, 1);
6112 }
6113 
6114 bool game_lua_kernel::run_wml_event(int ref, const vconfig& args, const game_events::queued_event& ev, bool* out)
6115 {
6116  lua_State* L = mState;
6117  lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
6118  ON_SCOPE_EXIT(L) {
6119  lua_pop(L, 1);
6120  };
6121  lua_geti(L, -1, ref);
6122  if(lua_isnil(L, -1)) return false;
6123  luaW_pushvconfig(L, args);
6124  queued_event_context dummy(&ev, queued_events_);
6125  if(luaW_pcall(L, 1, out ? 1 : 0, true)) {
6126  if(out) {
6127  *out = luaW_toboolean(L, -1);
6128  lua_pop(L, 1);
6129  }
6130  return true;
6131  }
6132  return false;
6133 }
6134 
6135 
6136 /**
6137 * Runs a script from a location filter.
6138 * The script is an already compiled function given by its name.
6139 */
6140 bool game_lua_kernel::run_filter(char const *name, const map_location& l)
6141 {
6142  lua_pushinteger(mState, l.wml_x());
6143  lua_pushinteger(mState, l.wml_y());
6144  return run_filter(name, 2);
6145 }
6146 
6147 /**
6148 * Runs a script from a location filter.
6149 * The script is an already compiled function given by its name.
6150 */
6151 bool game_lua_kernel::run_filter(char const *name, const team& t)
6152 {
6153  //TODO: instead of passing the lua team object we coudl also jsut pass its
6154  // number. then we wouldn't need this const cast.
6155  luaW_pushteam(mState, const_cast<team&>(t));
6156  return run_filter(name, 1);
6157 }
6158 /**
6159 * Runs a script from a unit filter.
6160 * The script is an already compiled function given by its name.
6161 */
6162 bool game_lua_kernel::run_filter(char const *name, const unit& u)
6163 {
6164  lua_State *L = mState;
6165  lua_unit* lu = luaW_pushlocalunit(L, const_cast<unit&>(u));
6166  // stack: unit
6167  // put the unit to the stack twice to prevent gc.
6168  lua_pushvalue(L, -1);
6169  // stack: unit, unit
6170  bool res = run_filter(name, 1);
6171  // stack: unit
6172  lu->clear_ref();
6173  lua_pop(L, 1);
6174  return res;
6175 }
6176 /**
6177 * Runs a script from a filter.
6178 * The script is an already compiled function given by its name.
6179 */
6180 bool game_lua_kernel::run_filter(char const *name, int nArgs)
6181 {
6182  auto ml = map_locker(this);
6183  lua_State *L = mState;
6184  // Get the user filter by name.
6185  const std::vector<std::string>& path = utils::split(name, '.', utils::STRIP_SPACES);
6186  if (!luaW_getglobal(L, path))
6187  {
6188  std::string message = std::string() + "function " + name + " not found";
6189  log_error(message.c_str(), "Lua SUF Error");
6190  //we pushed nothing and can safeley return.
6191  return false;
6192  }
6193  lua_insert(L, -nArgs - 1);
6194 
6195  if (!luaW_pcall(L, nArgs, 1)) return false;
6196 
6197  bool b = luaW_toboolean(L, -1);
6198  lua_pop(L, 1);
6199  return b;
6200 }
6201 
6202 std::string game_lua_kernel::apply_effect(const std::string& name, unit& u, const config& cfg, bool need_apply)
6203 {
6204  lua_State *L = mState;
6205  int top = lua_gettop(L);
6206  std::string descr;
6207  // Stack: nothing
6208  lua_unit* lu = luaW_pushlocalunit(L, u);
6209  // Stack: unit
6210  // (Note: The unit needs to be on the stack twice to prevent untimely GC.)
6211  luaW_pushconfig(L, cfg);
6212  // Stack: unit, cfg
6213  if(luaW_getglobal(L, "wesnoth", "effects", name)) {
6214  auto ml = map_locker(this);
6215  // Stack: unit, cfg, effect
6216  if(lua_istable(L, -1)) {
6217  // Effect is implemented by a table with __call and __descr
6218  if(need_apply) {
6219  lua_pushvalue(L, -1);
6220  // Stack: unit, cfg, effect, effect
6221  lua_pushvalue(L, top + 1);
6222  // Stack: unit, cfg, effect, effect, unit
6223  lua_pushvalue(L, top + 2);
6224  // Stack: unit, cfg, effect, effect, unit, cfg
6225  luaW_pcall(L, 2, 0);
6226  // Stack: unit, cfg, effect
6227  }
6228  if(luaL_getmetafield(L, -1, "__descr")) {
6229  // Stack: unit, cfg, effect, __descr
6230  if(lua_isstring(L, -1)) {
6231  // __descr was a static string
6232  descr = lua_tostring(L, -1);
6233  } else {
6234  lua_pushvalue(L, -2);
6235  // Stack: unit, cfg, effect, __descr, effect
6236  lua_pushvalue(L, top + 1);
6237  // Stack: unit, cfg, effect, __descr, effect, unit
6238  lua_pushvalue(L, top + 2);
6239  // Stack: unit, cfg, effect, __descr, effect, unit, cfg
6240  luaW_pcall(L, 3, 1);
6241  if(lua_isstring(L, -1) && !lua_isnumber(L, -1)) {
6242  descr = lua_tostring(L, -1);
6243  } else {
6244  ERR_LUA << "Effect __descr metafunction should have returned a string, but instead returned ";
6245  if(lua_isnone(L, -1)) {
6246  ERR_LUA << "nothing";
6247  } else {
6248  ERR_LUA << lua_typename(L, lua_type(L, -1));
6249  }
6250  }
6251  }
6252  }
6253  } else if(need_apply) {
6254  // Effect is assumed to be a simple function; no description is provided
6255  lua_pushvalue(L, top + 1);
6256  // Stack: unit, cfg, effect, unit
6257  lua_pushvalue(L, top + 2);
6258  // Stack: unit, cfg, effect, unit, cfg
6259  luaW_pcall(L, 2, 0);
6260  // Stack: unit, cfg
6261  }
6262  }
6263  lua_settop(L, top);
6264  lu->clear_ref();
6265  return descr;
6266 }
6267 
6269 {
6270  return ai::lua_ai_context::create(mState,code,engine);
6271 }
6272 
6274 {
6275  return ai::lua_ai_action_handler::create(mState,code,context);
6276 }
6277 
6279 {
6280  lua_State *L = mState;
6281 
6282  if(!impl_get_callback(L, "on_mouse_move")) {
6283  return;
6284  }
6285  lua_push(L, loc.wml_x());
6286  lua_push(L, loc.wml_y());
6287  luaW_pcall(L, 2, 0, false);
6288  return;
6289 }
6290 
6291 bool game_lua_kernel::mouse_button_callback(const map_location& loc, const std::string &button, const std::string &event)
6292 {
6293  lua_State *L = mState;
6294 
6295  if(!impl_get_callback<bool>(L, "on_mouse_button")) {
6296  return false;
6297  }
6298 
6299  lua_push(L, loc.wml_x());
6300  lua_push(L, loc.wml_y());
6301  lua_push(L, button);
6302  lua_push(L, event);
6303 
6304  if (!luaW_pcall(L, 4, 1)) return false;
6305  bool result = luaW_toboolean(L, -1);
6306  lua_pop(L, 1);
6307  return result;
6308 }
6309 
6311 {
6312  lua_State *L = mState;
6313 
6314  if(!impl_get_callback(L, "on_mouse_action")) {
6315  return;
6316  }
6317  lua_push(L, loc.wml_x());
6318  lua_push(L, loc.wml_y());
6319  luaW_pcall(L, 2, 0, false);
6320  return;
6321 }
Various functions that implement attacks and attack calculations.
map_location loc
Definition: move.cpp:172
Various functions related to moving units.
void advance_unit_at(const advance_unit_params &params)
Various functions that implement advancements of units.
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
GAME_CONFIG_SETTER("debug", bool, application_lua_kernel)
double t
Definition: astarsearch.cpp:63
#define debug(x)
Class to encapsulate fog/shroud clearing and the resultant sighted events.
Definition: vision.hpp:72
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:490
game_events::pump_result_t fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:541
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,...
Definition: vision.cpp:330
virtual ai_context & get_ai_context()
unwrap
Definition: ai.cpp:193
virtual double evaluate()=0
Evaluate the candidate action, resetting the internal state of the action.
virtual void execute()=0
Execute the candidate action.
static bool add_component(component *root, const std::string &path, const config &cfg)
Definition: component.cpp:172
static void expand_simplified_aspects(side_number side, config &cfg)
Expand simplified aspects, similar to the change from 1.7.2 to 1.7.3 but with some additional syntax ...
static const config & get_default_ai_parameters()
get default AI parameters
virtual config to_config() const
Serialize to config.
Definition: engine_lua.cpp:391
virtual void push_ai_table()
Method that pushes the AI table of the lua_context on the stack for debugging purposes.
Definition: engine_lua.cpp:297
component * get_component(component *root, const std::string &path)
Definition: manager.cpp:310
Proxy class for calling AI action handlers defined in Lua.
Definition: core.hpp:71
static lua_ai_action_handler * create(lua_State *L, char const *code, lua_ai_context &context)
Definition: core.cpp:1020
Proxy table for the AI context.
Definition: core.hpp:34
static void init(lua_State *L)
Definition: core.cpp:53
static lua_ai_context * create(lua_State *L, char const *code, engine_lua *engine)
Definition: core.cpp:978
bool add_ai_for_side_from_config(side_number side, const config &cfg, bool replace=true)
Adds active AI for specified side from cfg.
Definition: manager.cpp:632
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:677
void raise_user_interact()
Notifies all observers of 'ai_user_interact' event.
Definition: manager.cpp:424
static manager & get_singleton()
Definition: manager.hpp:140
void modify_active_ai_for_side(ai::side_number side, const config &cfg)
Modifies AI parameters for active AI of the given side.
Definition: manager.cpp:672
ai::holder & get_active_ai_holder_for_side_dbg(side_number side)
Gets the active AI holder for debug purposes.
Definition: manager.cpp:697
bool play_stage()
Play the turn - strategy.
Definition: stage.cpp:55
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:167
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition: attack.hpp:199
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:441
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition: attack.hpp:193
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition: attack.cpp:448
Variant for storing WML attributes.
std::string str(const std::string &fallback="") const
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:362
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:312
void remove_children(config_key_type key, const std::function< bool(const config &)> &p={})
Removes all children with tag key for which p returns true.
Definition: config.cpp:650
child_itors child_range(config_key_type key)
Definition: config.cpp:268
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:401
auto child_name_view() const
A non-owning view over all child tag names.
Definition: config.hpp:904
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:167
bool empty() const
Definition: config.cpp:845
void splice_children(config &src, config_key_type key)
Moves all the children with tag key from src to this.
Definition: config.cpp:577
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:380
config & add_child(config_key_type key)
Definition: config.cpp:436
virtual void play_slice()
void add_chat_message(const std::time_t &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it,...
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:96
const team & viewing_team() const
Definition: display.cpp:335
void remove_overlay(const map_location &loc)
remove_overlay will remove all overlays on a tile.
Definition: display.cpp:131
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.cpp:1474
void remove_single_overlay(const map_location &loc, const std::string &toDelete)
remove_single_overlay will remove a single overlay from a tile
Definition: display.cpp:136
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:2979
double turbo_speed() const
Definition: display.cpp:2023
@ ONSCREEN
Definition: display.hpp:504
@ ONSCREEN_WARP
Definition: display.hpp:504
@ SCROLL
Definition: display.hpp:504
void adjust_color_overlay(int r, int g, int b)
Add r,g,b to the colors for all images displayed on the map.
Definition: display.cpp:398
static double get_zoom_factor()
Returns the current zoom factor.
Definition: display.hpp:269
map_labels & labels()
Definition: display.cpp:2444
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:1873
bool view_locked() const
Definition: display.hpp:499
bool set_zoom(bool increase)
Zooms the display in (true) or out (false).
Definition: display.cpp:1701
tod_color get_color_overlay() const
Definition: display.hpp:215
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:2972
rect map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.cpp:514
void fade_to(const color_t &color, const std::chrono::milliseconds &duration)
Screen fade.
Definition: display.cpp:2120
void add_overlay(const map_location &loc, overlay &&ov)
Functions to add and remove overlays from locations.
Definition: display.cpp:118
const map_location & selected_hex() const
Definition: display.hpp:308
bool show_everything() const
Definition: display.hpp:112
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:110
void reinit_flags_for_team(const team &)
Rebuild the flag list (not team colors) for a single side.
Definition: display.cpp:263
const map_location & mouseover_hex() const
Definition: display.hpp:309
void set_view_locked(bool value)
Sets whether the map view is locked (e.g.
Definition: display.hpp:502
bool scroll(const point &amount, bool force=false)
Scrolls the display by amount pixels.
Definition: display.cpp:1598
void select_hex(const map_location &hex, const bool browse, const bool highlight=true, const bool fire_event=true, const bool force_unhighlight=false)
void set_position(double xpos, double ypos)
void set_alignment(ALIGN align)
void set_lifetime(const std::chrono::milliseconds &lifetime, const std::chrono::milliseconds &fadeout=std::chrono::milliseconds{100})
void set_color(const color_t &color)
void set_clip_rect(const SDL_Rect &r)
void set_bg_color(const color_t &bg_color)
void set_font_size(int font_size)
std::ostringstream wrapper.
Definition: formatter.hpp:40
Game board class.
Definition: game_board.hpp:47
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
team & get_team(int i)
Definition: game_board.hpp:92
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:185
virtual const unit_map & units() const override
Definition: game_board.hpp:107
virtual const gamemap & map() const override
Definition: game_board.hpp:97
static game_config_manager * get()
const game_config_view & game_config() const
A class grating read only view to a vector of config objects, viewed as one config with all children ...
const config & find_mandatory_child(config_key_type key, const std::string &name, const std::string &value) const
void clear_variable(const std::string &varname)
Clears attributes config children does nothing if varname is no valid variable name.
Definition: game_data.cpp:118
@ PRELOAD
the preload [event] is fired next phase: PRESTART (normal game), TURN_STARTING_WAITING (reloaded game...
Definition: game_data.hpp:76
@ INITIAL
creating intitial [unit]s, executing toplevel [lua] etc.
Definition: game_data.hpp:73
@ PRESTART
the prestart [event] is fired next phase: START (default), GAME_ENDING
Definition: game_data.hpp:79
@ TURN_PLAYING
The User is controlling the game and invoking actions The game can be saved here.
Definition: game_data.hpp:93
const std::string & get_theme() const
Definition: game_data.hpp:136
variable_access_create get_variable_access_write(const std::string &varname)
returns a variable_access that can be used to change the game variables
Definition: game_data.hpp:51
void set_theme(const std::string &value)
Definition: game_data.hpp:137
void set_allow_end_turn(bool value, const t_string &reason="")
Definition: game_data.hpp:120
variable_access_const get_variable_access_read(const std::string &varname) const
returns a variable_access that cannot be used to change the game variables
Definition: game_data.hpp:45
void display_unit_hex(map_location hex)
Change the unit to be displayed in the sidebar.
void invalidate_unit_after_move(const map_location &src, const map_location &dst)
Same as invalidate_unit() if moving the displayed unit.
virtual void highlight_hex(map_location hex) override
Function to highlight a location.
virtual const map_location & displayed_unit_hex() const override
Virtual functions shadowed in game_display.
void new_turn()
Update lighting settings.
bool maybe_rebuild()
Rebuilds the screen if needs_rebuild(true) was previously called, and resets the flag.
display_chat_manager & get_chat_manager()
void float_label(const map_location &loc, const std::string &text, const color_t &color)
Function to float a label above a tile.
void set_arguments(const config &cfg)
Definition: handlers.hpp:127
The game event manager loads the scenario configuration object, and ensures that events are handled a...
Definition: manager.hpp:45
void add_event_handler_from_wml(const config &handler, game_lua_kernel &lk, bool is_menu_item=false)
Create an event handler from an [event] tag.
Definition: manager.cpp:66
pending_event_handler add_event_handler_from_lua(const std::string &name, const std::string &id, bool repeat=false, double priority=0., bool is_menu_item=false)
Create an empty event handler.
Definition: manager.cpp:106
bool fire_item(const std::string &id, const map_location &hex, game_data &gamedata, filter_context &fc, unit_map &units, bool is_key_hold_repeat=false) const
Fires the menu item with the given id.
Definition: wmi_manager.cpp:79
bool erase(const std::string &id)
Erases the item with the provided id.
Definition: wmi_manager.cpp:53
void set_item(const std::string &id, const vconfig &menu_item)
Updates or creates (as appropriate) the menu item with the given id.
void(* handler)(const queued_event &, const vconfig &)
Definition: action_wml.hpp:44
static const map & registry()
Definition: action_wml.hpp:57
void set_action_canceled()
Sets whether or not wml wants to abort the currently executed user action.
Definition: pump.cpp:363
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:399
void set_undo_disabled(bool mutated)
[allow_undo] implementation
Definition: pump.cpp:351
int intf_get_mouseover_tile(lua_State *L)
Returns the currently overed tile.
void custom_command(const std::string &, const config &)
int intf_put_unit(lua_State *L)
Places a unit on the map.
int intf_get_label(lua_State *L)
int intf_cancel_action(lua_State *)
int intf_set_floating_label(lua_State *L, bool spawn)
Arg 1: text - string Arg 2: options table.
int intf_replace_schedule(lua_State *l)
Replacing the current time of day schedule.
int intf_select_unit(lua_State *L)
Selects and highlights the given location on the map.
int intf_deselect_hex(lua_State *L)
Deselects any highlighted hex on the map.
std::stack< game_events::queued_event const * > queued_events_
int intf_get_all_vars(lua_State *L)
Gets all the WML variables currently set.
int intf_remove_tile_overlay(lua_State *L)
Removes an overlay from a tile.
int intf_screen_fade(lua_State *L)
int intf_erase_unit(lua_State *L)
Erases a unit from the map.
int intf_get_fog_or_shroud(lua_State *L, bool fog)
int intf_override_shroud(lua_State *L)
Overrides the shroud entirely.
int intf_get_variable(lua_State *L)
Gets a WML variable.
int intf_scroll(lua_State *L)
game_lua_kernel(game_state &, play_controller &, reports &)
int intf_set_sub_achievement(lua_State *L)
Marks a single sub-achievement as completed.
int intf_lock_view(lua_State *L)
Sets whether gamemap scrolling is disabled for the user.
void put_unit_helper(const map_location &loc)
int intf_toggle_shroud(lua_State *L, bool place_shroud)
Toggle shroud on some locations Arg 1: Side number Arg 2: List of locations on which to place/remove ...
int intf_match_side(lua_State *L)
Matches a side against the given filter.
int intf_get_selected_tile(lua_State *L)
Returns the currently selected tile.
int cfun_wml_action(lua_State *L)
Executes its upvalue as a wml action.
virtual void log_error(char const *msg, char const *context="Lua error") override
Error reporting mechanisms, used by virtual methods protected_call and load_string.
int cfun_builtin_effect(lua_State *L)
Applies its upvalue as an effect Arg 1: The unit to apply to Arg 3: The [effect] tag contents Arg 3: ...
int impl_theme_item(lua_State *L, const std::string &name)
Executes its upvalue as a theme item generator.
ai::lua_ai_context * create_lua_ai_context(char const *code, ai::engine_lua *engine)
int intf_has_achievement(lua_State *L)
Returns whether an achievement has been completed.
int intf_gamestate_inspector(lua_State *)
int intf_float_label(lua_State *L)
Floats some text on the map.
tod_manager & tod_man()
int intf_find_reach(lua_State *L)
Finds all the locations reachable by a unit.
bool run_event(const game_events::queued_event &)
Executes the game_events.on_event function.
int intf_end_turn(lua_State *)
int save_wml_event()
Store a WML event in the Lua registry, as a function.
int impl_scenario_dir(lua_State *L)
Get a list of scenario data (__dir metamethod).
int intf_set_achievement(lua_State *L)
Sets an achievement as being completed.
int intf_get_units(lua_State *)
Gets all the units matching a given filter.
int intf_color_adjust(lua_State *L)
int intf_redraw(lua_State *L)
int intf_set_village_owner(lua_State *L)
Sets the owner of a village.
int impl_current_dir(lua_State *L)
Gets a list of date about current point of game (__dir metamethod).
static config preload_config
int intf_find_cost_map(lua_State *L)
Is called with one or more units and builds a cost map.
int intf_get_time_area(lua_State *)
int intf_highlight_hex(lua_State *L)
Highlights the given location on the map.
bool run_wml_event(int ref, const vconfig &args, const game_events::queued_event &ev, bool *out=nullptr)
Run a WML stored in the Lua registry.
int intf_scroll_to_tile(lua_State *L)
Scrolls to given tile.
int intf_get_side(lua_State *L)
int intf_remove_event(lua_State *L)
int intf_is_enemy(lua_State *L)
Returns whether the first side is an enemy of the second one.
static void extract_preload_scripts(const game_config_view &game_config)
int impl_get_terrain_info(lua_State *L)
Gets details about a terrain.
virtual std::string my_name() override
User-visible name of the lua kernel that they are talking to.
int intf_remove_label(lua_State *L)
bool run_wml_action(const std::string &, const vconfig &, const game_events::queued_event &)
Runs a command from an event handler.
int intf_move_floating_label(lua_State *L)
int intf_match_unit(lua_State *L)
Matches a unit against the given filter.
int intf_add_time_area(lua_State *)
Adding new time_areas dynamically with Standard Location Filters.
void clear_wml_event(int ref)
Clear a WML event store in the Lua registry.
int intf_create_animator(lua_State *)
int impl_theme_items_dir(lua_State *L)
Get all available theme_items (__dir metamethod).
int intf_add_event_wml(lua_State *L)
Add a new event handler Arg: A full event specification as a WML config.
int impl_scenario_set(lua_State *L)
Sets some scenario data (__newindex metamethod).
int intf_message(lua_State *L)
Displays a message in the chat window and in the logs.
int intf_set_side_id(lua_State *L)
int intf_view_locked(lua_State *L)
Gets whether gamemap scrolling is disabled for the user.
int intf_get_displayed_unit(lua_State *)
Gets the unit displayed in the sidebar.
bool run_wml_conditional(const std::string &, const vconfig &)
Evaluates a WML conidition.
int intf_get_recall_units(lua_State *L)
Gets the numeric ids of all the units matching a given filter on the recall lists.
int intf_add_label(lua_State *L)
int intf_zoom(lua_State *L)
int intf_log_replay(lua_State *L)
const game_events::queued_event & get_event_info()
int intf_unit_ability(lua_State *L)
Returns true if the unit has the given ability enabled.
void push_builtin_effect()
Registers a function for use as an effect handler.
int impl_run_animation(lua_State *)
std::string apply_effect(const std::string &name, unit &u, const config &cfg, bool need_apply)
game_board & board()
int intf_fire_wml_menu_item(lua_State *L)
Fires a wml menu item.
int intf_clear_menu_item(lua_State *L)
void mouse_over_hex_callback(const map_location &loc)
int impl_theme_items_get(lua_State *L)
Creates a field of the theme_items table and returns it (__index metamethod).
int intf_find_vacant_tile(lua_State *L)
Finds a vacant tile.
play_controller & play_controller_
int impl_schedule_dir(lua_State *L)
const gamemap & map() const
int map_locked_
A value != 0 means that the shouldn't remove any units from the map, usually because we are currently...
static std::vector< config > preload_scripts
int intf_get_unit(lua_State *)
Gets the unit at the given location or with the given id.
int impl_current_get(lua_State *L)
Gets some data about current point of game (__index metamethod).
int impl_schedule_set(lua_State *L)
void select_hex_callback(const map_location &loc)
void set_wml_condition(const std::string &, bool(*)(const vconfig &))
Registers a function for use as a conditional handler.
game_state & game_state_
int impl_end_level_data_set(lua_State *)
std::string synced_state()
converts synced_context::get_synced_state() to a string.
int intf_find_path(lua_State *L)
Finds a path between two locations.
int intf_remove_floating_label(lua_State *L)
void set_wml_action(const std::string &, game_events::wml_action::handler)
Registers a function for use as an action handler.
int intf_put_recall_unit(lua_State *L)
Puts a unit on a recall list.
int intf_teleport(lua_State *L)
Teeleports a unit to a location.
void luaW_push_schedule(lua_State *L, int area_index)
int intf_add_tile_overlay(lua_State *L)
Adds an overlay on a tile.
int intf_play_sound(lua_State *L)
Plays a sound, possibly repeated.
void lua_chat(const std::string &caption, const std::string &msg)
int intf_allow_end_turn(lua_State *)
Allow undo sets the flag saying whether the event has mutated the game to false.
int intf_get_village_owner(lua_State *L)
Gets the side of a village owner.
int intf_get_sides(lua_State *L)
Returns a proxy table array for all sides matching the given SSF.
int intf_has_sub_achievement(lua_State *L)
Returns whether an achievement has been completed.
int intf_create_side(lua_State *L)
void initialize(const config &level)
int intf_add_undo_actions(lua_State *L)
Add undo actions for the current active event Arg 1: Either a table of ActionWML or a function to cal...
void save_game(config &level)
Executes the game_events.on_save function and adds to cfg the returned tags.
int impl_scenario_get(lua_State *L)
Gets some scenario data (__index metamethod).
int intf_remove_time_area(lua_State *)
Removing new time_areas dynamically with Standard Location Filters.
int impl_schedule_len(lua_State *L)
int intf_get_locations(lua_State *L)
Gets all the locations matching a given filter.
int intf_add_event(lua_State *L)
Add a new event handler Arg 1: Table of options.
int intf_simulate_combat(lua_State *L)
Simulates a combat between two units.
int impl_get_terrain_list(lua_State *L)
Gets a list of known terrain codes.
std::vector< team > & teams()
int intf_progress_achievement(lua_State *L)
Progresses the provided achievement.
int intf_match_location(lua_State *L)
Matches a location against the given filter.
int intf_toggle_fog(lua_State *L, const bool clear)
Implements the lifting and resetting of fog via WML.
int intf_extract_unit(lua_State *L)
Extracts a unit from the map or a recall list and gives it to Lua.
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...
int intf_clear_messages(lua_State *)
Removes all messages from the chat window.
int intf_allow_undo(lua_State *)
Allow undo sets the flag saying whether the event has mutated the game to false.
int impl_schedule_get(lua_State *L)
int intf_set_variable(lua_State *L)
Sets a WML variable.
int impl_theme_items_set(lua_State *L)
Sets a field of the theme_items table (__newindex metamethod).
void set_game_display(game_display *gd)
int intf_set_menu_item(lua_State *L)
ai::lua_ai_action_handler * create_lua_ai_action_handler(char const *code, ai::lua_ai_context &context)
bool mouse_button_callback(const map_location &loc, const std::string &button, const std::string &event)
int intf_get_achievement(lua_State *L)
Returns information on a single achievement, or no data if the achievement is not found.
std::vector< int > get_sides_vector(const vconfig &cfg)
Gets a vector of sides from side= attribute in a given config node.
int intf_fire_event(lua_State *L, const bool by_id)
Fires an event.
int intf_delay(lua_State *L)
Delays engine for a while.
int intf_add_event_simple(lua_State *L)
Add a new event handler Arg 1: Event to handle, as a string or list of strings; or menu item ID if th...
bool run_filter(char const *name, const unit &u)
Runs a script from a unit filter.
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).
int intf_skip_messages(lua_State *L)
Set whether to skip messages Arg 1 (optional) - boolean.
game_display * game_display_
int cfun_undoable_event(lua_State *L)
Upvalue 1: The event function Upvalue 2: The undo function Arg 1: The event content.
void load_game(const config &level)
Executes the game_events.on_load function and passes to it all the scenario tags not yet handled.
int intf_get_time_of_day(lua_State *L)
Gets time of day information.
game_data & gamedata()
int intf_get_color_adjust(lua_State *L)
int intf_is_skipping_messages(lua_State *L)
Return true if a replay is in progress but the player has chosen to skip it.
void add_side_wml(config cfg)
creates a new side during a game.
Definition: game_state.cpp:430
int next_player_number_
Definition: game_state.hpp:58
const std::unique_ptr< actions::undo_list > undo_stack_
undo_stack_ is never nullptr.
Definition: game_state.hpp:56
game_board board_
Definition: game_state.hpp:44
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:50
game_events::wmi_manager & get_wml_menu_items()
Definition: game_state.cpp:383
tod_manager tod_manager_
Definition: game_state.hpp:45
game_data gamedata_
Definition: game_state.hpp:43
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
Encapsulates the map of the game.
Definition: map.hpp:172
const std::shared_ptr< terrain_type_data > & tdata() const
Definition: map.hpp:204
virtual void log_error(char const *msg, char const *context="Lua error")
Error reporting mechanisms, used by virtual methods protected_call and load_string.
command_log cmd_log_
lua_State * mState
bool load_string(const std::string &prog, const std::string &name, const error_handler &, bool allow_unsafe=false)
void run_lua_tag(const config &cfg)
Runs a [lua] tag.
Storage for a unit, either owned by the Lua code (ptr != 0), a local variable unit (c_ptr !...
Definition: lua_unit.hpp:81
bool on_map() const
Definition: lua_unit.hpp:100
bool put_map(const map_location &loc)
Definition: lua_unit.cpp:74
int on_recall_list() const
Definition: lua_unit.hpp:102
unit_ptr get_shared() const
Definition: lua_unit.cpp:59
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const color_t color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition: label.cpp:147
const terrain_label * get_label(const map_location &loc, const std::string &team_name) const
Definition: label.hpp:48
void recalculate_shroud()
Definition: label.cpp:278
void clear_all()
Definition: label.cpp:240
game_classification & get_classification()
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
bool is_skipping_story() const
game_state & gamestate()
bool is_skipping_replay() const
std::shared_ptr< wb::manager > get_whiteboard() const
bool is_replay() const
virtual void force_end_turn()=0
game_events::wml_event_pump & pump()
std::set< std::string > & encountered_units()
static prefs & get()
int progress_achievement(const std::string &content_for, const std::string &id, int limit=999999, int max_progress=999999, int amount=0)
Increments the achievement's current progress by amount if it hasn't already been completed.
void set_achievement(const std::string &content_for, const std::string &id)
Marks the specified achievement as completed.
void set_sub_achievement(const std::string &content_for, const std::string &id, const std::string &sub_id)
Marks the specified sub-achievement as completed.
void add_log_data(const std::string &key, const std::string &var)
Definition: replay.cpp:299
config generate_report(const std::string &name, const context &ct, bool only_static=false)
Definition: reports.cpp:1827
void register_generator(const std::string &name, generator *)
Definition: reports.cpp:1821
const std::set< std::string > & report_list()
Definition: reports.cpp:1841
An object to leave the synced context during draw or unsynced wml items when we don’t know whether we...
std::vector< int > get_teams() const
Definition: side_filter.cpp:59
bool match(const team &t) const
static map & registry()
using static function variable instead of static member variable to prevent static initialization fia...
static synced_state get_synced_state()
static bool is_synced()
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
game_events::pump_result_t get_village(const map_location &, const int owner_side, game_data *fire_event)
Acquires a village from owner_side.
Definition: team.cpp:426
const std::string & team_name() const
Definition: team.hpp:287
void set_color(const std::string &color)
Definition: team.hpp:249
void set_flag(const std::string &flag)
Definition: team.hpp:295
void lose_village(const map_location &)
Definition: team.cpp:452
bool match(const map_location &loc) const
Definition: filter.hpp:43
void get_locations(std::set< map_location > &locs, bool with_border=false) const
gets all locations on the map that match this filter
Definition: filter.hpp:63
To store label data Class implements logic for rendering.
Definition: label.hpp:111
const std::string & id() const
Definition: terrain.hpp:52
t_translation::terrain_code number() const
Definition: terrain.hpp:66
void replace_schedule(const config &time_cfg)
Replace the time of day schedule.
int number_of_turns() const
const time_of_day get_illuminated_time_of_day(const unit_map &units, const gamemap &map, const map_location &loc, int for_turn=0) const
Returns time of day object for the passed turn at a location.
const std::vector< time_of_day > & times(const map_location &loc=map_location::null_location()) const
std::vector< std::string > get_area_ids() const
const std::set< map_location > & get_area_by_index(int index) const
void remove_time_area(const std::string &id)
Removes a time area from config, making it follow the scenario's normal time-of-day sequence.
void add_time_area(const gamemap &map, const config &cfg)
Adds a new local time area from config, making it follow its own time-of-day sequence.
void replace_local_schedule(const std::vector< time_of_day > &schedule, int area_index, int initial_time=0)
int turn() const
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:56
const std::set< map_location > & get_area_by_id(const std::string &id) const
std::pair< int, std::string > get_area_on_hex(const map_location &loc) const
void wait_for_end() const
Definition: animation.cpp:1437
void add_animation(unit_const_ptr animated_unit, const unit_animation *animation, const map_location &src=map_location::null_location(), bool with_bars=false, const std::string &text="", const color_t text_color={0, 0, 0})
Definition: animation.cpp:1319
void start_animations()
Definition: animation.cpp:1371
void set_all_standing()
Definition: animation.cpp:1498
std::vector< const unit * > all_matches_with_unit(const unit &u) const
Definition: filter.hpp:162
std::vector< const unit * > all_matches_at(const map_location &loc) const
Definition: filter.hpp:158
std::vector< const unit * > all_matches_on_map(const map_location *loc=nullptr, const unit *other_unit=nullptr) const
Definition: filter.cpp:67
Container associating units to locations.
Definition: map.hpp:98
unit_ptr extract(const map_location &loc)
Extracts a unit from the map.
Definition: map.cpp:259
unit_iterator find(std::size_t id)
Definition: map.cpp:302
std::size_t erase(const map_location &l)
Erases the unit at location l, if any.
Definition: map.cpp:289
umap_retval_pair_t insert(const unit_ptr &p)
Inserts the unit pointed to by p into the map.
Definition: map.cpp:135
umap_retval_pair_t move(const map_location &src, const map_location &dst)
Moves a unit from location src to location dst.
Definition: map.cpp:92
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:1265
config_array_view traits() const
Definition: types.hpp:408
A single unit type that the player may recruit.
Definition: types.hpp:43
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:476
bool has_variation(const std::string &variation_id) const
Definition: types.cpp:759
This class represents a single unit of a specific type.
Definition: unit.hpp:133
unit_ptr clone() const
Definition: unit.hpp:221
static void clear_status_caches()
Clear this unit status cache for all units.
Definition: unit.cpp:750
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
Definition: unit.hpp:201
Additional functionality for a non-const variable_info.
Information on a WML variable.
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
static vconfig unconstructed_vconfig()
This is just a wrapper for the default constructor; it exists for historical reasons and to make it c...
Definition: variable.cpp:152
bool null() const
Definition: variable.hpp:72
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:288
const config & get_config() const
Definition: variable.hpp:75
config get_parsed_config() const
Definition: variable.cpp:176
child_list get_children(const std::string &key) const
Definition: variable.cpp:226
constexpr uint8_t ALPHA_OPAQUE
Definition: color.hpp:45
A component of the AI framework.
Composite AI with turn sequence which is a vector of stages.
Composite AI contexts.
Define conditionals for the game's events mechanism, a.k.a.
Definitions for the interface to Wesnoth Markup Language (WML).
Managing the AIs configuration - headers.
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
LUA AI Support engine - creating specific ai components from config.
Define locations as used by the game's events mechanism.
std::size_t i
Definition: function.cpp:1030
static int intf_modify_ai(lua_State *L, const char *action)
int dispatch(lua_State *L)
static int load_fake_units(lua_State *L, int arg, T &fake_units)
static int impl_animator_get(lua_State *L)
#define SCENARIO_SETTER(name, type)
static int impl_null_callback(lua_State *L)
static int impl_animator_collect(lua_State *L)
#define SCENARIO_GETTER(name, type)
static int intf_unit_resistance(lua_State *L)
Returns unit resistance against a given attack type.
static int intf_synchronize_choice(lua_State *L)
Ensures a value is synchronized among all the clients.
static int luaW_check_schedule(lua_State *L, int idx)
#define SCENARIO_VALID(name)
#define ERR_LUA
static lg::log_domain log_scripting_lua("scripting/lua")
static int intf_add_known_unit(lua_State *L)
Adds a new known unit type to the help system.
static int intf_append_ai(lua_State *L)
static int impl_end_level_data_collect(lua_State *L)
static int impl_mp_settings_len(lua_State *L)
#define CURRENT_GETTER(name, type)
#define READ_ONE_FILTER(key, tag)
luaW_Registry currentReg
#define SCHEDULE_SETTER(name, type)
luaW_Registry scenarioReg
bool(*)(const vconfig &) wml_conditional_handler
static int cfun_exec_candidate_action(lua_State *L)
#define ERR_WML
static int intf_run_event_wml(lua_State *L)
static int intf_eval_conditional(lua_State *L)
Evaluates a boolean WML conditional.
luaW_Registry & gameConfigReg()
static int impl_clear_animation(lua_State *L)
const char * labelKey
static int intf_advance_unit(lua_State *L)
Advances a unit if the unit has enough xp.
static int impl_add_animation(lua_State *L)
static int intf_debug_ai(lua_State *L)
Debug access to the ai tables.
#define GAME_CONFIG_SIMPLE_SETTER(name)
static int intf_unit_movement_cost(lua_State *L)
Returns unit movement cost on a given terrain.
#define LOG_LUA
static int intf_add_modification(lua_State *L)
Adds a modification to a unit.
#define SCHEDULE_GETTER(name, type)
static void luaW_pushsimdata(lua_State *L, const combatant &cmb)
Puts a table at the top of the stack with some combat result.
#define CALLBACK_GETTER(name, type)
static std::string read_event_name(lua_State *L, int idx)
static int intf_remove_modifications(lua_State *L)
Removes modifications from a unit.
int dispatch2(lua_State *L)
static int intf_copy_unit(lua_State *L)
Copies a unit.
static void push_component(lua_State *L, ai::component *c, const std::string &ct="")
#define WRN_LUA
static int impl_mp_settings_get(lua_State *L)
static int cfun_exec_stage(lua_State *L)
static int impl_game_events_get(lua_State *L)
static int intf_modify_ai_old(lua_State *L)
Lua frontend to the modify_ai functionality.
static int intf_get_resource(lua_State *L)
Gets a table for an resource tag.
static const char animatorKey[]
static bool is_handled_file_tag(std::string_view s)
These are the child tags of [scenario] (and the like) that are handled elsewhere (in the C++ code).
static int intf_synchronize_choices(lua_State *L)
Ensures a value is synchronized among all the clients.
static int intf_switch_ai(lua_State *L)
static int intf_create_unit(lua_State *L)
Creates a unit from its WML description.
static int intf_transform_unit(lua_State *L)
Changes a unit to the given unit type.
static int impl_floating_label_getmethod(lua_State *L)
static int intf_get_viewing_side(lua_State *L)
Gets currently viewing side.
static auto & dummy
int(game_lua_kernel::* member_callback)(lua_State *)
int(game_lua_kernel::* member_callback2)(lua_State *, bool)
static int intf_invoke_synced_command(lua_State *L)
static void luaW_push_tod(lua_State *L, const time_of_day &tod)
#define DBG_LUA
static int intf_do_unsynced(lua_State *L)
Calls a function in an unsynced context (this specially means that all random calls used by that func...
luaW_Registry scheduleReg
static void luaW_pushsimweapon(lua_State *L, const battle_context_unit_stats &bcustats)
Puts a table at the top of the stack with information about the combatants' weapons.
static int intf_handle_user_interact(lua_State *)
static int intf_unit_jamming_cost(lua_State *L)
Returns unit jamming cost on a given terrain.
static int impl_end_level_data_get(lua_State *L)
static lg::log_domain log_wml("wml")
static int intf_unit_defense(lua_State *L)
Returns unit defense on a given terrain.
static int intf_get_era(lua_State *L)
Gets a table for an era tag.
static bool impl_get_callback(lua_State *L, const std::string &name)
#define SCHEDULE_VALID(name)
static int cfun_wml_condition(lua_State *L)
Executes its upvalue as a wml condition and returns the result.
luaW_Registry callbacksReg
static int * luaW_check_floating_label(lua_State *L, int idx)
static int impl_game_events_dir(lua_State *L)
static int intf_unit_vision_cost(lua_State *L)
Returns unit vision cost on a given terrain.
static std::string _(const char *str)
Definition: gettext.hpp:97
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses or is affected by a given ability.
Definition: abilities.cpp:186
const std::string & id() const
Gets this unit's id.
Definition: unit.hpp:380
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
void advance_to(const unit_type &t, bool use_traits=false)
Advances this unit to another type.
Definition: unit.cpp:979
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit's defense on a given terrain.
Definition: unit.cpp:1726
int resistance_against(const std::string &damage_name, bool attacker, const map_location &loc, const_attack_ptr weapon=nullptr, const const_attack_ptr &opp_weapon=nullptr) const
The unit's resistance against a given damage type.
Definition: unit.cpp:1790
void add_modification(const std::string &type, const config &modification, bool no_add=false)
Add a new modification to the unit.
Definition: unit.cpp:2437
static const std::set< std::string > builtin_effects
Definition: unit.hpp:1596
std::string describe_builtin_effect(const std::string &type, const config &effect)
Construct a string describing a built-in effect.
Definition: unit.cpp:1968
config & get_modifications()
Get the raw modifications.
Definition: unit.hpp:1535
void expire_modifications(const std::string &duration)
Clears those modifications whose duration has expired.
Definition: unit.cpp:1289
void apply_builtin_effect(const std::string &type, const config &effect)
Apply a builtin effect to the unit.
Definition: unit.cpp:2033
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1413
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition: unit.hpp:1496
int vision_cost(const t_translation::terrain_code &terrain) const
Get the unit's vision cost on a particular terrain.
Definition: unit.hpp:1506
int jamming_cost(const t_translation::terrain_code &terrain) const
Get the unit's jamming cost on a particular terrain.
Definition: unit.hpp:1516
std::string label
What to show in the filter's drop-down list.
Definition: manager.cpp:201
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:199
Define the handlers for the game's events mechanism.
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:507
Standard logging facilities (interface).
#define log_scope(description)
Definition: log.hpp:275
std::decay_t< T > lua_check(lua_State *L, int n)
Definition: push_check.hpp:411
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:905
bool luaW_iststring(lua_State *L, int index)
Definition: lua_common.cpp:643
void luaW_push_namedtuple(lua_State *L, const std::vector< std::string > &names)
Push an empty "named tuple" onto the stack.
Definition: lua_common.cpp:763
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
void luaW_pushlocation(lua_State *L, const map_location &ml)
Converts a map location object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:808
std::set< map_location > luaW_check_locationset(lua_State *L, int idx)
Converts a table of integer pairs to a set of map location objects.
Definition: lua_common.cpp:888
void luaW_pushtstring(lua_State *L, const t_string &v)
Pushes a t_string on the top of the stack.
Definition: lua_common.cpp:545
bool luaW_pushvariable(lua_State *L, variable_access_const &v)
bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
Gets an optional vconfig from either a table or a userdata.
void luaW_pushvconfig(lua_State *L, const vconfig &cfg)
Pushes a vconfig on the top of the stack.
Definition: lua_common.cpp:539
bool luaW_toboolean(lua_State *L, int n)
int luaW_type_error(lua_State *L, int narg, const char *tname)
std::string_view luaW_tostring(lua_State *L, int index)
bool luaW_getmetafield(lua_State *L, int idx, const char *key)
Like luaL_getmetafield, but returns false if key is an empty string or begins with two underscores.
Definition: lua_common.cpp:524
bool luaW_totstring(lua_State *L, int index, t_string &str)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:610
bool luaW_tableget(lua_State *L, int index, const char *key)
bool luaW_checkvariable(lua_State *L, variable_access_create &v, int n)
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.
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:934
int luaW_push_locationset(lua_State *L, const std::set< map_location > &locs)
Converts a set of map locations to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:876
vconfig luaW_checkvconfig(lua_State *L, int index, bool allow_missing)
Gets an optional vconfig from either a table or a userdata.
bool luaW_tolocation(lua_State *L, int index, map_location &loc)
Converts an optional table or pair of integers to a map location object.
Definition: lua_common.cpp:819
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:868
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil.
t_string luaW_checktstring(lua_State *L, int index)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:635
#define return_cstring_attrib(name, accessor)
Definition: lua_common.hpp:256
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:266
#define return_cfgref_attrib(name, accessor)
Definition: lua_common.hpp:319
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:277
#define modify_bool_attrib(name, accessor)
Definition: lua_common.hpp:414
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:297
#define return_cfg_attrib(name, accessor)
Definition: lua_common.hpp:307
#define modify_string_attrib(name, accessor)
Definition: lua_common.hpp:357
#define return_string_attrib_deprecated(name, prefix, level, version, msg, accessor)
Definition: lua_common.hpp:274
#define GAME_CONFIG_GETTER(name, type, kernel_type)
void luaW_pushracetable(lua_State *L)
Definition: lua_race.cpp:130
void luaW_pushteam(lua_State *L, team &tm)
Create a full userdata containing a pointer to the team.
Definition: lua_team.cpp:536
team * luaW_toteam(lua_State *L, int idx)
Test if the top stack element is a team, and if so, return it.
Definition: lua_team.cpp:562
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:543
std::set< map_location > location_set
int intf_terrain_mask(lua_State *L)
Replaces part of the map.
int intf_on_border(lua_State *L)
int intf_on_board(lua_State *L)
int intf_terrainmap_iter(lua_State *L)
int intf_terrainmap_get(lua_State *L)
int intf_replace_if_failed(lua_State *L)
lua_unit * luaW_checkunit_ref(lua_State *L, int index)
Similar to luaW_checkunit but returns a lua_unit; use this if you need to handle map and recall units...
Definition: lua_unit.cpp:199
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
bool luaW_isunit(lua_State *L, int index)
Test if a Lua value is a unit.
Definition: lua_unit.cpp:113
unit_ptr luaW_checkunit_ptr(lua_State *L, int index, bool only_on_map)
Similar to luaW_checkunit but returns a unit_ptr; use this instead of luaW_checkunit when using an ap...
Definition: lua_unit.cpp:183
lua_unit * luaW_pushlocalunit(lua_State *L, unit &u)
Pushes a private unit on the stack.
Definition: lua_unit.cpp:212
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:142
lua_unit * luaW_pushunit(lua_State *L, Args... args)
Definition: lua_unit.hpp:116
int intf_create_attack(lua_State *L)
const_attack_ptr luaW_toweapon(lua_State *L, int idx)
void luaW_pushweapon(lua_State *L, const attack_ptr &weapon)
std::map< std::string, config > traits_map
bool clear_shroud(int side, bool reset_fog, bool fire_events)
Function that will clear shroud (and fog) based on current unit positions.
Definition: vision.cpp:746
game_events::pump_result_t get_village(const map_location &loc, int side, bool *action_timebonus, bool fire_event)
Makes it so the village at the given location is owned by the given side.
Definition: move.cpp:221
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's needed when creating a pathfinding::...
Definition: vision.cpp:47
void clear()
Clear the current render target.
Definition: draw.cpp:43
const color_t LABEL_COLOR
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
const int SIZE_SMALL
Definition: constants.cpp:24
void remove_floating_label(int handle, const std::chrono::milliseconds &fadeout)
removes the floating label given by 'handle' from the screen
void move_floating_label(int handle, double xmove, double ymove)
moves the floating label given by 'handle' by (xmove,ymove)
@ CENTER_ALIGN
Game configuration data as global variables.
Definition: build_info.cpp:61
std::string path
Definition: filesystem.cpp:93
int rest_heal_amount
Definition: game_config.cpp:48
int village_income
Definition: game_config.cpp:41
const bool & debug
Definition: game_config.cpp:95
int kill_experience
Definition: game_config.cpp:44
unsigned int tile_size
Definition: game_config.cpp:55
int combat_experience
Definition: game_config.cpp:45
void load_config(const config &v)
int village_support
Definition: game_config.cpp:42
bool variable_matches(const vconfig &values)
bool have_location(const vconfig &cfg)
bool have_unit(const vconfig &cfg)
bool conditional_passed(const vconfig &cond)
bool fire_event(const ui_event event, const std::vector< std::pair< widget *, ui_event >> &event_chain, widget *dispatcher, widget *w, F &&... params)
Helper function for fire_event.
std::shared_ptr< halo_record > handle
Definition: halo.hpp:31
logger & err()
Definition: log.cpp:306
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:517
logger & info()
Definition: log.cpp:318
std::string register_table(lua_State *L)
Definition: lua_audio.cpp:509
std::string register_vconfig_metatable(lua_State *L)
Adds the vconfig metatable.
Definition: lua_common.cpp:473
int intf_tovconfig(lua_State *L)
Creates a vconfig containing the WML table.
Definition: lua_common.cpp:411
void set_functions(lua_State *L, const std::vector< lua_cpp::Reg > &functions)
Analogous to lua_setfuncs, it registers a collection of function wrapper objects into a table,...
void push_closure(lua_State *L, const lua_function &f, int nup)
Pushes a closure which retains a std::function object as its first up-value.
int intf_show_recall_dialog(lua_State *L)
Definition: lua_gui2.cpp:318
int intf_show_recruit_dialog(lua_State *L)
Definition: lua_gui2.cpp:262
int show_gamestate_inspector(const std::string &name, const game_data &data, const game_state &state)
Definition: lua_gui2.cpp:256
std::string register_metatable(lua_State *L)
Definition: lua_race.cpp:104
std::string register_metatable(lua_State *L)
Definition: lua_team.cpp:500
std::string register_metatables(lua_State *L)
std::string register_metatable(lua_State *L)
std::string register_table(lua_State *L)
std::string register_metatables(lua_State *L)
Definition: lua_unit.cpp:942
std::string register_attacks_metatables(lua_State *L)
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
Definition: markup.hpp:51
config get_user_choice(const std::string &name, const user_choice &uch, int side=0)
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.
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
@ VACANT_ANY
Definition: pathfind.hpp:39
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units, bool check_vision)
Definition: teleport.cpp:251
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,...
Definition: pathfind.cpp:54
Unit and team statistics.
game_board * gameboard
Definition: resources.cpp:20
fake_unit_manager * fake_units
Definition: resources.cpp:30
replay * recorder
Definition: resources.cpp:28
play_controller * controller
Definition: resources.cpp:21
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:33
Contains the general settings which have a default.
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1047
@ SOUND_FX
Definition: sound.hpp:34
terrain_code read_terrain_code(std::string_view str)
Reads a single terrain from a string.
std::string write_terrain_code(const terrain_code &tcode)
Writes a single terrain code to a string.
const terrain_code NONE_TERRAIN
Definition: translation.hpp:58
void move_unit(const std::vector< map_location > &path, const unit_ptr &u, bool animate, map_location::direction dir, bool force_scroll)
Display a unit moving along a given path.
Definition: udisplay.cpp:505
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
constexpr auto transform
Definition: ranges.hpp:41
constexpr auto filter
Definition: ranges.hpp:38
@ STRIP_SPACES
REMOVE_EMPTY: remove empty elements.
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
Definition: charconv.hpp:154
std::string join_map(const T &v, const std::string &major=",", const std::string &minor=":")
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
bool headless()
The game is running headless.
Definition: video.cpp:139
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
This module contains various pathfinding functions and utilities.
std::string_view data
Definition: picture.cpp:178
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
Define the game's event mechanism.
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:425
std::decay_t< T > luaW_table_get_def(lua_State *L, int index, std::string_view k, const T &def)
returns t[k] where k is the table at index index and k is k or def if it is not convertible to the co...
Definition: push_check.hpp:435
void luaW_table_set(lua_State *L, int index, std::string_view k, const T &value)
Definition: push_check.hpp:457
candidate action framework
Replay control code.
#define ON_SCOPE_EXIT(...)
Run some arbitrary code (a lambda) when the current scope exits The lambda body follows this header,...
Definition: scope_exit.hpp:43
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
Composite AI stages.
A set of achievements tied to a particular content.
Represents a single achievement and its data.
std::string icon_completed_
The icon of the achievement to show on the UI if the achievement is completed.
t_string name_completed_
The name of the achievement to show on the UI if the achievement is completed.
t_string description_completed_
The name of the achievement to show on the UI if the achievement is completed.
bool achieved_
Whether the achievement has been completed.
std::string id_
The ID of the achievement.
int max_progress_
When the achievement's current progress matches or equals this value, then it should be marked as com...
std::string sound_path_
The path to a sound to play when an achievement is completed.
int current_progress_
The current progress value of the achievement.
std::vector< sub_achievement > sub_achievements_
The list of distinct sub-achievements for this achievement.
advances the unit at loc if it has enough experience, maximum 20 times.
Definition: advancement.hpp:39
advance_unit_params & animate(bool value)
Definition: advancement.hpp:43
advance_unit_params & fire_events(bool value)
Definition: advancement.hpp:42
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:51
bool slows
Attack slows opponent when it hits.
Definition: attack.hpp:57
unsigned int num_blows
Effective number of blows, takes swarm into account.
Definition: attack.hpp:76
std::string plague_type
The plague type used by the attack, if any.
Definition: attack.hpp:80
bool petrifies
Attack petrifies opponent when it hits.
Definition: attack.hpp:59
int drain_percent
Percentage of damage recovered as health.
Definition: attack.hpp:74
bool drains
Attack drains opponent when it hits.
Definition: attack.hpp:58
const_attack_ptr weapon
The weapon used by the unit to attack the opponent, or nullptr if there is none.
Definition: attack.hpp:52
unsigned int rounds
Berserk special can force us to fight more than one round.
Definition: attack.hpp:68
int damage
Effective damage of the weapon (all factors accounted for).
Definition: attack.hpp:72
bool poisons
Attack poisons opponent when it hits.
Definition: attack.hpp:61
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
Definition: attack.hpp:71
int drain_constant
Base HP drained regardless of damage dealt.
Definition: attack.hpp:75
bool firststrike
Attack has firststrike special.
Definition: attack.hpp:63
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:53
bool plagues
Attack turns opponent into a zombie when fatal.
Definition: attack.hpp:60
game_lua_kernel & ref
callbacks_tag(game_lua_kernel &k)
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
static color_t from_hex_string(std::string_view c)
Creates a new color_t object from a string variable in hex format.
Definition: color.cpp:64
static color_t from_rgb_string(std::string_view c)
Creates a new opaque color_t object from a string variable in "R,G,B" format.
Definition: color.cpp:44
All combat-related info.
double slowed
Resulting chance we are slowed.
std::vector< double > hp_dist
Resulting probability distribution (might be not as large as max_hp)
double poisoned
Resulting chance we are poisoned.
double average_hp(unsigned int healing=0) const
What's the average hp (weighted average of hp_dist).
double untouched
Resulting chance we were not hit by this opponent (important if it poisons)
void push_schedule(lua_State *L) const
auto & pc() const
current_tag(game_lua_kernel &k)
auto & gd() const
game_lua_kernel & ref
auto & ev() const
auto ss() const
Additional information on the game outcome which can be provided by WML.
Error used for any general game error, e.g.
Definition: game_errors.hpp:47
game_config_glk_tag(lua_kernel_base &k)
game_lua_kernel & ref
const map_location & filter_loc() const
Represents a single filter condition on an event.
Definition: handlers.hpp:38
entity_location loc1
Definition: pump.hpp:65
entity_location loc2
Definition: pump.hpp:66
std::string name
Definition: pump.hpp:63
Holds a lookup table for members of one type of object.
int dir(lua_State *L)
Implement __dir metamethod.
int set(lua_State *L)
Implement __newindex metamethod.
int get(lua_State *L)
Implement __index metamethod.
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
game_lua_kernel & lk
lua_event_filter(game_lua_kernel &lk, int idx, const config &args)
bool operator()(const game_events::queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
static game_lua_kernel & get(lua_State *L, int)
static game_lua_kernel & get(lua_State *L, int)
static scenario_tag get(lua_State *L, int)
static schedule_tag get(lua_State *L, int n)
Cost function object relying on a Lua function.
Encapsulates the map of the game.
Definition: location.hpp:45
void set_wml_y(int v)
Definition: location.hpp:221
bool valid() const
Definition: location.hpp:110
int wml_y() const
Definition: location.hpp:218
void set_wml_x(int v)
Definition: location.hpp:220
static const map_location & null_location()
Definition: location.hpp:102
int wml_x() const
Definition: location.hpp:217
game_lua_kernel * kernel_
map_locker(game_lua_kernel *kernel)
Interface for querying local choices.
virtual config query_user(int side) const =0
virtual std::string description() const
virtual bool is_visible() const
whether the choice is visible for the user like an advancement choice a non-visible choice is for exa...
virtual config random_choice(int side) const =0
Structure which uses find_routes() to build a cost map This maps each hex to a the movements a unit w...
Definition: pathfind.hpp:268
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:919
std::pair< int, int > get_pair_at(map_location loc) const
Accessor for the cost/reach-amount pairs.
Definition: pathfind.cpp:970
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:73
dest_vect destinations
Definition: pathfind.hpp:101
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:133
std::vector< map_location > steps
Definition: pathfind.hpp:135
int move_cost
Movement cost for reaching the end of the route.
Definition: pathfind.hpp:137
A refinement of paths for use when calculating vision.
Definition: pathfind.hpp:108
std::set< map_location > edges
The edges are the non-destination hexes bordering the destinations.
Definition: pathfind.hpp:117
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:49
auto & cls() const
auto & gamedata() const
auto & tod_man() const
game_lua_kernel & ref
scenario_tag(game_lua_kernel &k)
auto end_level_set() const
auto & pc() const
auto & tod_man() const
schedule_tag(game_lua_kernel &k)
game_lua_kernel & ref
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
Represents a distinct sub-achievement within another achievement.
std::string id_
The ID of the sub-achievement.
bool achieved_
Whether the sub-achievement has been completed.
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
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
int bonus_modified
Definition: time_of_day.hpp:84
std::string id
Definition: time_of_day.hpp:90
tod_color color
The color modifications that should be made to the game board to reflect the time of day.
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:83
t_string name
Definition: time_of_day.hpp:88
std::string image
The image to be displayed in the game status.
Definition: time_of_day.hpp:87
std::string sounds
List of "ambient" sounds associated with this time_of_day, Played at the beginning of turn.
std::string image_mask
The image that is to be laid over all images while this time of day lasts.
Definition: time_of_day.hpp:96
bool valid() const
Definition: map.hpp:273
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:217
mock_char c
mock_party p
static map_location::direction n
static map_location::direction s
unit_type_data unit_types
Definition: types.cpp:1504
Display units performing various actions: moving, attacking, and dying.
Various functions that implement the undoing (and redoing) of in-game commands.
Various functions implementing vision (through fog of war and shroud).
#define d
#define e
#define h
#define b