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