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