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