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