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