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