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