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