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