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