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