1 /*
2    Copyright (C) 2009 - 2018 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Provides a Lua interpreter, to be embedded in WML.
18  *
19  * @note Naming conventions:
20  *   - intf_ functions are exported in the wesnoth domain,
21  *   - impl_ functions are hidden inside metatables,
22  *   - cfun_ functions are closures,
23  *   - luaW_ functions are helpers in Lua style.
24  */
25 
26 #include "scripting/game_lua_kernel.hpp"
27 
28 #include "actions/attack.hpp"           // for battle_context_unit_stats, etc
29 #include "actions/advancement.hpp"           // for advance_unit_at, etc
30 #include "actions/move.hpp"		// for clear_shroud
31 #include "actions/vision.hpp"		// for clear_shroud
32 #include "ai/composite/ai.hpp"          // for ai_composite
33 #include "ai/composite/component.hpp"   // for component, etc
34 #include "ai/composite/contexts.hpp"    // for ai_context
35 #include "ai/lua/engine_lua.hpp"  // for engine_lua
36 #include "ai/composite/rca.hpp"  // for candidate_action
37 #include "ai/composite/stage.hpp"  // for stage
38 #include "ai/configuration.hpp"         // for configuration
39 #include "ai/lua/core.hpp"              // for lua_ai_context, etc
40 #include "ai/manager.hpp"               // for manager, holder
41 #include "attack_prediction.hpp"        // for combatant
42 #include "chat_events.hpp"              // for chat_handler, etc
43 #include "config.hpp"                   // for config, etc
44 #include "display_chat_manager.hpp"	// for clear_chat_messages
45 #include "formatter.hpp"
46 #include "game_board.hpp"               // for game_board
47 #include "game_classification.hpp"      // for game_classification, etc
48 #include "game_config.hpp"              // for debug, base_income, etc
49 #include "game_config_manager.hpp"      // for game_config_manager
50 #include "game_data.hpp"               // for game_data, etc
51 #include "game_display.hpp"             // for game_display
52 #include "game_errors.hpp"              // for game_error
53 #include "game_events/conditional_wml.hpp"  // for conditional_passed
54 #include "game_events/entity_location.hpp"
55 #include "game_events/pump.hpp"         // for queued_event
56 #include "preferences/game.hpp"         // for encountered_units
57 #include "help/help.hpp"
58 #include "log.hpp"                      // for LOG_STREAM, logger, etc
59 #include "utils/make_enum.hpp"                // for operator<<
60 #include "map/map.hpp"                      // for gamemap
61 #include "map/label.hpp"
62 #include "map/location.hpp"             // for map_location
63 #include "mouse_events.hpp"             // for mouse_handler
64 #include "mp_game_settings.hpp"         // for mp_game_settings
65 #include "pathfind/pathfind.hpp"        // for full_cost_map, plain_route, etc
66 #include "pathfind/teleport.hpp"        // for get_teleport_locations, etc
67 #include "play_controller.hpp"          // for play_controller
68 #include "recall_list_manager.hpp"      // for recall_list_manager
69 #include "replay.hpp"                   // for get_user_choice, etc
70 #include "reports.hpp"                  // for register_generator, etc
71 #include "resources.hpp"                // for whiteboard
72 #include "scripting/lua_audio.hpp"
73 #include "scripting/lua_unit.hpp"
74 #include "scripting/lua_unit_attacks.hpp"
75 #include "scripting/lua_common.hpp"
76 #include "scripting/lua_cpp_function.hpp"
77 #include "scripting/lua_gui2.hpp"	// for show_gamestate_inspector
78 #include "scripting/lua_pathfind_cost_calculator.hpp"
79 #include "scripting/lua_race.hpp"
80 #include "scripting/lua_team.hpp"
81 #include "scripting/lua_unit_type.hpp"
82 #include "scripting/push_check.hpp"
83 #include "synced_commands.hpp"
84 #include "color.hpp"                // for surface
85 #include "sdl/surface.hpp"                // for surface
86 #include "side_filter.hpp"              // for side_filter
87 #include "sound.hpp"                    // for commit_music_changes, etc
88 #include "soundsource.hpp"
89 #include "synced_context.hpp"           // for synced_context, etc
90 #include "synced_user_choice.hpp"
91 #include "team.hpp"                     // for team, village_owner
92 #include "terrain/terrain.hpp"                  // for terrain_type
93 #include "terrain/filter.hpp"           // for terrain_filter
94 #include "terrain/translation.hpp"      // for read_terrain_code, etc
95 #include "terrain/type_data.hpp"
96 #include "time_of_day.hpp"              // for time_of_day
97 #include "tod_manager.hpp"              // for tod_manager
98 #include "tstring.hpp"                  // for t_string, operator+
99 #include "units/unit.hpp"                     // for unit
100 #include "units/animation_component.hpp"  // for unit_animation_component
101 #include "units/udisplay.hpp"
102 #include "units/filter.hpp"
103 #include "units/map.hpp"  // for unit_map, etc
104 #include "units/ptr.hpp"                 // for unit_const_ptr, unit_ptr
105 #include "units/types.hpp"    // for unit_type_data, unit_types, etc
106 #include "variable.hpp"                 // for vconfig, etc
107 #include "variable_info.hpp"
108 #include "whiteboard/manager.hpp"       // for whiteboard
109 #include "wml_exception.hpp"
110 #include "deprecation.hpp"
111 
112 #include "utils/functional.hpp"               // for bind_t, bind
113 #include <boost/range/algorithm/copy.hpp>    // boost::copy
114 #include <boost/range/adaptors.hpp>     // boost::adaptors::filtered
115 #include <cassert>                      // for assert
116 #include <cstring>                      // for strcmp
117 #include <iterator>                     // for distance, advance
118 #include <map>                          // for map, map<>::value_type, etc
119 #include <new>                          // for operator new
120 #include <set>                          // for set
121 #include <sstream>                      // for operator<<, basic_ostream, etc
122 #include <utility>                      // for pair
123 #include <algorithm>
124 #include <vector>                       // for vector, etc
125 #include <SDL2/SDL_timer.h>                  // for SDL_GetTicks
126 #include "lua/lauxlib.h"                // for luaL_checkinteger, etc
127 #include "lua/lua.h"                    // for lua_setfield, etc
128 
129 class CVideo;
130 
131 #ifdef DEBUG_LUA
132 #include "scripting/debug_lua.hpp"
133 #endif
134 
135 static lg::log_domain log_scripting_lua("scripting/lua");
136 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
137 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
138 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
139 
140 std::vector<config> game_lua_kernel::preload_scripts;
141 config game_lua_kernel::preload_config;
142 
143 // Template which allows to push member functions to the lua kernel base into lua as C functions, using a shim
144 typedef int (game_lua_kernel::*member_callback)(lua_State *);
145 
146 template <member_callback method>
dispatch(lua_State * L)147 int dispatch(lua_State *L) {
148 	return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L);
149 }
150 
151 // Pass a const bool also...
152 typedef int (game_lua_kernel::*member_callback2)(lua_State *, bool);
153 
154 template <member_callback2 method, bool b>
dispatch2(lua_State * L)155 int dispatch2(lua_State *L) {
156 	return ((lua_kernel_base::get_lua_kernel<game_lua_kernel>(L)).*method)(L, b);
157 }
158 
159 struct map_locker
160 {
map_lockermap_locker161 	map_locker(game_lua_kernel* kernel) : kernel_(kernel)
162 	{
163 		++kernel_->map_locked_;
164 	}
~map_lockermap_locker165 	~map_locker()
166 	{
167 		--kernel_->map_locked_;
168 	}
169 	game_lua_kernel* kernel_;
170 };
171 
172 
extract_preload_scripts(const config & game_config)173 void game_lua_kernel::extract_preload_scripts(const config& game_config)
174 {
175 	game_lua_kernel::preload_scripts.clear();
176 	for (const config& cfg : game_config.child_range("lua")) {
177 		game_lua_kernel::preload_scripts.push_back(cfg);
178 	}
179 	game_lua_kernel::preload_config = game_config.child("game_config");
180 }
181 
log_error(char const * msg,char const * context)182 void game_lua_kernel::log_error(char const * msg, char const * context)
183 {
184 	lua_kernel_base::log_error(msg, context);
185 	lua_chat(context, msg);
186 }
187 
lua_chat(const std::string & caption,const std::string & msg)188 void game_lua_kernel::lua_chat(const std::string& caption, const std::string& msg)
189 {
190 	if (game_display_) {
191 		game_display_->get_chat_manager().add_chat_message(time(nullptr), caption, 0, msg,
192 			events::chat_handler::MESSAGE_PUBLIC, false);
193 	}
194 }
195 
196 /**
197  * Gets a vector of sides from side= attribute in a given config node.
198  * Promotes consistent behavior.
199  */
get_sides_vector(const vconfig & cfg)200 std::vector<int> game_lua_kernel::get_sides_vector(const vconfig& cfg)
201 {
202 	const config::attribute_value sides = cfg["side"];
203 	const vconfig &ssf = cfg.child("filter_side");
204 
205 	if (!ssf.null()) {
206 		if(!sides.empty()) { WRN_LUA << "ignoring duplicate side filter information (inline side=)" << std::endl; }
207 		side_filter filter(ssf, &game_state_);
208 		return filter.get_teams();
209 	}
210 
211 	side_filter filter(sides.str(), &game_state_);
212 	return filter.get_teams();
213 }
214 
215 
216 
special_locations_len(lua_State * L)217 static int special_locations_len(lua_State *L)
218 {
219 	lua_pushnumber(L, lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().size());
220 	return 1;
221 }
222 
special_locations_next(lua_State * L)223 static int special_locations_next(lua_State *L)
224 {
225 	const t_translation::starting_positions::left_map& left = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().left;
226 
227 	t_translation::starting_positions::left_const_iterator it;
228 	if (lua_isnoneornil(L, 2)) {
229 		it = left.begin();
230 	}
231 	else {
232 		it = left.find(luaL_checkstring(L, 2));
233 		if (it == left.end()) {
234 			return 0;
235 		}
236 		++it;
237 	}
238 	if (it == left.end()) {
239 		return 0;
240 	}
241 	lua_pushstring(L, it->first.c_str());
242 	luaW_pushlocation(L, it->second);
243 	return 2;
244 }
245 
special_locations_pairs(lua_State * L)246 static int special_locations_pairs(lua_State *L)
247 {
248 	lua_pushcfunction(L, &special_locations_next);
249 	lua_pushvalue(L, -2);
250 	lua_pushnil(L);
251 	return 3;
252 }
253 
special_locations_index(lua_State * L)254 static int special_locations_index(lua_State *L)
255 {
256 	const t_translation::starting_positions::left_map& left = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().left;
257 	auto it = left.find(luaL_checkstring(L, 2));
258 	if (it == left.end()) {
259 		return 0;
260 	}
261 	else {
262 		luaW_pushlocation(L, it->second);
263 		return 1;
264 	}
265 }
266 
special_locations_newindex(lua_State * L)267 static int special_locations_newindex(lua_State *L)
268 {
269 	lua_pushstring(L, "special locations cannot be modified using wesnoth.special_locations");
270 	return lua_error(L);
271 }
272 
push_locations_table(lua_State * L)273 static void push_locations_table(lua_State *L)
274 {
275 	lua_newtable(L); // The functor table
276 	lua_newtable(L); // The functor metatable
277 	lua_pushstring(L, "__len");
278 	lua_pushcfunction(L, &special_locations_len);
279 	lua_rawset(L, -3);
280 	lua_pushstring(L, "__index");
281 	lua_pushcfunction(L, &special_locations_index);
282 	lua_rawset(L, -3);
283 	lua_pushstring(L, "__newindex");
284 	lua_pushcfunction(L, &special_locations_newindex);
285 	lua_rawset(L, -3);
286 	lua_pushstring(L, "__pairs");
287 	lua_pushcfunction(L, &special_locations_pairs);
288 	lua_rawset(L, -3);
289 	lua_setmetatable(L, -2); // Apply the metatable to the functor table
290 }
291 
292 namespace {
293 	/**
294 	 * Temporary entry to a queued_event stack
295 	 */
296 	struct queued_event_context
297 	{
298 		typedef game_events::queued_event qe;
299 		std::stack<qe const *> & stack_;
300 
queued_event_context__anon384f78800111::queued_event_context301 		queued_event_context(qe const *new_qe, std::stack<qe const*> & stack)
302 			: stack_(stack)
303 		{
304 			stack_.push(new_qe);
305 		}
306 
~queued_event_context__anon384f78800111::queued_event_context307 		~queued_event_context()
308 		{
309 			stack_.pop();
310 		}
311 	};
312 }//unnamed namespace for queued_event_context
313 
314 /**
315  * Gets currently viewing side.
316  * - Ret 1: integer specifying the currently viewing side
317  * - Ret 2: Bool whether the vision is not limited to that team, this can for example be true during replays.
318  */
intf_get_viewing_side(lua_State * L)319 static int intf_get_viewing_side(lua_State *L)
320 {
321 	if(const display* disp = display::get_singleton()) {
322 		lua_pushinteger(L, disp->viewing_side());
323 		lua_pushboolean(L, disp->show_everything());
324 		return 2;
325 	}
326 	else {
327 		return 0;
328 	}
329 }
330 
331 static const char animatorKey[] = "unit animator";
332 
impl_animator_collect(lua_State * L)333 static int impl_animator_collect(lua_State* L) {
334 	unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
335 	anim.~unit_animator();
336 	return 0;
337 }
338 
impl_add_animation(lua_State * L)339 static int impl_add_animation(lua_State* L)
340 {
341 	unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
342 	unit& u = luaW_checkunit(L, 2);
343 	std::string which = luaL_checkstring(L, 3);
344 
345 	using hit_type = unit_animation::hit_type;
346 	std::string hits_str = luaL_checkstring(L, 4);
347 	hit_type hits = hit_type::string_to_enum(hits_str, hit_type::INVALID);
348 
349 	map_location dest;
350 	int v1 = 0, v2 = 0;
351 	bool bars = false;
352 	t_string text;
353 	color_t color{255, 255, 255};
354 	const_attack_ptr primary, secondary;
355 
356 	if(lua_istable(L, 5)) {
357 		lua_getfield(L, 5, "target");
358 		if(luaW_tolocation(L, -1, dest)) {
359 			if(dest == u.get_location()) {
360 				return luaL_argerror(L, 5, "target location must be different from animated unit's location");
361 			} else if(!tiles_adjacent(dest, u.get_location())) {
362 				return luaL_argerror(L, 5, "target location must be adjacent to the animated unit");
363 			}
364 		} else {
365 			// luaW_tolocation may set the location to (0,0) if it fails
366 			dest = map_location();
367 			if(!lua_isnoneornil(L, -1)) {
368 				return luaW_type_error(L, 5, "target", "location table");
369 			}
370 		}
371 		lua_pop(L, 1);
372 
373 		lua_getfield(L, 5, "value");
374 		if(lua_isnumber(L, -1)) {
375 			v1 = lua_tonumber(L, -1);
376 		} else if(lua_istable(L, -1)) {
377 			lua_rawgeti(L, -1, 1);
378 			v1 = lua_tonumber(L, -1);
379 			lua_pop(L, 1);
380 			lua_rawgeti(L, -1, 2);
381 			v2 = lua_tonumber(L, -1);
382 			lua_pop(L, 1);
383 		} else if(!lua_isnoneornil(L, -1)) {
384 			return luaW_type_error(L, 5, "value", "number or array of two numbers");
385 		}
386 		lua_pop(L, 1);
387 
388 		lua_getfield(L, 5, "with_bars");
389 		if(lua_isboolean(L, -1)) {
390 			bars = luaW_toboolean(L, -1);
391 		} else if(!lua_isnoneornil(L, -1)) {
392 			return luaW_type_error(L, 5, "with_bars", lua_typename(L, LUA_TBOOLEAN));
393 		}
394 		lua_pop(L, 1);
395 
396 		lua_getfield(L, 5, "text");
397 		if(lua_isstring(L, -1)) {
398 			text = lua_tostring(L, -1);
399 		} else if(luaW_totstring(L, -1, text)) {
400 			// Do nothing; luaW_totstring already assigned the value
401 		} else if(!lua_isnoneornil(L, -1)) {
402 			return luaW_type_error(L, 5, "text", lua_typename(L, LUA_TSTRING));
403 		}
404 		lua_pop(L, 1);
405 
406 		lua_getfield(L, 5, "color");
407 		if(lua_istable(L, -1) && lua_rawlen(L, -1) == 3) {
408 			int idx = lua_absindex(L, -1);
409 			lua_rawgeti(L, idx, 1); // red @ -3
410 			lua_rawgeti(L, idx, 2); // green @ -2
411 			lua_rawgeti(L, idx, 3); // blue @ -1
412 			color = color_t(lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1));
413 			lua_pop(L, 3);
414 		} else if(!lua_isnoneornil(L, -1)) {
415 			return luaW_type_error(L, 5, "color", "array of three numbers");
416 		}
417 		lua_pop(L, 1);
418 
419 		lua_getfield(L, 5, "primary");
420 		primary = luaW_toweapon(L, -1);
421 		if(!primary && !lua_isnoneornil(L, -1)) {
422 			return luaW_type_error(L, 5, "primary", "weapon");
423 		}
424 		lua_pop(L, 1);
425 
426 		lua_getfield(L, 5, "secondary");
427 		secondary = luaW_toweapon(L, -1);
428 		if(!secondary && !lua_isnoneornil(L, -1)) {
429 			return luaW_type_error(L, 5, "secondary", "weapon");
430 		}
431 		lua_pop(L, 1);
432 	} else if(!lua_isnoneornil(L, 5)) {
433 		return luaW_type_error(L, 5, "table of options");
434 	}
435 
436 	anim.add_animation(&u, which, u.get_location(), dest, v1, bars, text, color, hits, primary, secondary, v2);
437 	return 0;
438 }
439 
impl_run_animation(lua_State * L)440 int game_lua_kernel::impl_run_animation(lua_State* L)
441 {
442 	CVideo& v = CVideo::get_singleton();
443 	if(v.update_locked() || v.faked()) {
444 		return 0;
445 	}
446 	events::command_disabler command_disabler;
447 	unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
448 	play_controller_.play_slice(false);
449 	anim.start_animations();
450 	anim.wait_for_end();
451 	anim.set_all_standing();
452 	return 0;
453 }
454 
impl_clear_animation(lua_State * L)455 static int impl_clear_animation(lua_State* L)
456 {
457 	unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
458 	anim.clear();
459 	return 0;
460 }
461 
impl_animator_get(lua_State * L)462 static int impl_animator_get(lua_State* L)
463 {
464 	const char* m = lua_tostring(L, 2);
465 	return luaW_getmetafield(L, 1, m);
466 }
467 
intf_create_animator(lua_State * L)468 int game_lua_kernel::intf_create_animator(lua_State* L)
469 {
470 	new(L) unit_animator;
471 	if(luaL_newmetatable(L, animatorKey)) {
472 		luaL_Reg metafuncs[] {
473 			{"__gc", impl_animator_collect},
474 			{"__index", impl_animator_get},
475 			{"add", impl_add_animation},
476 			{"run", &dispatch<&game_lua_kernel::impl_run_animation>},
477 			{"clear", impl_clear_animation},
478 			{nullptr, nullptr},
479 		};
480 		luaL_setfuncs(L, metafuncs, 0);
481 		lua_pushstring(L, "__metatable");
482 		lua_setfield(L, -2, animatorKey);
483 	}
484 	lua_setmetatable(L, -2);
485 	return 1;
486 }
487 
intf_gamestate_inspector(lua_State * L)488 int game_lua_kernel::intf_gamestate_inspector(lua_State *L)
489 {
490 	if (game_display_) {
491 		return lua_gui2::show_gamestate_inspector(luaW_checkvconfig(L, 1), gamedata(), game_state_);
492 	}
493 	return 0;
494 }
495 
496 /**
497  * Gets the unit at the given location or with the given id.
498  * - Arg 1: location
499  * OR
500  * - Arg 1: string ID
501  * - Ret 1: full userdata with __index pointing to impl_unit_get and
502  *          __newindex pointing to impl_unit_set.
503  */
intf_get_unit(lua_State * L)504 int game_lua_kernel::intf_get_unit(lua_State *L)
505 {
506 	map_location loc;
507 	if(lua_isstring(L, 1) && !lua_isnumber(L, 1)) {
508 		std::string id = luaL_checkstring(L, 1);
509 		for(const unit& u : units()) {
510 			if(u.id() == id) {
511 				luaW_pushunit(L, u.underlying_id());
512 				return 1;
513 			}
514 		}
515 		return 0;
516 	}
517 	if(!luaW_tolocation(L, 1, loc)) {
518 		return luaL_argerror(L, 1, "expected string or location");
519 	}
520 	unit_map::const_iterator ui = units().find(loc);
521 
522 	if (!ui.valid()) return 0;
523 
524 	luaW_pushunit(L, ui->underlying_id());
525 	return 1;
526 }
527 
528 /**
529  * Gets the unit displayed in the sidebar.
530  * - Ret 1: full userdata with __index pointing to impl_unit_get and
531  *          __newindex pointing to impl_unit_set.
532  */
intf_get_displayed_unit(lua_State * L)533 int game_lua_kernel::intf_get_displayed_unit(lua_State *L)
534 {
535 	if (!game_display_) {
536 		return 0;
537 	}
538 
539 	unit_map::const_iterator ui = board().find_visible_unit(
540 		game_display_->displayed_unit_hex(),
541 		teams()[game_display_->viewing_team()],
542 		game_display_->show_everything());
543 	if (!ui.valid()) return 0;
544 
545 	luaW_pushunit(L, ui->underlying_id());
546 	return 1;
547 }
548 
549 /**
550  * Gets all the units matching a given filter.
551  * - Arg 1: optional table containing a filter
552  * - Arg 2: optional location (to find all units that would match on that location)
553                   OR unit (to find all units that would match adjacent to that unit)
554  * - Ret 1: table containing full userdata with __index pointing to
555  *          impl_unit_get and __newindex pointing to impl_unit_set.
556  */
intf_get_units(lua_State * L)557 int game_lua_kernel::intf_get_units(lua_State *L)
558 {
559 	vconfig filter = luaW_checkvconfig(L, 1, true);
560 
561 	unit_filter filt(filter);
562 	std::vector<const unit*> units;
563 
564 	if(unit* u_adj = luaW_tounit(L, 2)) {
565 		if(!u_adj) {
566 			return luaL_argerror(L, 2, "unit not found");
567 		}
568 		units = filt.all_matches_with_unit(*u_adj);
569 	} else if(!lua_isnoneornil(L, 2)) {
570 		map_location loc;
571 		luaW_tolocation(L, 2, loc);
572 		if(!loc.valid()) {
573 			return luaL_argerror(L, 2, "invalid location");
574 		}
575 		units = filt.all_matches_at(loc);
576 	} else {
577 		units = filt.all_matches_on_map();
578 	}
579 
580 	// Go through all the units while keeping the following stack:
581 	// 1: return table, 2: userdata
582 	lua_settop(L, 0);
583 	lua_newtable(L);
584 	int i = 1;
585 
586 	for (const unit * ui : units) {
587 		luaW_pushunit(L, ui->underlying_id());
588 		lua_rawseti(L, 1, i);
589 		++i;
590 	}
591 	return 1;
592 }
593 
594 /**
595  * Matches a unit against the given filter.
596  * - Arg 1: full userdata.
597  * - Arg 2: table containing a filter
598  * - Arg 3: optional location OR optional "adjacent" unit
599  * - Ret 1: boolean.
600  */
intf_match_unit(lua_State * L)601 int game_lua_kernel::intf_match_unit(lua_State *L)
602 {
603 	lua_unit& u = *luaW_checkunit_ref(L, 1);
604 
605 	vconfig filter = luaW_checkvconfig(L, 2, true);
606 
607 	if (filter.null()) {
608 		lua_pushboolean(L, true);
609 		return 1;
610 	}
611 
612 	if(unit* u_adj = luaW_tounit(L, 3)) {
613 		if(int side = u.on_recall_list()) {
614 			WRN_LUA << "wesnoth.match_unit called with a secondary unit (3rd argument), ";
615 			WRN_LUA << "but unit to match was on recall list. ";
616 			WRN_LUA << "Thus the 3rd argument is ignored.\n";
617 			team &t = board().get_team(side);
618 			scoped_recall_unit auto_store("this_unit", t.save_id_or_number(), t.recall_list().find_index(u->id()));
619 			lua_pushboolean(L, unit_filter(filter).matches(*u, map_location()));
620 			return 1;
621 		}
622 		if (!u_adj) {
623 			return luaL_argerror(L, 3, "unit not found");
624 		}
625 		lua_pushboolean(L, unit_filter(filter).matches(*u, *u_adj));
626 	} else if(int side = u.on_recall_list()) {
627 		map_location loc;
628 		luaW_tolocation(L, 3, loc); // If argument 3 isn't a location, loc is unchanged
629 		team &t = board().get_team(side);
630 		scoped_recall_unit auto_store("this_unit", t.save_id_or_number(), t.recall_list().find_index(u->id()));
631 		lua_pushboolean(L, unit_filter(filter).matches(*u, loc));
632 		return 1;
633 	} else {
634 		map_location loc = u->get_location();
635 		luaW_tolocation(L, 3, loc); // If argument 3 isn't a location, loc is unchanged
636 		lua_pushboolean(L, unit_filter(filter).matches(*u, loc));
637 	}
638 	return 1;
639 }
640 
641 /**
642  * Gets the numeric ids of all the units matching a given filter on the recall lists.
643  * - Arg 1: optional table containing a filter
644  * - Ret 1: table containing full userdata with __index pointing to
645  *          impl_unit_get and __newindex pointing to impl_unit_set.
646  */
intf_get_recall_units(lua_State * L)647 int game_lua_kernel::intf_get_recall_units(lua_State *L)
648 {
649 	vconfig filter = luaW_checkvconfig(L, 1, true);
650 
651 	// Go through all the units while keeping the following stack:
652 	// 1: return table, 2: userdata
653 	lua_settop(L, 0);
654 	lua_newtable(L);
655 	int i = 1, s = 1;
656 	const unit_filter ufilt(filter);
657 	for (team &t : teams())
658 	{
659 		for (unit_ptr & u : t.recall_list())
660 		{
661 			if (!filter.null()) {
662 				scoped_recall_unit auto_store("this_unit",
663 					t.save_id_or_number(), t.recall_list().find_index(u->id()));
664 				if (!ufilt( *u, map_location() ))
665 					continue;
666 			}
667 			luaW_pushunit(L, s, u->underlying_id());
668 			lua_rawseti(L, 1, i);
669 			++i;
670 		}
671 		++s;
672 	}
673 	return 1;
674 }
675 
676 /**
677  * Fires an event.
678  * - Arg 1: string containing the event name or id.
679  * - Arg 2: optional first location.
680  * - Arg 3: optional second location.
681  * - Arg 4: optional WML table used as the [weapon] tag.
682  * - Arg 5: optional WML table used as the [second_weapon] tag.
683  * - Ret 1: boolean indicating whether the event was processed or not.
684  */
intf_fire_event(lua_State * L,const bool by_id)685 int game_lua_kernel::intf_fire_event(lua_State *L, const bool by_id)
686 {
687 	char const *m = luaL_checkstring(L, 1);
688 
689 	int pos = 2;
690 	map_location l1, l2;
691 	config data;
692 
693 	if (luaW_tolocation(L, 2, l1)) {
694 		if (luaW_tolocation(L, 3, l2)) {
695 			pos = 4;
696 		} else {
697 			pos = 3;
698 		}
699 	}
700 
701 	if (!lua_isnoneornil(L, pos)) {
702 		data.add_child("first", luaW_checkconfig(L, pos));
703 	}
704 	++pos;
705 	if (!lua_isnoneornil(L, pos)) {
706 		data.add_child("second", luaW_checkconfig(L, pos));
707 	}
708 
709 	bool b = false;
710 
711 	if (by_id) {
712 	  b = std::get<0>(play_controller_.pump().fire("", m, l1, l2, data));
713 	}
714 	else {
715 	  b = std::get<0>(play_controller_.pump().fire(m, l1, l2, data));
716 	}
717 	lua_pushboolean(L, b);
718 	return 1;
719 }
720 
721 
722 /**
723  * Fires a wml menu item.
724  * - Arg 1: id of the item. it is not possible to fire items that don't have ids with this function.
725  * - Arg 2: optional first location.
726  * - Ret 1: boolean, true indicating that the event was fired successfully
727  *
728  * NOTE: This is not an "official" feature, it may currently cause assertion failures if used with
729  * menu items which have "needs_select". It is not supported right now to use it this way.
730  * The purpose of this function right now is to make it possible to have automated sanity tests for
731  * the wml menu items system.
732  */
intf_fire_wml_menu_item(lua_State * L)733 int game_lua_kernel::intf_fire_wml_menu_item(lua_State *L)
734 {
735 	char const *m = luaL_checkstring(L, 1);
736 
737 	map_location l1 = luaW_checklocation(L, 2);
738 
739 	bool b = game_state_.get_wml_menu_items().fire_item(m, l1, gamedata(), game_state_, units());
740 	lua_pushboolean(L, b);
741 	return 1;
742 }
743 
744 /**
745  * Gets a WML variable.
746  * - Arg 1: string containing the variable name.
747  * - Arg 2: optional bool indicating if tables for containers should be left empty.
748  * - Ret 1: value of the variable, if any.
749  */
intf_get_variable(lua_State * L)750 int game_lua_kernel::intf_get_variable(lua_State *L)
751 {
752 	char const *m = luaL_checkstring(L, 1);
753 	variable_access_const v = gamedata().get_variable_access_read(m);
754 	return luaW_pushvariable(L, v) ? 1 : 0;
755 }
756 
757 /**
758  * Gets a side specific WML variable.
759  * - Arg 1: integer side number.
760  * - Arg 2: string containing the variable name.
761  * - Ret 1: value of the variable, if any.
762  */
intf_get_side_variable(lua_State * L)763 int game_lua_kernel::intf_get_side_variable(lua_State *L)
764 {
765 
766 	unsigned side_index = luaL_checkinteger(L, 1) - 1;
767 	if(side_index >= teams().size()) {
768 		return luaL_argerror(L, 1, "invalid side number");
769 	}
770 	char const *m = luaL_checkstring(L, 2);
771 	variable_access_const v(m, teams()[side_index].variables());
772 	return luaW_pushvariable(L, v) ? 1 : 0;
773 }
774 
775 /**
776  * Gets a side specific WML variable.
777  * - Arg 1: integer side number.
778  * - Arg 2: string containing the variable name.
779  * - Arg 3: boolean/integer/string/table containing the value.
780  */
intf_set_side_variable(lua_State * L)781 int game_lua_kernel::intf_set_side_variable(lua_State *L)
782 {
783 	unsigned side = luaL_checkinteger(L, 1);
784 	if(side > teams().size() || side == 0) {
785 		return luaL_argerror(L, 1, "invalid side number");
786 	}
787 	char const *m = luaL_checkstring(L, 2);
788 	config& vars = game_state_.board_.get_team(side).variables();
789 	if(lua_isnoneornil(L, 3)) {
790 		try {
791 			variable_access_throw(m, vars).clear(false);
792 		} catch(const invalid_variablename_exception&) {
793 		}
794 		return 0;
795 	}
796 	variable_access_create v(m, vars);
797 	luaW_checkvariable(L, v, 3);
798 	return 0;
799 }
800 
801 /**
802  * Sets a WML variable.
803  * - Arg 1: string containing the variable name.
804  * - Arg 2: boolean/integer/string/table containing the value.
805  */
intf_set_variable(lua_State * L)806 int game_lua_kernel::intf_set_variable(lua_State *L)
807 {
808 	const std::string m = luaL_checkstring(L, 1);
809 	if(m.empty()) return luaL_argerror(L, 1, "empty variable name");
810 	if (lua_isnoneornil(L, 2)) {
811 		gamedata().clear_variable(m);
812 		return 0;
813 	}
814 	variable_access_create v = gamedata().get_variable_access_write(m);
815 	luaW_checkvariable(L, v, 2);
816 	return 0;
817 }
818 
intf_set_menu_item(lua_State * L)819 int game_lua_kernel::intf_set_menu_item(lua_State *L)
820 {
821 	game_state_.get_wml_menu_items().set_item(luaL_checkstring(L, 1), luaW_checkvconfig(L,2));
822 	return 0;
823 }
824 
intf_clear_menu_item(lua_State * L)825 int game_lua_kernel::intf_clear_menu_item(lua_State *L)
826 {
827 	std::string ids(luaL_checkstring(L, 1));
828 	for(const std::string& id : utils::split(ids, ',', utils::STRIP_SPACES)) {
829 		if(id.empty()) {
830 			WRN_LUA << "[clear_menu_item] has been given an empty id=, ignoring" << std::endl;
831 			continue;
832 		}
833 		game_state_.get_wml_menu_items().erase(id);
834 	}
835 	return 0;
836 }
837 
intf_set_end_campaign_credits(lua_State * L)838 int game_lua_kernel::intf_set_end_campaign_credits(lua_State *L)
839 {
840 	game_classification &classification = play_controller_.get_classification();
841 	classification.end_credits = luaW_toboolean(L, 1);
842 	return 0;
843 }
844 
intf_set_end_campaign_text(lua_State * L)845 int game_lua_kernel::intf_set_end_campaign_text(lua_State *L)
846 {
847 	game_classification &classification = play_controller_.get_classification();
848 	classification.end_text = luaW_checktstring(L, 1);
849 	if (lua_isnumber(L, 2)) {
850 		classification.end_text_duration = static_cast<int> (lua_tonumber(L, 2));
851 	}
852 
853 	return 0;
854 }
855 
intf_set_next_scenario(lua_State * L)856 int game_lua_kernel::intf_set_next_scenario(lua_State *L)
857 {
858 	deprecated_message("wesnoth.set_next_scenario", DEP_LEVEL::INDEFINITE, "");
859 	gamedata().set_next_scenario(luaL_checkstring(L, 1));
860 	return 0;
861 }
862 
intf_shroud_op(lua_State * L,bool place_shroud)863 int game_lua_kernel::intf_shroud_op(lua_State *L, bool place_shroud)
864 {
865 
866 	int side_num = luaL_checkinteger(L, 1);
867 
868 	if(lua_isstring(L, 2)) {
869 		std::string data = lua_tostring(L, 2);
870 		// Special case - using a shroud_data string, or "all"
871 		team& side = board().get_team(side_num);
872 		if(place_shroud) {
873 			side.reshroud();
874 		}
875 		if(data != "all") {
876 			side.merge_shroud_map_data(data);
877 		} else if(!place_shroud) {
878 			bool was_shrouded = side.uses_shroud();
879 			side.set_shroud(false);
880 			actions::clear_shroud(side.side());
881 			side.set_shroud(was_shrouded);
882 		}
883 		return 0;
884 	} else if(lua_istable(L, 2)) {
885 		std::vector<map_location> locs_v = lua_check<std::vector<map_location>>(L, 2);
886 		std::set<map_location> locs(locs_v.begin(), locs_v.end());
887 		team &t = board().get_team(side_num);
888 
889 		for (const map_location& loc : locs)
890 		{
891 			if (place_shroud) {
892 				t.place_shroud(loc);
893 			} else {
894 				t.clear_shroud(loc);
895 			}
896 		}
897 	} else {
898 		return luaL_argerror(L, 2, "expected list of locations or shroud data string");
899 	}
900 
901 	game_display_->labels().recalculate_shroud();
902 	game_display_->recalculate_minimap();
903 	game_display_->invalidate_all();
904 
905 	return 0;
906 }
907 
908 
909 /**
910  * Highlights the given location on the map.
911  * - Arg 1: location.
912  */
intf_highlight_hex(lua_State * L)913 int game_lua_kernel::intf_highlight_hex(lua_State *L)
914 {
915 	if (!game_display_) {
916 		return 0;
917 	}
918 
919 	const map_location loc = luaW_checklocation(L, 1);
920 	if(!map().on_board(loc)) return luaL_argerror(L, 1, "not on board");
921 	game_display_->highlight_hex(loc);
922 	game_display_->display_unit_hex(loc);
923 
924 	return 0;
925 }
926 
927 /**
928  * Returns whether the first side is an enemy of the second one.
929  * - Args 1,2: side numbers.
930  * - Ret 1: boolean.
931  */
intf_is_enemy(lua_State * L)932 int game_lua_kernel::intf_is_enemy(lua_State *L)
933 {
934 	unsigned side_1 = luaL_checkinteger(L, 1) - 1;
935 	unsigned side_2 = luaL_checkinteger(L, 2) - 1;
936 	if (side_1 >= teams().size() || side_2 >= teams().size()) return 0;
937 	lua_pushboolean(L, teams()[side_1].is_enemy(side_2 + 1));
938 	return 1;
939 }
940 
941 /**
942  * Gets whether gamemap scrolling is disabled for the user.
943  * - Ret 1: boolean.
944  */
intf_view_locked(lua_State * L)945 int game_lua_kernel::intf_view_locked(lua_State *L)
946 {
947 	if (!game_display_) {
948 		return 0;
949 	}
950 
951 	lua_pushboolean(L, game_display_->view_locked());
952 	return 1;
953 }
954 
955 /**
956  * Sets whether gamemap scrolling is disabled for the user.
957  * - Arg 1: boolean, specifying the new locked/unlocked status.
958  */
intf_lock_view(lua_State * L)959 int game_lua_kernel::intf_lock_view(lua_State *L)
960 {
961 	bool lock = luaW_toboolean(L, 1);
962 	if (game_display_) {
963 		game_display_->set_view_locked(lock);
964 	}
965 	return 0;
966 }
967 
968 /**
969  * Gets a terrain code.
970  * - Arg 1: map location.
971  * - Ret 1: string.
972  */
intf_get_terrain(lua_State * L)973 int game_lua_kernel::intf_get_terrain(lua_State *L)
974 {
975 	map_location loc = luaW_checklocation(L, 1);
976 
977 	const t_translation::terrain_code& t = board().map().
978 		get_terrain(loc);
979 	lua_pushstring(L, t_translation::write_terrain_code(t).c_str());
980 	return 1;
981 }
982 
983 /**
984  * Sets a terrain code.
985  * - Arg 1: map location.
986  * - Arg 2: terrain code string.
987  * - Arg 3: layer: (overlay|base|both, default=both)
988  * - Arg 4: replace_if_failed, default = no
989  */
intf_set_terrain(lua_State * L)990 int game_lua_kernel::intf_set_terrain(lua_State *L)
991 {
992 	map_location loc = luaW_checklocation(L, 1);
993 	std::string t_str(luaL_checkstring(L, 2));
994 
995 	std::string mode_str = "both";
996 	bool replace_if_failed = false;
997 	if (!lua_isnone(L, 3)) {
998 		if (!lua_isnil(L, 3)) {
999 			mode_str = luaL_checkstring(L, 3);
1000 		}
1001 
1002 		if(!lua_isnoneornil(L, 4)) {
1003 			replace_if_failed = luaW_toboolean(L, 4);
1004 		}
1005 	}
1006 
1007 	bool result = board().change_terrain(loc, t_str, mode_str, replace_if_failed);
1008 
1009 	if (game_display_) {
1010 		game_display_->needs_rebuild(result);
1011 	}
1012 
1013 	return 0;
1014 }
1015 
1016 /**
1017  * Gets details about a terrain.
1018  * - Arg 1: terrain code string.
1019  * - Ret 1: table.
1020  */
intf_get_terrain_info(lua_State * L)1021 int game_lua_kernel::intf_get_terrain_info(lua_State *L)
1022 {
1023 	char const *m = luaL_checkstring(L, 1);
1024 	t_translation::terrain_code t = t_translation::read_terrain_code(m);
1025 	if (t == t_translation::NONE_TERRAIN) return 0;
1026 	const terrain_type& info = board().map().tdata()->get_terrain_info(t);
1027 
1028 	lua_newtable(L);
1029 	lua_pushstring(L, info.id().c_str());
1030 	lua_setfield(L, -2, "id");
1031 	luaW_pushtstring(L, info.name());
1032 	lua_setfield(L, -2, "name");
1033 	luaW_pushtstring(L, info.editor_name());
1034 	lua_setfield(L, -2, "editor_name");
1035 	luaW_pushtstring(L, info.description());
1036 	lua_setfield(L, -2, "description");
1037 	lua_push(L, info.icon_image());
1038 	lua_setfield(L, -2, "icon");
1039 	lua_push(L, info.editor_image());
1040 	lua_setfield(L, -2, "editor_image");
1041 	lua_pushinteger(L, info.light_bonus(0));
1042 	lua_setfield(L, -2, "light");
1043 	lua_pushboolean(L, info.is_village());
1044 	lua_setfield(L, -2, "village");
1045 	lua_pushboolean(L, info.is_castle());
1046 	lua_setfield(L, -2, "castle");
1047 	lua_pushboolean(L, info.is_keep());
1048 	lua_setfield(L, -2, "keep");
1049 	lua_pushinteger(L, info.gives_healing());
1050 	lua_setfield(L, -2, "healing");
1051 
1052 	return 1;
1053 }
1054 
1055 /**
1056  * Gets time of day information.
1057  * - Arg 1: optional turn number
1058  * - Arg 2: optional location
1059  * - Arg 3: optional boolean (consider_illuminates)
1060  * - Ret 1: table.
1061  */
intf_get_time_of_day(lua_State * L)1062 int game_lua_kernel::intf_get_time_of_day(lua_State *L)
1063 {
1064 	unsigned arg = 1;
1065 
1066 	int for_turn = tod_man().turn();
1067 	map_location loc = map_location();
1068 	bool consider_illuminates = false;
1069 
1070 	if(lua_isnumber(L, arg)) {
1071 		++arg;
1072 		for_turn = luaL_checkinteger(L, 1);
1073 		int number_of_turns = tod_man().number_of_turns();
1074 		if(for_turn < 1 || (number_of_turns != -1 && for_turn > number_of_turns)) {
1075 			return luaL_argerror(L, 1, "turn number out of range");
1076 		}
1077 	}
1078 	else if(lua_isnil(L, arg)) ++arg;
1079 
1080 	if(luaW_tolocation(L, arg, loc)) {
1081 		if(!board().map().on_board(loc)) return luaL_argerror(L, arg, "coordinates are not on board");
1082 
1083 		if(lua_istable(L, arg)) {
1084 			lua_rawgeti(L, arg, 3);
1085 			consider_illuminates = luaW_toboolean(L, -1);
1086 			lua_pop(L, 1);
1087 		} else if(lua_isboolean(L, arg + 1)) {
1088 			consider_illuminates = luaW_toboolean(L, arg + 1);
1089 		}
1090 	}
1091 
1092 	const time_of_day& tod = consider_illuminates ?
1093 		tod_man().get_illuminated_time_of_day(board().units(), board().map(), loc, for_turn) :
1094 		tod_man().get_time_of_day(loc, for_turn);
1095 
1096 	lua_newtable(L);
1097 	lua_pushstring(L, tod.id.c_str());
1098 	lua_setfield(L, -2, "id");
1099 	lua_pushinteger(L, tod.lawful_bonus);
1100 	lua_setfield(L, -2, "lawful_bonus");
1101 	lua_pushinteger(L, tod.bonus_modified);
1102 	lua_setfield(L, -2, "bonus_modified");
1103 	lua_pushstring(L, tod.image.c_str());
1104 	lua_setfield(L, -2, "image");
1105 	luaW_pushtstring(L, tod.name);
1106 	lua_setfield(L, -2, "name");
1107 
1108 	lua_pushinteger(L, tod.color.r);
1109 	lua_setfield(L, -2, "red");
1110 	lua_pushinteger(L, tod.color.g);
1111 	lua_setfield(L, -2, "green");
1112 	lua_pushinteger(L, tod.color.b);
1113 	lua_setfield(L, -2, "blue");
1114 
1115 	return 1;
1116 }
1117 
1118 /**
1119  * Gets the side of a village owner.
1120  * - Arg 1: map location.
1121  * - Ret 1: integer.
1122  */
intf_get_village_owner(lua_State * L)1123 int game_lua_kernel::intf_get_village_owner(lua_State *L)
1124 {
1125 	map_location loc = luaW_checklocation(L, 1);
1126 	if (!board().map().is_village(loc))
1127 		return 0;
1128 
1129 	int side = board().village_owner(loc) + 1;
1130 	if (!side) return 0;
1131 	lua_pushinteger(L, side);
1132 	return 1;
1133 }
1134 
1135 /**
1136  * Sets the owner of a village.
1137  * - Arg 1: map location.
1138  * - Arg 2: integer for the side or empty to remove ownership.
1139  */
intf_set_village_owner(lua_State * L)1140 int game_lua_kernel::intf_set_village_owner(lua_State *L)
1141 {
1142 	map_location loc = luaW_checklocation(L, 1);
1143 	if(!board().map().is_village(loc)) {
1144 		return 0;
1145 	}
1146 
1147 	const int old_side_num = board().village_owner(loc) + 1;
1148 	const int new_side_num = lua_isnoneornil(L, 2) ? 0 : luaL_checkinteger(L, 2);
1149 
1150 	team* old_side = nullptr;
1151 	team* new_side = nullptr;
1152 
1153 	if(old_side_num == new_side_num) {
1154 		return 0;
1155 	}
1156 
1157 	try {
1158 		old_side = &board().get_team(old_side_num);
1159 	} catch(const std::out_of_range&) {
1160 		// old_side_num is invalid, most likely because the village wasn't captured.
1161 		old_side = nullptr;
1162 	}
1163 
1164 	try {
1165 		new_side = &board().get_team(new_side_num);
1166 	} catch(const std::out_of_range&) {
1167 		// new_side_num is invalid.
1168 		new_side = nullptr;
1169 	}
1170 
1171 	// The new side was valid, but already defeated. Do nothing.
1172 	if(new_side && board().team_is_defeated(*new_side)) {
1173 		return 0;
1174 	}
1175 
1176 	// Even if the new side is not valid, we still want to remove the village from the old side.
1177 	// This covers the case where new_side_num equals 0. The behavior in that case is to simply
1178 	// un-assign the village from the old side, which of course we also want to happen if the new
1179 	// side IS valid. If the village in question hadn't been captured, this won't fire (old_side
1180 	// will be a nullptr).
1181 	if(old_side) {
1182 		old_side->lose_village(loc);
1183 	}
1184 
1185 	// If the new side was valid, re-assign the village.
1186 	if(new_side) {
1187 		new_side->get_village(loc, old_side_num, (luaW_toboolean(L, 3) ? &gamedata() : nullptr));
1188 	}
1189 
1190 	return 0;
1191 }
1192 
1193 
1194 /**
1195  * Returns the map size.
1196  * - Ret 1: width.
1197  * - Ret 2: height.
1198  * - Ret 3: border size.
1199  */
intf_get_map_size(lua_State * L)1200 int game_lua_kernel::intf_get_map_size(lua_State *L)
1201 {
1202 	const gamemap &map = board().map();
1203 	lua_pushinteger(L, map.w());
1204 	lua_pushinteger(L, map.h());
1205 	lua_pushinteger(L, map.border_size());
1206 	return 3;
1207 }
1208 
1209 /**
1210  * Returns the currently overed tile.
1211  * - Ret 1: x.
1212  * - Ret 2: y.
1213  */
intf_get_mouseover_tile(lua_State * L)1214 int game_lua_kernel::intf_get_mouseover_tile(lua_State *L)
1215 {
1216 	if (!game_display_) {
1217 		return 0;
1218 	}
1219 
1220 	const map_location &loc = game_display_->mouseover_hex();
1221 	if (!board().map().on_board(loc)) return 0;
1222 	lua_pushinteger(L, loc.wml_x());
1223 	lua_pushinteger(L, loc.wml_y());
1224 	return 2;
1225 }
1226 
1227 /**
1228  * Returns the currently selected tile.
1229  * - Ret 1: x.
1230  * - Ret 2: y.
1231  */
intf_get_selected_tile(lua_State * L)1232 int game_lua_kernel::intf_get_selected_tile(lua_State *L)
1233 {
1234 	if (!game_display_) {
1235 		return 0;
1236 	}
1237 
1238 	const map_location &loc = game_display_->selected_hex();
1239 	if (!board().map().on_board(loc)) return 0;
1240 	lua_pushinteger(L, loc.wml_x());
1241 	lua_pushinteger(L, loc.wml_y());
1242 	return 2;
1243 }
1244 
1245 /**
1246  * Returns the starting position of a side.
1247  * Arg 1: side number
1248  * Ret 1: table with unnamed indices holding wml coordinates x and y
1249 */
intf_get_starting_location(lua_State * L)1250 int game_lua_kernel::intf_get_starting_location(lua_State* L)
1251 {
1252 	const int side = luaL_checkinteger(L, 1);
1253 	if(side < 1 || static_cast<int>(teams().size()) < side)
1254 		return luaL_argerror(L, 1, "out of bounds");
1255 	const map_location& starting_pos = board().map().starting_position(side);
1256 	if(!board().map().on_board(starting_pos)) return 0;
1257 
1258 	luaW_pushlocation(L, starting_pos);
1259 	return 1;
1260 }
1261 
1262 /**
1263  * Gets a table for an era tag.
1264  * - Arg 1: userdata (ignored).
1265  * - Arg 2: string containing id of the desired era
1266  * - Ret 1: config for the era
1267  */
intf_get_era(lua_State * L)1268 static int intf_get_era(lua_State *L)
1269 {
1270 	char const *m = luaL_checkstring(L, 1);
1271 	luaW_pushconfig(L, game_config_manager::get()->game_config().find_child("era","id",m));
1272 	return 1;
1273 }
1274 
1275 /**
1276  * Gets some game_config data (__index metamethod).
1277  * - Arg 1: userdata (ignored).
1278  * - Arg 2: string containing the name of the property.
1279  * - Ret 1: something containing the attribute.
1280  */
impl_game_config_get(lua_State * L)1281 int game_lua_kernel::impl_game_config_get(lua_State *L)
1282 {
1283 	LOG_LUA << "impl_game_config_get\n";
1284 	char const *m = luaL_checkstring(L, 2);
1285 
1286 	// Find the corresponding attribute.
1287 	return_int_attrib("last_turn", tod_man().number_of_turns());
1288 	return_string_attrib("next_scenario", gamedata().next_scenario());
1289 	return_string_attrib("theme", gamedata().get_theme());
1290 	return_string_attrib("scenario_id", gamedata().get_id());
1291 	return_vector_string_attrib("defeat_music", gamedata().get_defeat_music());
1292 	return_vector_string_attrib("victory_music", gamedata().get_victory_music());
1293 
1294 	const mp_game_settings& mp_settings = play_controller_.get_mp_settings();
1295 	const game_classification & classification = play_controller_.get_classification();
1296 
1297 	return_string_attrib("campaign_type", classification.campaign_type.to_string());
1298 	if(classification.campaign_type==game_classification::CAMPAIGN_TYPE::MULTIPLAYER) {
1299 		return_cfgref_attrib("mp_settings", mp_settings.to_config());
1300 		return_cfgref_attrib("era", game_config_manager::get()->game_config().find_child("era","id",mp_settings.mp_era));
1301 		//^ finds the era with name matching mp_era, and creates a lua reference from the config of that era.
1302 
1303 		//This code for SigurdFD, not the cleanest implementation but seems to work just fine.
1304 		config::const_child_itors its = game_config_manager::get()->game_config().child_range("era");
1305 		std::string eras_list(its.front()["id"]);
1306 		its.pop_front();
1307 		for(const auto& cfg : its) {
1308 			eras_list = eras_list + "," + cfg["id"];
1309 		}
1310 		return_string_attrib("eras", eras_list);
1311 	}
1312 	return lua_kernel_base::impl_game_config_get(L);
1313 }
1314 
1315 /**
1316  * Sets some game_config data (__newindex metamethod).
1317  * - Arg 1: userdata (ignored).
1318  * - Arg 2: string containing the name of the property.
1319  * - Arg 3: something containing the attribute.
1320  */
impl_game_config_set(lua_State * L)1321 int game_lua_kernel::impl_game_config_set(lua_State *L)
1322 {
1323 	LOG_LUA << "impl_game_config_set\n";
1324 	char const *m = luaL_checkstring(L, 2);
1325 
1326 	// Find the corresponding attribute.
1327 	modify_int_attrib("base_income", game_config::base_income = value);
1328 	modify_int_attrib("village_income", game_config::village_income = value);
1329 	modify_int_attrib("village_support", game_config::village_support = value);
1330 	modify_int_attrib("poison_amount", game_config::poison_amount = value);
1331 	modify_int_attrib("rest_heal_amount", game_config::rest_heal_amount = value);
1332 	modify_int_attrib("recall_cost", game_config::recall_cost = value);
1333 	modify_int_attrib("kill_experience", game_config::kill_experience = value);
1334 	modify_int_attrib("last_turn", tod_man().set_number_of_turns_by_wml(value));
1335 	modify_string_attrib("next_scenario", gamedata().set_next_scenario(value));
1336 	modify_string_attrib("theme",
1337 		gamedata().set_theme(value);
1338 		const config& game_config = game_config_manager::get()->game_config();
1339 		game_display_->set_theme(play_controller_.get_theme(game_config, value));
1340 	);
1341 	modify_vector_string_attrib("defeat_music", gamedata().set_defeat_music(std::move(value)));
1342 	modify_vector_string_attrib("victory_music", gamedata().set_victory_music(std::move(value)));
1343 	return lua_kernel_base::impl_game_config_set(L);
1344 }
1345 
1346 /**
1347 	converts synced_context::get_synced_state() to a string.
1348 */
synced_state()1349 std::string game_lua_kernel::synced_state()
1350 {
1351 	//maybe return "initial" for game_data::INITIAL?
1352 	if(gamedata().phase() == game_data::PRELOAD || gamedata().phase() == game_data::INITIAL)
1353 	{
1354 		return "preload";
1355 	}
1356 	switch(synced_context::get_synced_state())
1357 	{
1358 	case synced_context::LOCAL_CHOICE:
1359 		return "local_choice";
1360 	case synced_context::SYNCED:
1361 		return "synced";
1362 	case synced_context::UNSYNCED:
1363 		return "unsynced";
1364 	default:
1365 		throw game::game_error("Found corrupt synced_context::synced_state");
1366 	}
1367 }
1368 
1369 
1370 /**
1371  * Gets some data about current point of game (__index metamethod).
1372  * - Arg 1: userdata (ignored).
1373  * - Arg 2: string containing the name of the property.
1374  * - Ret 1: something containing the attribute.
1375  */
impl_current_get(lua_State * L)1376 int game_lua_kernel::impl_current_get(lua_State *L)
1377 {
1378 	char const *m = luaL_checkstring(L, 2);
1379 
1380 	// Find the corresponding attribute.
1381 	return_int_attrib("side", play_controller_.current_side());
1382 	return_int_attrib("turn", play_controller_.turn());
1383 	return_string_attrib("synced_state", synced_state());
1384 	return_bool_attrib("user_can_invoke_commands", !play_controller_.is_lingering() && play_controller_.gamestate().init_side_done() && !events::commands_disabled && gamedata().phase() == game_data::PLAY);
1385 
1386 	if (strcmp(m, "event_context") == 0)
1387 	{
1388 		const game_events::queued_event &ev = get_event_info();
1389 		config cfg;
1390 		cfg["name"] = ev.name;
1391 		cfg["id"]   = ev.id;
1392 		if (const config &weapon = ev.data.child("first")) {
1393 			cfg.add_child("weapon", weapon);
1394 		}
1395 		if (const config &weapon = ev.data.child("second")) {
1396 			cfg.add_child("second_weapon", weapon);
1397 		}
1398 
1399 		const config::attribute_value di = ev.data["damage_inflicted"];
1400 		if(!di.empty()) {
1401 			cfg["damage_inflicted"] = di;
1402 		}
1403 
1404 		if (ev.loc1.valid()) {
1405 			cfg["x1"] = ev.loc1.filter_loc().wml_x();
1406 			cfg["y1"] = ev.loc1.filter_loc().wml_y();
1407 			// 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
1408 			cfg["unit_x"] = ev.loc1.wml_x();
1409 			cfg["unit_y"] = ev.loc1.wml_y();
1410 		}
1411 		if (ev.loc2.valid()) {
1412 			cfg["x2"] = ev.loc2.filter_loc().wml_x();
1413 			cfg["y2"] = ev.loc2.filter_loc().wml_y();
1414 		}
1415 		luaW_pushconfig(L, cfg);
1416 		return 1;
1417 	}
1418 
1419 	return 0;
1420 }
1421 
1422 /**
1423  * Displays a message in the chat window and in the logs.
1424  * - Arg 1: optional message header.
1425  * - Arg 2 (or 1): message.
1426  */
intf_message(lua_State * L)1427 int game_lua_kernel::intf_message(lua_State *L)
1428 {
1429 	t_string m = luaW_checktstring(L, 1);
1430 	t_string h = m;
1431 	if (lua_isnone(L, 2)) {
1432 		h = "Lua";
1433 	} else {
1434 		m = luaW_checktstring(L, 2);
1435 	}
1436 	lua_chat(h, m);
1437 	LOG_LUA << "Script says: \"" << m << "\"\n";
1438 	return 0;
1439 }
1440 
intf_open_help(lua_State * L)1441 int game_lua_kernel::intf_open_help(lua_State *L)
1442 {
1443 	if (game_display_) {
1444 		help::show_help(luaL_checkstring(L, 1));
1445 	}
1446 	return 0;
1447 }
1448 
intf_zoom(lua_State * L)1449 int game_lua_kernel::intf_zoom(lua_State* L)
1450 {
1451 	if(!game_display_) {
1452 		return 0;
1453 	}
1454 	double factor = luaL_checknumber(L, 1);
1455 	bool relative = luaW_toboolean(L, 2);
1456 	if(relative) {
1457 		factor *= game_display_->get_zoom_factor();
1458 	}
1459 	// Passing true explicitly to avoid casting to int.
1460 	// Without doing one of the two, the call is ambiguous.
1461 	game_display_->set_zoom(factor * game_config::tile_size, true);
1462 	lua_pushnumber(L, game_display_->get_zoom_factor());
1463 	return 1;
1464 }
1465 
1466 /**
1467  * Removes all messages from the chat window.
1468  */
intf_clear_messages(lua_State *)1469 int game_lua_kernel::intf_clear_messages(lua_State*)
1470 {
1471 	if (game_display_) {
1472 		game_display_->get_chat_manager().clear_chat_messages();
1473 	}
1474 	return 0;
1475 }
1476 
impl_end_level_data_get(lua_State * L)1477 static int impl_end_level_data_get(lua_State* L)
1478 {
1479 	const end_level_data& data = *static_cast<end_level_data*>(lua_touserdata(L, 1));
1480 	const char* m = luaL_checkstring(L, 2);
1481 
1482 	return_bool_attrib("linger_mode", data.transient.linger_mode);
1483 	return_bool_attrib("reveal_map", data.transient.reveal_map);
1484 	return_bool_attrib("carryover_report", data.transient.carryover_report);
1485 	return_bool_attrib("prescenario_save", data.prescenario_save);
1486 	return_bool_attrib("replay_save", data.replay_save);
1487 	return_bool_attrib("proceed_to_next_level", data.proceed_to_next_level);
1488 	return_bool_attrib("is_victory", data.is_victory);
1489 	return_bool_attrib("is_loss", !data.is_victory);
1490 	return_cstring_attrib("result", data.is_victory ? "victory" : "loss"); // to match wesnoth.end_level()
1491 	return_cfg_attrib("__cfg", data.to_config_full());
1492 
1493 	return 0;
1494 }
1495 
1496 namespace {
1497 	struct end_level_committer {
end_level_committer__anon384f78800211::end_level_committer1498 		end_level_committer(end_level_data& data, play_controller& pc) : data_(data), pc_(pc) {}
~end_level_committer__anon384f78800211::end_level_committer1499 		~end_level_committer() {
1500 			pc_.set_end_level_data(data_);
1501 		}
1502 	private:
1503 		end_level_data& data_;
1504 		play_controller& pc_;
1505 	};
1506 }
1507 
impl_end_level_data_set(lua_State * L)1508 int game_lua_kernel::impl_end_level_data_set(lua_State* L)
1509 {
1510 	end_level_data& data = *static_cast<end_level_data*>(lua_touserdata(L, 1));
1511 	const char* m = luaL_checkstring(L, 2);
1512 	end_level_committer commit(data, play_controller_);
1513 
1514 	modify_bool_attrib("linger_mode", data.transient.linger_mode = value);
1515 	modify_bool_attrib("reveal_map", data.transient.reveal_map = value);
1516 	modify_bool_attrib("carryover_report", data.transient.carryover_report = value);
1517 	modify_bool_attrib("prescenario_save", data.prescenario_save = value);
1518 	modify_bool_attrib("replay_save", data.replay_save = value);
1519 
1520 	return 0;
1521 }
1522 
impl_end_level_data_collect(lua_State * L)1523 static int impl_end_level_data_collect(lua_State* L)
1524 {
1525 	end_level_data* data = static_cast<end_level_data*>(lua_touserdata(L, 1));
1526 	UNUSED(data); // Suppress an erroneous MSVC warning (a destructor call doesn't count as a reference)
1527 	data->~end_level_data();
1528 	return 0;
1529 }
1530 
intf_get_end_level_data(lua_State * L)1531 int game_lua_kernel::intf_get_end_level_data(lua_State* L)
1532 {
1533 	if (!play_controller_.is_regular_game_end()) {
1534 		return 0;
1535 	}
1536 	auto data = play_controller_.get_end_level_data_const();
1537 	new(L) end_level_data(data);
1538 	if(luaL_newmetatable(L, "end level data")) {
1539 		static luaL_Reg const callbacks[] {
1540 			{ "__index", 	    &impl_end_level_data_get},
1541 			{ "__newindex",     &dispatch<&game_lua_kernel::impl_end_level_data_set>},
1542 			{ "__gc",           &impl_end_level_data_collect},
1543 			{ nullptr, nullptr }
1544 		};
1545 		luaL_setfuncs(L, callbacks, 0);
1546 	}
1547 	lua_setmetatable(L, -2);
1548 	return 1;
1549 }
1550 
intf_end_level(lua_State * L)1551 int game_lua_kernel::intf_end_level(lua_State *L)
1552 {
1553 	vconfig cfg(luaW_checkvconfig(L, 1));
1554 	end_level_data data;
1555 
1556 	data.proceed_to_next_level = cfg["proceed_to_next_level"].to_bool(true);
1557 	data.transient.carryover_report = cfg["carryover_report"].to_bool(true);
1558 	data.prescenario_save = cfg["save"].to_bool(true);
1559 	data.replay_save = cfg["replay_save"].to_bool(true);
1560 	data.transient.linger_mode = cfg["linger_mode"].to_bool(true) && !teams().empty();
1561 	data.transient.reveal_map = cfg["reveal_map"].to_bool(true);
1562 	data.is_victory = cfg["result"] == "victory";
1563 	play_controller_.set_end_level_data(data);
1564 	return 0;
1565 }
1566 
intf_end_turn(lua_State * L)1567 int game_lua_kernel::intf_end_turn(lua_State* L)
1568 {
1569 	//note that next_player_number = 1, next_player_number = nteams+1 both set the next team to be the first team
1570 	//but the later will make the turn counter change aswell fire turn end events accoringly etc.
1571 	if (!lua_isnoneornil(L, 1)) {
1572 		int npn = luaL_checknumber(L, 1);
1573 		if (npn <= 0 /*TODO: || npn > 2*nteams*/) {
1574 			return luaL_argerror(L, 1, "side number out of range");
1575 		}
1576 		resources::controller->gamestate().next_player_number_ = npn;
1577 	}
1578 	play_controller_.force_end_turn();
1579 	return 0;
1580 }
1581 
1582 /**
1583  * Evaluates a boolean WML conditional.
1584  * - Arg 1: WML table.
1585  * - Ret 1: boolean.
1586  */
intf_eval_conditional(lua_State * L)1587 static int intf_eval_conditional(lua_State *L)
1588 {
1589 	vconfig cond = luaW_checkvconfig(L, 1);
1590 	bool b = game_events::conditional_passed(cond);
1591 	lua_pushboolean(L, b);
1592 	return 1;
1593 }
1594 
1595 
1596 /**
1597  * Finds a path between two locations.
1598  * - Arg 1: source location. (Or Arg 1: unit.)
1599  * - Arg 2: destination.
1600  * - Arg 3: optional cost function or
1601  *          table (optional fields: ignore_units, ignore_teleport, max_cost, viewing_side).
1602  * - Ret 1: array of pairs containing path steps.
1603  * - Ret 2: path cost.
1604  */
intf_find_path(lua_State * L)1605 int game_lua_kernel::intf_find_path(lua_State *L)
1606 {
1607 	int arg = 1;
1608 	map_location src, dst;
1609 	const unit* u = nullptr;
1610 
1611 	if (lua_isuserdata(L, arg))
1612 	{
1613 		u = &luaW_checkunit(L, arg);
1614 		src = u->get_location();
1615 		++arg;
1616 	}
1617 	else
1618 	{
1619 		src = luaW_checklocation(L, arg);
1620 		unit_map::const_unit_iterator ui = units().find(src);
1621 		if (ui.valid()) {
1622 			u = ui.get_shared_ptr().get();
1623 		}
1624 		++arg;
1625 	}
1626 
1627 	dst = luaW_checklocation(L, arg);
1628 	++arg;
1629 
1630 	if (!board().map().on_board(src))
1631 		return luaL_argerror(L, 1, "invalid location");
1632 	if (!board().map().on_board(dst))
1633 		return luaL_argerror(L, arg - 2, "invalid location");
1634 
1635 	const gamemap &map = board().map();
1636 	int viewing_side = 0;
1637 	bool ignore_units = false, see_all = false, ignore_teleport = false;
1638 	double stop_at = 10000;
1639 	std::unique_ptr<pathfind::cost_calculator> calc;
1640 
1641 	if (lua_istable(L, arg))
1642 	{
1643 		lua_pushstring(L, "ignore_units");
1644 		lua_rawget(L, arg);
1645 		ignore_units = luaW_toboolean(L, -1);
1646 		lua_pop(L, 1);
1647 
1648 		lua_pushstring(L, "ignore_teleport");
1649 		lua_rawget(L, arg);
1650 		ignore_teleport = luaW_toboolean(L, -1);
1651 		lua_pop(L, 1);
1652 
1653 		lua_pushstring(L, "max_cost");
1654 		lua_rawget(L, arg);
1655 		if (!lua_isnil(L, -1))
1656 			stop_at = luaL_checknumber(L, -1);
1657 		lua_pop(L, 1);
1658 
1659 		lua_pushstring(L, "viewing_side");
1660 		lua_rawget(L, arg);
1661 		if (!lua_isnil(L, -1)) {
1662 			int i = luaL_checkinteger(L, -1);
1663 			if (i >= 1 && i <= static_cast<int>(teams().size())) viewing_side = i;
1664 			else see_all = true;
1665 		}
1666 		lua_pop(L, 1);
1667 	}
1668 	else if (lua_isfunction(L, arg))
1669 	{
1670 		calc.reset(new lua_pathfind_cost_calculator(L, arg));
1671 	}
1672 
1673 	pathfind::teleport_map teleport_locations;
1674 
1675 	if (!calc) {
1676 		if (!u) return luaL_argerror(L, 1, "unit not found");
1677 
1678 		const team& viewing_team = viewing_side
1679 			? board().get_team(viewing_side)
1680 			: board().get_team(u->side());
1681 
1682 		if (!ignore_teleport) {
1683 			teleport_locations = pathfind::get_teleport_locations(
1684 				*u, viewing_team, see_all, ignore_units);
1685 		}
1686 		calc.reset(new pathfind::shortest_path_calculator(*u, viewing_team,
1687 			teams(), map, ignore_units, false, see_all));
1688 	}
1689 
1690 	pathfind::plain_route res = pathfind::a_star_search(src, dst, stop_at, *calc, map.w(), map.h(),
1691 		&teleport_locations);
1692 
1693 	int nb = res.steps.size();
1694 	lua_createtable(L, nb, 0);
1695 	for (int i = 0; i < nb; ++i)
1696 	{
1697 		lua_createtable(L, 2, 0);
1698 		lua_pushinteger(L, res.steps[i].wml_x());
1699 		lua_rawseti(L, -2, 1);
1700 		lua_pushinteger(L, res.steps[i].wml_y());
1701 		lua_rawseti(L, -2, 2);
1702 		lua_rawseti(L, -2, i + 1);
1703 	}
1704 	lua_pushinteger(L, res.move_cost);
1705 
1706 	return 2;
1707 }
1708 
1709 /**
1710  * Finds all the locations reachable by a unit.
1711  * - Arg 1: source location OR unit.
1712  * - Arg 2: optional table (optional fields: ignore_units, ignore_teleport, additional_turns, viewing_side).
1713  * - Ret 1: array of triples (coordinates + remaining movement).
1714  */
intf_find_reach(lua_State * L)1715 int game_lua_kernel::intf_find_reach(lua_State *L)
1716 {
1717 	int arg = 1;
1718 	const unit* u = nullptr;
1719 
1720 	if (lua_isuserdata(L, arg))
1721 	{
1722 		u = &luaW_checkunit(L, arg);
1723 		++arg;
1724 	}
1725 	else
1726 	{
1727 		map_location src = luaW_checklocation(L, arg);
1728 		unit_map::const_unit_iterator ui = units().find(src);
1729 		if (!ui.valid())
1730 			return luaL_argerror(L, 1, "unit not found");
1731 		u = ui.get_shared_ptr().get();
1732 		++arg;
1733 	}
1734 
1735 	int viewing_side = 0;
1736 	bool ignore_units = false, see_all = false, ignore_teleport = false;
1737 	int additional_turns = 0;
1738 
1739 	if (lua_istable(L, arg))
1740 	{
1741 		lua_pushstring(L, "ignore_units");
1742 		lua_rawget(L, arg);
1743 		ignore_units = luaW_toboolean(L, -1);
1744 		lua_pop(L, 1);
1745 
1746 		lua_pushstring(L, "ignore_teleport");
1747 		lua_rawget(L, arg);
1748 		ignore_teleport = luaW_toboolean(L, -1);
1749 		lua_pop(L, 1);
1750 
1751 		lua_pushstring(L, "additional_turns");
1752 		lua_rawget(L, arg);
1753 		additional_turns = lua_tointeger(L, -1);
1754 		lua_pop(L, 1);
1755 
1756 		lua_pushstring(L, "viewing_side");
1757 		lua_rawget(L, arg);
1758 		if (!lua_isnil(L, -1)) {
1759 			int i = luaL_checkinteger(L, -1);
1760 			if (i >= 1 && i <= static_cast<int>(teams().size())) viewing_side = i;
1761 			else see_all = true;
1762 		}
1763 		lua_pop(L, 1);
1764 	}
1765 
1766 	const team& viewing_team = viewing_side
1767 		? board().get_team(viewing_side)
1768 		: board().get_team(u->side());
1769 
1770 	pathfind::paths res(*u, ignore_units, !ignore_teleport,
1771 		viewing_team, additional_turns, see_all, ignore_units);
1772 
1773 	int nb = res.destinations.size();
1774 	lua_createtable(L, nb, 0);
1775 	for (int i = 0; i < nb; ++i)
1776 	{
1777 		pathfind::paths::step &s = res.destinations[i];
1778 		lua_createtable(L, 2, 0);
1779 		lua_pushinteger(L, s.curr.wml_x());
1780 		lua_rawseti(L, -2, 1);
1781 		lua_pushinteger(L, s.curr.wml_y());
1782 		lua_rawseti(L, -2, 2);
1783 		lua_pushinteger(L, s.move_left);
1784 		lua_rawseti(L, -2, 3);
1785 		lua_rawseti(L, -2, i + 1);
1786 	}
1787 
1788 	return 1;
1789 }
1790 
intf_find_cost_map_helper(const unit * ptr)1791 static bool intf_find_cost_map_helper(const unit * ptr) {
1792 	return ptr->get_location().valid();
1793 }
1794 
1795 template<typename T> // This is only a template so I can avoid typing out the long typename. >_>
load_fake_units(lua_State * L,int arg,T & fake_units)1796 static int load_fake_units(lua_State* L, int arg, T& fake_units)
1797 {
1798 	for (int i = 1, i_end = lua_rawlen(L, arg); i <= i_end; ++i)
1799 	{
1800 		map_location src;
1801 		lua_rawgeti(L, arg, i);
1802 		int entry = lua_gettop(L);
1803 		if (!lua_istable(L, entry)) {
1804 			goto error;
1805 		}
1806 
1807 		if (!luaW_tolocation(L, entry, src)) {
1808 			goto error;
1809 		}
1810 
1811 		lua_rawgeti(L, entry, 3);
1812 		if (!lua_isnumber(L, -1)) {
1813 			lua_getfield(L, entry, "side");
1814 			if (!lua_isnumber(L, -1)) {
1815 				goto error;
1816 			}
1817 		}
1818 		int side = lua_tointeger(L, -1);
1819 
1820 		lua_rawgeti(L, entry, 4);
1821 		if (!lua_isstring(L, -1)) {
1822 			lua_getfield(L, entry, "type");
1823 			if (!lua_isstring(L, -1)) {
1824 				goto error;
1825 			}
1826 		}
1827 		std::string unit_type = lua_tostring(L, -1);
1828 
1829 		fake_units.emplace_back(src, side, unit_type);
1830 
1831 		lua_settop(L, entry - 1);
1832 	}
1833 	return 0;
1834 error:
1835 	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");
1836 }
1837 
1838 /**
1839  * Is called with one or more units and builds a cost map.
1840  * - Arg 1: source location. (Or Arg 1: unit. Or Arg 1: table containing a filter)
1841  * - Arg 2: optional array of tables with 4 elements (coordinates + side + unit type string)
1842  * - Arg 3: optional table (optional fields: ignore_units, ignore_teleport, viewing_side, debug).
1843  * - Arg 4: optional table: standard location filter.
1844  * - Ret 1: array of triples (coordinates + array of tuples(summed cost + reach counter)).
1845  */
intf_find_cost_map(lua_State * L)1846 int game_lua_kernel::intf_find_cost_map(lua_State *L)
1847 {
1848 	int arg = 1;
1849 	unit* unit = luaW_tounit(L, arg, true);
1850 	vconfig filter = vconfig::unconstructed_vconfig();
1851 	luaW_tovconfig(L, arg, filter);
1852 
1853 	std::vector<const ::unit*> real_units;
1854 	typedef std::vector<std::tuple<map_location, int, std::string>> unit_type_vector;
1855 	unit_type_vector fake_units;
1856 
1857 
1858 	if (unit)  // 1. arg - unit
1859 	{
1860 		real_units.push_back(unit);
1861 	}
1862 	else if (!filter.null())  // 1. arg - filter
1863 	{
1864 		boost::copy(unit_filter(filter).all_matches_on_map() | boost::adaptors::filtered(&intf_find_cost_map_helper), std::back_inserter(real_units));
1865 	}
1866 	else  // 1. arg - coordinates
1867 	{
1868 		map_location src = luaW_checklocation(L, arg);
1869 		unit_map::const_unit_iterator ui = units().find(src);
1870 		if (ui.valid())
1871 		{
1872 			real_units.push_back(&(*ui));
1873 		}
1874 	}
1875 	++arg;
1876 
1877 	if (lua_istable(L, arg))  // 2. arg - optional types
1878 	{
1879 		load_fake_units(L, arg, fake_units);
1880 		++arg;
1881 	}
1882 
1883 	if(real_units.empty() && fake_units.empty())
1884 	{
1885 		return luaL_argerror(L, 1, "unit(s) not found");
1886 	}
1887 
1888 	int viewing_side = 0;
1889 	bool ignore_units = true, see_all = true, ignore_teleport = false, debug = false, use_max_moves = false;
1890 
1891 	if (lua_istable(L, arg))  // 4. arg - options
1892 	{
1893 		lua_pushstring(L, "ignore_units");
1894 		lua_rawget(L, arg);
1895 		if (!lua_isnil(L, -1))
1896 		{
1897 			ignore_units = luaW_toboolean(L, -1);
1898 		}
1899 		lua_pop(L, 1);
1900 
1901 		lua_pushstring(L, "ignore_teleport");
1902 		lua_rawget(L, arg);
1903 		if (!lua_isnil(L, -1))
1904 		{
1905 			ignore_teleport = luaW_toboolean(L, -1);
1906 		}
1907 		lua_pop(L, 1);
1908 
1909 		lua_pushstring(L, "viewing_side");
1910 		lua_rawget(L, arg);
1911 		if (!lua_isnil(L, -1))
1912 		{
1913 			int i = luaL_checkinteger(L, -1);
1914 			if (i >= 1 && i <= static_cast<int>(teams().size()))
1915 			{
1916 				viewing_side = i;
1917 				see_all = false;
1918 			}
1919 		}
1920 
1921 		lua_pushstring(L, "debug");
1922 		lua_rawget(L, arg);
1923 		if (!lua_isnil(L, -1))
1924 		{
1925 			debug = luaW_toboolean(L, -1);
1926 		}
1927 		lua_pop(L, 1);
1928 
1929 		lua_pushstring(L, "use_max_moves");
1930 		lua_rawget(L, arg);
1931 		if (!lua_isnil(L, -1))
1932 		{
1933 			use_max_moves = luaW_toboolean(L, -1);
1934 		}
1935 		lua_pop(L, 1);
1936 		++arg;
1937 	}
1938 
1939 	// 5. arg - location filter
1940 	filter = vconfig::unconstructed_vconfig();
1941 	std::set<map_location> location_set;
1942 	luaW_tovconfig(L, arg, filter);
1943 	if (filter.null())
1944 	{
1945 		filter = vconfig(config(), true);
1946 	}
1947 	filter_context & fc = game_state_;
1948 	const terrain_filter t_filter(filter, &fc);
1949 	t_filter.get_locations(location_set, true);
1950 	++arg;
1951 
1952 	// build cost_map
1953 	const team& viewing_team = viewing_side
1954 		? board().get_team(viewing_side)
1955 		: board().teams()[0];
1956 
1957 	pathfind::full_cost_map cost_map(
1958 			ignore_units, !ignore_teleport, viewing_team, see_all, ignore_units);
1959 
1960 	for (const ::unit* const u : real_units)
1961 	{
1962 		cost_map.add_unit(*u, use_max_moves);
1963 	}
1964 	for (const unit_type_vector::value_type& fu : fake_units)
1965 	{
1966 		const unit_type* ut = unit_types.find(std::get<2>(fu));
1967 		cost_map.add_unit(std::get<0>(fu), ut, std::get<1>(fu));
1968 	}
1969 
1970 	if (debug)
1971 	{
1972 		if (game_display_) {
1973 			game_display_->labels().clear_all();
1974 			for (const map_location& loc : location_set)
1975 			{
1976 				std::stringstream s;
1977 				s << cost_map.get_pair_at(loc.x, loc.y).first;
1978 				s << " / ";
1979 				s << cost_map.get_pair_at(loc.x, loc.y).second;
1980 				game_display_->labels().set_label(loc, s.str());
1981 			}
1982 		}
1983 	}
1984 
1985 	// create return value
1986 	lua_createtable(L, location_set.size(), 0);
1987 	int counter = 1;
1988 	for (const map_location& loc : location_set)
1989 	{
1990 		lua_createtable(L, 4, 0);
1991 
1992 		lua_pushinteger(L, loc.wml_x());
1993 		lua_rawseti(L, -2, 1);
1994 
1995 		lua_pushinteger(L, loc.wml_y());
1996 		lua_rawseti(L, -2, 2);
1997 
1998 		lua_pushinteger(L, cost_map.get_pair_at(loc.x, loc.y).first);
1999 		lua_rawseti(L, -2, 3);
2000 
2001 		lua_pushinteger(L, cost_map.get_pair_at(loc.x, loc.y).second);
2002 		lua_rawseti(L, -2, 4);
2003 
2004 		lua_rawseti(L, -2, counter);
2005 		++counter;
2006 	}
2007 	return 1;
2008 }
2009 
intf_print(lua_State * L)2010 int game_lua_kernel::intf_print(lua_State *L) {
2011 	vconfig cfg(luaW_checkvconfig(L, 1));
2012 
2013 	// Remove any old message.
2014 	static int floating_label = 0;
2015 	if (floating_label)
2016 		font::remove_floating_label(floating_label);
2017 
2018 	// Display a message on-screen
2019 	std::string text = cfg["text"];
2020 	if(text.empty() || !game_display_)
2021 		return 0;
2022 
2023 	int size = cfg["size"].to_int(font::SIZE_SMALL);
2024 	int lifetime = cfg["duration"].to_int(50);
2025 
2026 	color_t color = font::LABEL_COLOR;
2027 
2028 	if(!cfg["color"].empty()) {
2029 		color = color_t::from_rgb_string(cfg["color"]);
2030 	} else if(cfg.has_attribute("red") || cfg.has_attribute("green") || cfg.has_attribute("blue")) {
2031 		color = color_t(cfg["red"], cfg["green"], cfg["blue"]);
2032 	}
2033 
2034 	const SDL_Rect& rect = game_display_->map_outside_area();
2035 
2036 	font::floating_label flabel(text);
2037 	flabel.set_font_size(size);
2038 	flabel.set_color(color);
2039 	flabel.set_position(rect.x + rect.w/2, rect.y + rect.h/2);
2040 	flabel.set_lifetime(lifetime);
2041 	flabel.set_clip_rect(rect);
2042 
2043 	floating_label = font::add_floating_label(flabel);
2044 
2045 	return 0;
2046 }
2047 
put_unit_helper(const map_location & loc)2048 void game_lua_kernel::put_unit_helper(const map_location& loc)
2049 {
2050 	if(game_display_) {
2051 		game_display_->invalidate(loc);
2052 	}
2053 
2054 	units().erase(loc);
2055 	resources::whiteboard->on_kill_unit();
2056 }
2057 
2058 /**
2059  * Places a unit on the map.
2060  * - Arg 1: (optional) location.
2061  * - Arg 2: Unit (WML table or proxy), or nothing/nil to delete.
2062  * OR
2063  * - Arg 1: Unit (WML table or proxy)
2064  * - Arg 2: (optional) location
2065  * - Arg 3: (optional) boolean
2066  */
intf_put_unit(lua_State * L)2067 int game_lua_kernel::intf_put_unit(lua_State *L)
2068 {
2069 	if(map_locked_) {
2070 		return luaL_error(L, "Attempted to move a unit while the map is locked");
2071 	}
2072 	int unit_arg = 1;
2073 
2074 	map_location loc;
2075 	if (lua_isnumber(L, 1)) {
2076 		// Since this form is deprecated, I didn't bother updating it to luaW_tolocation.
2077 		unit_arg = 3;
2078 		loc.set_wml_x(lua_tointeger(L, 1));
2079 		loc.set_wml_y(luaL_checkinteger(L, 2));
2080 		if (!map().on_board(loc)) {
2081 			return luaL_argerror(L, 1, "invalid location");
2082 		}
2083 	} else if (luaW_tolocation(L, 2, loc)) {
2084 		if (!map().on_board(loc)) {
2085 			return luaL_argerror(L, 2, "invalid location");
2086 		}
2087 	}
2088 
2089 	if((luaW_isunit(L, unit_arg))) {
2090 		lua_unit& u = *luaW_checkunit_ref(L, unit_arg);
2091 		if(u.on_map() && u->get_location() == loc) {
2092 			return 0;
2093 		}
2094 		if (!loc.valid()) {
2095 			loc = u->get_location();
2096 			if (!map().on_board(loc))
2097 				return luaL_argerror(L, 1, "invalid location");
2098 		} else if (unit_arg != 1) {
2099 			deprecated_message("wesnoth.put_unit(x, y, unit)", DEP_LEVEL::FOR_REMOVAL, {1, 15, 0}, "Use wesnoth.put_unit(unit, x, y) or unit:to_map(x, y) instead.");
2100 		}
2101 		put_unit_helper(loc);
2102 		u.put_map(loc);
2103 		u.get_shared()->anim_comp().set_standing();
2104 	} else if(!lua_isnoneornil(L, unit_arg)) {
2105 		const vconfig* vcfg = nullptr;
2106 		config cfg = luaW_checkconfig(L, unit_arg, vcfg);
2107 		if (unit_arg == 1 && !map().on_board(loc)) {
2108 			loc.set_wml_x(cfg["x"]);
2109 			loc.set_wml_y(cfg["y"]);
2110 			if (!map().on_board(loc))
2111 				return luaL_argerror(L, 2, "invalid location");
2112 		} else if (unit_arg != 1) {
2113 			deprecated_message("wesnoth.put_unit(x, y, unit)", DEP_LEVEL::FOR_REMOVAL, {1, 15, 0}, "Use wesnoth.put_unit(unit, x, y) or unit:to_map(x, y) instead.");
2114 		}
2115 		unit_ptr u = unit::create(cfg, true, vcfg);
2116 		put_unit_helper(loc);
2117 		u->set_location(loc);
2118 		units().insert(u);
2119 	} else {
2120 		deprecated_message("wesnoth.put_unit(x, y)", DEP_LEVEL::FOR_REMOVAL, {1, 15, 0}, "Use wesnoth.erase_unit(x, y) or unit:erase() instead.");
2121 		put_unit_helper(loc);
2122 		return 0; // Don't fire event when unit is only erase
2123 	}
2124 
2125 	// Fire event if using the deprecated version or if the final argument is not false
2126 	// If the final boolean argument is omitted, the actual final argument (the unit or location) will always yield true.
2127 	if(unit_arg != 1 || luaW_toboolean(L, -1)) {
2128 		play_controller_.pump().fire("unit_placed", loc);
2129 	}
2130 	return 0;
2131 }
2132 
2133 /**
2134  * Erases a unit from the map
2135  * - Arg 1: Unit to erase OR Location to erase unit
2136  */
intf_erase_unit(lua_State * L)2137 int game_lua_kernel::intf_erase_unit(lua_State *L)
2138 {
2139 	if(map_locked_) {
2140 		return luaL_error(L, "Attempted to remove a unit while the map is locked");
2141 	}
2142 	map_location loc;
2143 
2144 	if(luaW_isunit(L, 1)) {
2145 		lua_unit& u = *luaW_checkunit_ref(L, 1);
2146 		if (u.on_map()) {
2147 			loc = u->get_location();
2148 			if (!map().on_board(loc)) {
2149 				return luaL_argerror(L, 1, "invalid location");
2150 			}
2151 		} else if (int side = u.on_recall_list()) {
2152 			team &t = board().get_team(side);
2153 			// Should it use underlying ID instead?
2154 			t.recall_list().erase_if_matches_id(u->id());
2155 		} else {
2156 			return luaL_argerror(L, 1, "can't erase private units");
2157 		}
2158 	} else if (luaW_tolocation(L, 1, loc)) {
2159 		if (!map().on_board(loc)) {
2160 			return luaL_argerror(L, 1, "invalid location");
2161 		}
2162 	} else {
2163 		return luaL_argerror(L, 1, "expected unit or location");
2164 	}
2165 
2166 	units().erase(loc);
2167 	resources::whiteboard->on_kill_unit();
2168 	return 0;
2169 }
2170 
2171 /**
2172  * Puts a unit on a recall list.
2173  * - Arg 1: WML table or unit.
2174  * - Arg 2: (optional) side.
2175  */
intf_put_recall_unit(lua_State * L)2176 int game_lua_kernel::intf_put_recall_unit(lua_State *L)
2177 {
2178 	if(map_locked_) {
2179 		return luaL_error(L, "Attempted to move a unit while the map is locked");
2180 	}
2181 	lua_unit *lu = nullptr;
2182 	unit_ptr u = unit_ptr();
2183 	int side = lua_tointeger(L, 2);
2184 	if (static_cast<unsigned>(side) > teams().size()) side = 0;
2185 
2186 	if(luaW_isunit(L, 1)) {
2187 		lu = luaW_checkunit_ref(L, 1);
2188 		u = lu->get_shared();
2189 		if(lu->on_recall_list() && lu->on_recall_list() == side) {
2190 			return luaL_argerror(L, 1, "unit already on recall list");
2191 		}
2192 	} else {
2193 		const vconfig* vcfg = nullptr;
2194 		config cfg = luaW_checkconfig(L, 1, vcfg);
2195 		u = unit::create(cfg, true, vcfg);
2196 	}
2197 
2198 	if (!side) {
2199 		side = u->side();
2200 	} else {
2201 		u->set_side(side);
2202 	}
2203 	team &t = board().get_team(side);
2204 	// Avoid duplicates in the recall list.
2205 	size_t uid = u->underlying_id();
2206 	t.recall_list().erase_by_underlying_id(uid);
2207 	t.recall_list().add(u);
2208 	if (lu) {
2209 		if (lu->on_map()) {
2210 			units().erase(u->get_location());
2211 			resources::whiteboard->on_kill_unit();
2212 			u->anim_comp().clear_haloes();
2213 		}
2214 		lu->lua_unit::~lua_unit();
2215 		new(lu) lua_unit(side, uid);
2216 	}
2217 
2218 	return 0;
2219 }
2220 
2221 /**
2222  * Extracts a unit from the map or a recall list and gives it to Lua.
2223  * - Arg 1: unit userdata.
2224  */
intf_extract_unit(lua_State * L)2225 int game_lua_kernel::intf_extract_unit(lua_State *L)
2226 {
2227 	if(map_locked_) {
2228 		return luaL_error(L, "Attempted to remove a unit while the map is locked");
2229 	}
2230 	lua_unit* lu = luaW_checkunit_ref(L, 1);
2231 	unit_ptr u = lu->get_shared();
2232 
2233 	if (lu->on_map()) {
2234 		u = units().extract(u->get_location());
2235 		assert(u);
2236 		u->anim_comp().clear_haloes();
2237 	} else if (int side = lu->on_recall_list()) {
2238 		team &t = board().get_team(side);
2239 		unit_ptr v = u->clone();
2240 		t.recall_list().erase_if_matches_id(u->id());
2241 		u = v;
2242 	} else {
2243 		return 0;
2244 	}
2245 
2246 	lu->lua_unit::~lua_unit();
2247 	new(lu) lua_unit(u);
2248 	return 0;
2249 }
2250 
2251 /**
2252  * Finds a vacant tile.
2253  * - Arg 1: location.
2254  * - Arg 2: optional unit for checking movement type.
2255  * - Rets 1,2: location.
2256  */
intf_find_vacant_tile(lua_State * L)2257 int game_lua_kernel::intf_find_vacant_tile(lua_State *L)
2258 {
2259 	map_location loc = luaW_checklocation(L, 1);
2260 
2261 	unit_ptr u;
2262 	if (!lua_isnoneornil(L, 2)) {
2263 		if(luaW_isunit(L, 2)) {
2264 			u = luaW_checkunit_ptr(L, 2, false);
2265 		} else {
2266 			const vconfig* vcfg = nullptr;
2267 			config cfg = luaW_checkconfig(L, 2, vcfg);
2268 			u = unit::create(cfg, false, vcfg);
2269 		}
2270 	}
2271 
2272 	map_location res = find_vacant_tile(loc, pathfind::VACANT_ANY, u.get());
2273 
2274 	if (!res.valid()) return 0;
2275 	lua_pushinteger(L, res.wml_x());
2276 	lua_pushinteger(L, res.wml_y());
2277 	return 2;
2278 }
2279 
2280 /**
2281  * Floats some text on the map.
2282  * - Arg 1: location.
2283  * - Arg 2: string.
2284  * - Arg 3: color.
2285  */
intf_float_label(lua_State * L)2286 int game_lua_kernel::intf_float_label(lua_State *L)
2287 {
2288 	map_location loc = luaW_checklocation(L, 1);
2289 	color_t color = font::LABEL_COLOR;
2290 
2291 	t_string text = luaW_checktstring(L, 2);
2292 	if (!lua_isnoneornil(L, 3)) {
2293 		color = color_t::from_rgb_string(luaL_checkstring(L, 3));
2294 	}
2295 
2296 	if (game_display_) {
2297 		game_display_->float_label(loc, text, color);
2298 	}
2299 	return 0;
2300 }
2301 
2302 /**
2303  * Creates a unit from its WML description.
2304  * - Arg 1: WML table.
2305  * - Ret 1: unit userdata.
2306  */
intf_create_unit(lua_State * L)2307 static int intf_create_unit(lua_State *L)
2308 {
2309 	const vconfig* vcfg = nullptr;
2310 	config cfg = luaW_checkconfig(L, 1, vcfg);
2311 	unit_ptr u  = unit::create(cfg, true, vcfg);
2312 	luaW_pushunit(L, u);
2313 	return 1;
2314 }
2315 
2316 /**
2317  * Copies a unit.
2318  * - Arg 1: unit userdata.
2319  * - Ret 1: unit userdata.
2320  */
intf_copy_unit(lua_State * L)2321 static int intf_copy_unit(lua_State *L)
2322 {
2323 	unit& u = luaW_checkunit(L, 1);
2324 	luaW_pushunit(L, u.clone());
2325 	return 1;
2326 }
2327 
2328 /**
2329  * Returns unit resistance against a given attack type.
2330  * - Arg 1: unit userdata.
2331  * - Arg 2: string containing the attack type.
2332  * - Arg 3: boolean indicating if attacker.
2333  * - Arg 4: optional location.
2334  * - Ret 1: integer.
2335  */
intf_unit_resistance(lua_State * L)2336 static int intf_unit_resistance(lua_State *L)
2337 {
2338 	const unit& u = luaW_checkunit(L, 1);
2339 	char const *m = luaL_checkstring(L, 2);
2340 	bool a = luaW_toboolean(L, 3);
2341 
2342 	map_location loc = u.get_location();
2343 	if (!lua_isnoneornil(L, 4)) {
2344 		loc = luaW_checklocation(L, 4);
2345 	}
2346 
2347 	lua_pushinteger(L, u.resistance_against(m, a, loc));
2348 	return 1;
2349 }
2350 
2351 /**
2352  * Returns unit movement cost on a given terrain.
2353  * - Arg 1: unit userdata.
2354  * - Arg 2: string containing the terrain type.
2355  * - Ret 1: integer.
2356  */
intf_unit_movement_cost(lua_State * L)2357 static int intf_unit_movement_cost(lua_State *L)
2358 {
2359 	const unit& u = luaW_checkunit(L, 1);
2360 	char const *m = luaL_checkstring(L, 2);
2361 	t_translation::terrain_code t = t_translation::read_terrain_code(m);
2362 	lua_pushinteger(L, u.movement_cost(t));
2363 	return 1;
2364 }
2365 
2366 /**
2367  * Returns unit vision cost on a given terrain.
2368  * - Arg 1: unit userdata.
2369  * - Arg 2: string containing the terrain type.
2370  * - Ret 1: integer.
2371  */
intf_unit_vision_cost(lua_State * L)2372 static int intf_unit_vision_cost(lua_State *L)
2373 {
2374 	const unit& u = luaW_checkunit(L, 1);
2375 	char const *m = luaL_checkstring(L, 2);
2376 	t_translation::terrain_code t = t_translation::read_terrain_code(m);
2377 	lua_pushinteger(L, u.vision_cost(t));
2378 	return 1;
2379 }
2380 
2381 /**
2382  * Returns unit jamming cost on a given terrain.
2383  * - Arg 1: unit userdata.
2384  * - Arg 2: string containing the terrain type.
2385  * - Ret 1: integer.
2386  */
intf_unit_jamming_cost(lua_State * L)2387 static int intf_unit_jamming_cost(lua_State *L)
2388 {
2389 	const unit& u = luaW_checkunit(L, 1);
2390 	char const *m = luaL_checkstring(L, 2);
2391 	t_translation::terrain_code t = t_translation::read_terrain_code(m);
2392 	lua_pushinteger(L, u.jamming_cost(t));
2393 	return 1;
2394 }
2395 
2396 /**
2397  * Returns unit defense on a given terrain.
2398  * - Arg 1: unit userdata.
2399  * - Arg 2: string containing the terrain type.
2400  * - Ret 1: integer.
2401  */
intf_unit_defense(lua_State * L)2402 static int intf_unit_defense(lua_State *L)
2403 {
2404 	const unit& u = luaW_checkunit(L, 1);
2405 	char const *m = luaL_checkstring(L, 2);
2406 	t_translation::terrain_code t = t_translation::read_terrain_code(m);
2407 	lua_pushinteger(L, u.defense_modifier(t));
2408 	return 1;
2409 }
2410 
2411 /**
2412  * Returns true if the unit has the given ability enabled.
2413  * - Arg 1: unit userdata.
2414  * - Arg 2: string.
2415  * - Ret 1: boolean.
2416  */
intf_unit_ability(lua_State * L)2417 int game_lua_kernel::intf_unit_ability(lua_State *L)
2418 {
2419 	const unit& u = luaW_checkunit(L, 1);
2420 	char const *m = luaL_checkstring(L, 2);
2421 	lua_pushboolean(L, u.get_ability_bool(m, board()));
2422 	return 1;
2423 }
2424 
2425 /**
2426  * Changes a unit to the given unit type.
2427  * - Arg 1: unit userdata.
2428  * - Arg 2: string.
2429  */
intf_transform_unit(lua_State * L)2430 static int intf_transform_unit(lua_State *L)
2431 {
2432 	unit& u = luaW_checkunit(L, 1);
2433 	char const *m = luaL_checkstring(L, 2);
2434 	const unit_type *utp = unit_types.find(m);
2435 	if (!utp) return luaL_argerror(L, 2, "unknown unit type");
2436 	u.advance_to(*utp);
2437 
2438 	return 0;
2439 }
2440 
2441 /**
2442  * Puts a table at the top of the stack with some combat result.
2443  */
luaW_pushsimdata(lua_State * L,const combatant & cmb)2444 static void luaW_pushsimdata(lua_State *L, const combatant &cmb)
2445 {
2446 	int n = cmb.hp_dist.size();
2447 	lua_createtable(L, 0, 4);
2448 	lua_pushnumber(L, cmb.poisoned);
2449 	lua_setfield(L, -2, "poisoned");
2450 	lua_pushnumber(L, cmb.slowed);
2451 	lua_setfield(L, -2, "slowed");
2452 	lua_pushnumber(L, cmb.untouched);
2453 	lua_setfield(L, -2, "untouched");
2454 	lua_pushnumber(L, cmb.average_hp());
2455 	lua_setfield(L, -2, "average_hp");
2456 	lua_createtable(L, n, 0);
2457 	for (int i = 0; i < n; ++i) {
2458 		lua_pushnumber(L, cmb.hp_dist[i]);
2459 		lua_rawseti(L, -2, i);
2460 	}
2461 	lua_setfield(L, -2, "hp_chance");
2462 }
2463 
2464 /**
2465  * Puts a table at the top of the stack with information about the combatants' weapons.
2466  */
luaW_pushsimweapon(lua_State * L,const battle_context_unit_stats & bcustats)2467 static void luaW_pushsimweapon(lua_State *L, const battle_context_unit_stats &bcustats)
2468 {
2469 
2470 	lua_createtable(L, 0, 16);
2471 
2472 	lua_pushnumber(L, bcustats.num_blows);
2473 	lua_setfield(L, -2, "num_blows");
2474 	lua_pushnumber(L, bcustats.damage);
2475 	lua_setfield(L, -2, "damage");
2476 	lua_pushnumber(L, bcustats.chance_to_hit);
2477 	lua_setfield(L, -2, "chance_to_hit");
2478 	lua_pushboolean(L, bcustats.poisons);
2479 	lua_setfield(L, -2, "poisons");
2480 	lua_pushboolean(L, bcustats.slows);
2481 	lua_setfield(L, -2, "slows");
2482 	lua_pushboolean(L, bcustats.petrifies);
2483 	lua_setfield(L, -2, "petrifies");
2484 	lua_pushboolean(L, bcustats.plagues);
2485 	lua_setfield(L, -2, "plagues");
2486 	lua_pushstring(L, bcustats.plague_type.c_str());
2487 	lua_setfield(L, -2, "plague_type");
2488 	lua_pushboolean(L, bcustats.backstab_pos);
2489 	lua_setfield(L, -2, "backstabs");
2490 	lua_pushnumber(L, bcustats.rounds);
2491 	lua_setfield(L, -2, "rounds");
2492 	lua_pushboolean(L, bcustats.firststrike);
2493 	lua_setfield(L, -2, "firststrike");
2494 	lua_pushboolean(L, bcustats.drains);
2495 	lua_setfield(L, -2, "drains");
2496 	lua_pushnumber(L, bcustats.drain_constant);
2497 	lua_setfield(L, -2, "drain_constant");
2498 	lua_pushnumber(L, bcustats.drain_percent);
2499 	lua_setfield(L, -2, "drain_percent");
2500 
2501 
2502 	//if we called simulate_combat without giving an explicit weapon this can be useful.
2503 	lua_pushnumber(L, bcustats.attack_num);
2504 	lua_setfield(L, -2, "attack_num"); // DEPRECATED
2505 	lua_pushnumber(L, bcustats.attack_num + 1);
2506 	lua_setfield(L, -2, "number");
2507 	//this is nullptr when there is no counter weapon
2508 	if(bcustats.weapon != nullptr)
2509 	{
2510 		lua_pushstring(L, bcustats.weapon->id().c_str());
2511 		lua_setfield(L, -2, "name");
2512 		luaW_pushweapon(L, bcustats.weapon);
2513 		lua_setfield(L, -2, "weapon");
2514 	}
2515 
2516 }
2517 
2518 /**
2519  * Simulates a combat between two units.
2520  * - Arg 1: attacker userdata.
2521  * - Arg 2: optional weapon index.
2522  * - Arg 3: defender userdata.
2523  * - Arg 4: optional weapon index.
2524  * - Ret 1: attacker results.
2525  * - Ret 2: defender results.
2526  * - Ret 3: info about the attacker weapon.
2527  * - Ret 4: info about the defender weapon.
2528  */
intf_simulate_combat(lua_State * L)2529 int game_lua_kernel::intf_simulate_combat(lua_State *L)
2530 {
2531 	int arg_num = 1, att_w = -1, def_w = -1;
2532 
2533 	const unit& att = luaW_checkunit(L, arg_num);
2534 	++arg_num;
2535 	if (lua_isnumber(L, arg_num)) {
2536 		att_w = lua_tointeger(L, arg_num) - 1;
2537 		if (att_w < 0 || att_w >= static_cast<int>(att.attacks().size()))
2538 			return luaL_argerror(L, arg_num, "weapon index out of bounds");
2539 		++arg_num;
2540 	}
2541 
2542 	const unit& def = luaW_checkunit(L, arg_num, true);
2543 	++arg_num;
2544 	if (lua_isnumber(L, arg_num)) {
2545 		def_w = lua_tointeger(L, arg_num) - 1;
2546 		if (def_w < 0 || def_w >= static_cast<int>(def.attacks().size()))
2547 			return luaL_argerror(L, arg_num, "weapon index out of bounds");
2548 		++arg_num;
2549 	}
2550 
2551 	battle_context context(units(), att.get_location(),
2552 		def.get_location(), att_w, def_w, 0.0, nullptr, &att);
2553 
2554 	luaW_pushsimdata(L, context.get_attacker_combatant());
2555 	luaW_pushsimdata(L, context.get_defender_combatant());
2556 	luaW_pushsimweapon(L, context.get_attacker_stats());
2557 	luaW_pushsimweapon(L, context.get_defender_stats());
2558 	return 4;
2559 }
2560 
2561 /**
2562  * Modifies the music playlist.
2563  * - Arg 1: WML table, or nil to force changes.
2564  */
intf_set_music(lua_State * L)2565 static int intf_set_music(lua_State *L)
2566 {
2567 	deprecated_message("wesnoth.set_music", DEP_LEVEL::INDEFINITE, "", "Use the wesnoth.playlist table instead!");
2568 	if (lua_isnoneornil(L, 1)) {
2569 		sound::commit_music_changes();
2570 		return 0;
2571 	}
2572 
2573 	config cfg = luaW_checkconfig(L, 1);
2574 	sound::play_music_config(cfg);
2575 	return 0;
2576 }
2577 
2578 /**
2579  * Plays a sound, possibly repeated.
2580  * - Arg 1: string.
2581  * - Arg 2: optional integer.
2582  */
intf_play_sound(lua_State * L)2583 int game_lua_kernel::intf_play_sound(lua_State *L)
2584 {
2585 	char const *m = luaL_checkstring(L, 1);
2586 	if (play_controller_.is_skipping_replay()) return 0;
2587 	int repeats = lua_tointeger(L, 2);
2588 	sound::play_sound(m, sound::SOUND_FX, repeats);
2589 	return 0;
2590 }
2591 
2592 /**
2593  * Gets/sets the current sound volume
2594  * - Arg 1: (optional) New volume to set
2595  * - Return: Original volume
2596  */
intf_sound_volume(lua_State * L)2597 static int intf_sound_volume(lua_State* L)
2598 {
2599 	int vol = preferences::sound_volume();
2600 	lua_pushnumber(L, sound::get_sound_volume() * 100.0 / vol);
2601 	if(lua_isnumber(L, 1)) {
2602 		float rel = lua_tonumber(L, 1);
2603 		if(rel < 0.0f || rel > 100.0f) {
2604 			return luaL_argerror(L, 1, "volume must be in range 0..100");
2605 		}
2606 		vol = static_cast<int>(rel*vol / 100.0f);
2607 		sound::set_sound_volume(vol);
2608 	}
2609 	return 1;
2610 }
2611 
2612 /**
2613  * Scrolls to given tile.
2614  * - Arg 1: location.
2615  * - Arg 2: boolean preventing scroll to fog.
2616  * - Arg 3: boolean specifying whether to warp instantly.
2617  * - Arg 4: boolean specifying whether to skip if already onscreen
2618  */
intf_scroll_to_tile(lua_State * L)2619 int game_lua_kernel::intf_scroll_to_tile(lua_State *L)
2620 {
2621 	map_location loc = luaW_checklocation(L, 1);
2622 	bool check_fogged = luaW_toboolean(L, 2);
2623 	game_display::SCROLL_TYPE scroll = luaW_toboolean(L, 4)
2624 		? luaW_toboolean(L, 3)
2625 			? game_display::ONSCREEN_WARP
2626 			: game_display::ONSCREEN
2627 		: luaW_toboolean(L, 3)
2628 			? game_display::WARP
2629 			: game_display::SCROLL
2630 	;
2631 	if (game_display_) {
2632 		game_display_->scroll_to_tile(loc, scroll, check_fogged);
2633 	}
2634 	return 0;
2635 }
2636 
intf_select_hex(lua_State * L)2637 int game_lua_kernel::intf_select_hex(lua_State *L)
2638 {
2639 	events::command_disabler command_disabler;
2640 	deprecated_message("wesnoth.select_hex", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use wesnoth.select_unit and/or wesnoth.highlight_hex instead.");
2641 
2642 	// Need this because check_location may change the stack
2643 	// By doing this now, we ensure that it won't do so when
2644 	// intf_select_unit and intf_highlight_hex call it.
2645 	const map_location loc = luaW_checklocation(L, 1);
2646 	luaW_pushlocation(L, loc);
2647 	lua_replace(L, 1);
2648 
2649 	intf_select_unit(L);
2650 	if(!lua_isnoneornil(L, 2) && luaW_toboolean(L,2)) {
2651 		intf_highlight_hex(L);
2652 	}
2653 	return 0;
2654 }
2655 
2656 /**
2657  * Selects and highlights the given location on the map.
2658  * - Arg 1: location.
2659  * - Args 2,3: booleans
2660  */
intf_select_unit(lua_State * L)2661 int game_lua_kernel::intf_select_unit(lua_State *L)
2662 {
2663 	events::command_disabler command_disabler;
2664 	if(lua_isnoneornil(L, 1)) {
2665 		play_controller_.get_mouse_handler_base().select_hex(map_location::null_location(), false, false, false);
2666 		return 0;
2667 	}
2668 	const map_location loc = luaW_checklocation(L, 1);
2669 	if(!map().on_board(loc)) return luaL_argerror(L, 1, "not on board");
2670 	bool highlight = true;
2671 	if(!lua_isnoneornil(L, 2))
2672 		highlight = luaW_toboolean(L, 2);
2673 	const bool fire_event = luaW_toboolean(L, 3);
2674 	play_controller_.get_mouse_handler_base().select_hex(
2675 		loc, false, highlight, fire_event);
2676 	return 0;
2677 }
2678 
2679 /**
2680  * Deselects any highlighted hex on the map.
2681  * No arguments or return values
2682  */
intf_deselect_hex(lua_State *)2683 int game_lua_kernel::intf_deselect_hex(lua_State*)
2684 {
2685 	if(game_display_) {
2686 		game_display_->highlight_hex(map_location::null_location());
2687 	}
2688 
2689 	return 0;
2690 }
2691 
2692 /**
2693  * Return true if a replay is in progress but the player has chosen to skip it
2694  */
intf_is_skipping_messages(lua_State * L)2695 int game_lua_kernel::intf_is_skipping_messages(lua_State *L)
2696 {
2697 	bool skipping = play_controller_.is_skipping_replay() || play_controller_.is_skipping_story();
2698 	if (!skipping) {
2699 		skipping = game_state_.events_manager_->pump().context_skip_messages();
2700 	}
2701 	lua_pushboolean(L, skipping);
2702 	return 1;
2703 }
2704 
2705 /**
2706  * Set whether to skip messages
2707  * Arg 1 (optional) - boolean
2708  */
intf_skip_messages(lua_State * L)2709 int game_lua_kernel::intf_skip_messages(lua_State *L)
2710 {
2711 	bool skip = true;
2712 	if (!lua_isnone(L, 1)) {
2713 		skip = luaW_toboolean(L, 1);
2714 	}
2715 	game_state_.events_manager_->pump().context_skip_messages(skip);
2716 	return 0;
2717 }
2718 
2719 namespace
2720 {
2721 	struct lua_synchronize : mp_sync::user_choice
2722 	{
2723 		lua_State *L;
2724 		int user_choice_index;
2725 		int random_choice_index;
2726 		int ai_choice_index;
2727 		std::string  desc;
lua_synchronize__anon384f78800311::lua_synchronize2728 		lua_synchronize(lua_State *l, const std::string& descr, int user_index, int random_index = 0, int ai_index = 0)
2729 			: L(l)
2730 			, user_choice_index(user_index)
2731 			, random_choice_index(random_index)
2732 			, ai_choice_index(ai_index != 0 ? ai_index : user_index)
2733 			, desc(descr)
2734 		{}
2735 
query_user__anon384f78800311::lua_synchronize2736 		virtual config query_user(int side) const override
2737 		{
2738 			bool is_local_ai = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).board().get_team(side).is_local_ai();
2739 			config cfg;
2740 			query_lua(side, is_local_ai ? ai_choice_index : user_choice_index, cfg);
2741 			return cfg;
2742 		}
2743 
random_choice__anon384f78800311::lua_synchronize2744 		virtual config random_choice(int side) const override
2745 		{
2746 			config cfg;
2747 			if(random_choice_index != 0 && lua_isfunction(L, random_choice_index)) {
2748 				query_lua(side, random_choice_index, cfg);
2749 			}
2750 			return cfg;
2751 		}
2752 
description__anon384f78800311::lua_synchronize2753 		virtual std::string description() const override
2754 		{
2755 			return desc;
2756 		}
2757 
query_lua__anon384f78800311::lua_synchronize2758 		void query_lua(int side, int function_index, config& cfg) const
2759 		{
2760 			assert(cfg.empty());
2761 			lua_pushvalue(L, function_index);
2762 			lua_pushnumber(L, side);
2763 			if (luaW_pcall(L, 1, 1, false)) {
2764 				if(!luaW_toconfig(L, -1, cfg)) {
2765 					lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).log_error("function returned to wesnoth.synchronize_choice a table which was partially invalid");
2766 				}
2767 			}
2768 		}
2769 		//Although lua's sync_choice can show a dialog, (and will in most cases)
2770 		//we return false to enable other possible things that do not contain UI things.
2771 		//it's in the responsibility of the umc dev to not show dialogs during prestart events.
is_visible__anon384f78800311::lua_synchronize2772 		virtual bool is_visible() const override { return false; }
2773 	};
2774 }//unnamed namespace for lua_synchronize
2775 
2776 /**
2777  * Ensures a value is synchronized among all the clients.
2778  * - Arg 1: optional string specifying the type id of the choice.
2779  * - Arg 2: function to compute the value, called if the client is the master.
2780  * - Arg 3: optional function, called instead of the first function if the user is not human.
2781  * - Arg 4: optional integer  specifying, on which side the function should be evaluated.
2782  * - Ret 1: WML table returned by the function.
2783  */
intf_synchronize_choice(lua_State * L)2784 static int intf_synchronize_choice(lua_State *L)
2785 {
2786 	std::string tagname = "input";
2787 	t_string desc = _("input");
2788 	int human_func = 0;
2789 	int ai_func = 0;
2790 	int side_for;
2791 
2792 	int nextarg = 1;
2793 	if(!lua_isfunction(L, nextarg) && luaW_totstring(L, nextarg, desc) ) {
2794 		++nextarg;
2795 	}
2796 	if(lua_isfunction(L, nextarg)) {
2797 		human_func = nextarg++;
2798 	}
2799 	else {
2800 		return luaL_argerror(L, nextarg, "expected a function");
2801 	}
2802 	if(lua_isfunction(L, nextarg)) {
2803 		ai_func = nextarg++;
2804 	}
2805 	side_for = lua_tointeger(L, nextarg);
2806 
2807 	config cfg = mp_sync::get_user_choice(tagname, lua_synchronize(L, desc, human_func, 0, ai_func), side_for);
2808 	luaW_pushconfig(L, cfg);
2809 	return 1;
2810 }
2811 /**
2812  * Ensures a value is synchronized among all the clients.
2813  * - Arg 1: optional string the id of this type of user input, may only contain characters a-z and '_'
2814  * - Arg 2: function to compute the value, called if the client is the master.
2815  * - Arg 3: an optional function to compute the value, if the side was null/empty controlled.
2816  * - Arg 4: an array of integers specifying, on which side the function should be evaluated.
2817  * - Ret 1: a map int -> WML tabls.
2818  */
intf_synchronize_choices(lua_State * L)2819 static int intf_synchronize_choices(lua_State *L)
2820 {
2821 	std::string tagname = "input";
2822 	t_string desc = _("input");
2823 	int human_func = 0;
2824 	int null_func = 0;
2825 	std::vector<int> sides_for;
2826 
2827 	int nextarg = 1;
2828 	if(!lua_isfunction(L, nextarg) && luaW_totstring(L, nextarg, desc) ) {
2829 		++nextarg;
2830 	}
2831 	if(lua_isfunction(L, nextarg)) {
2832 		human_func = nextarg++;
2833 	}
2834 	else {
2835 		return luaL_argerror(L, nextarg, "expected a function");
2836 	}
2837 	if(lua_isfunction(L, nextarg)) {
2838 		null_func = nextarg++;
2839 	};
2840 	sides_for = lua_check<std::vector<int>>(L, nextarg++);
2841 
2842 	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())));
2843 	return 1;
2844 }
2845 
2846 
2847 /**
2848  * Calls a function in an unsynced context (this specially means that all random calls used by that function will be unsynced).
2849  * This is usually used together with an unsynced if like 'if controller != network'
2850  * - Arg 1: function that will be called during the unsynced context.
2851  */
intf_do_unsynced(lua_State * L)2852 static int intf_do_unsynced(lua_State *L)
2853 {
2854 	set_scontext_unsynced sync;
2855 	lua_pushvalue(L, 1);
2856 	luaW_pcall(L, 0, 0, false);
2857 	return 0;
2858 }
2859 
2860 /**
2861  * Gets all the locations matching a given filter.
2862  * - Arg 1: WML table.
2863  * - Arg 2: Optional reference unit (teleport_unit)
2864  * - Ret 1: array of integer pairs.
2865  */
intf_get_locations(lua_State * L)2866 int game_lua_kernel::intf_get_locations(lua_State *L)
2867 {
2868 	vconfig filter = luaW_checkvconfig(L, 1);
2869 
2870 	std::set<map_location> res;
2871 	filter_context & fc = game_state_;
2872 	const terrain_filter t_filter(filter, &fc);
2873 	if(luaW_isunit(L, 2)) {
2874 		t_filter.get_locations(res, *luaW_tounit(L, 2), true);
2875 	} else {
2876 		t_filter.get_locations(res, true);
2877 	}
2878 
2879 	lua_createtable(L, res.size(), 0);
2880 	int i = 1;
2881 	for (const map_location& loc : res)
2882 	{
2883 		lua_createtable(L, 2, 0);
2884 		lua_pushinteger(L, loc.wml_x());
2885 		lua_rawseti(L, -2, 1);
2886 		lua_pushinteger(L, loc.wml_y());
2887 		lua_rawseti(L, -2, 2);
2888 		lua_rawseti(L, -2, i);
2889 		++i;
2890 	}
2891 	return 1;
2892 }
2893 
2894 /**
2895  * Gets all the villages matching a given filter, or all the villages on the map if no filter is given.
2896  * - Arg 1: WML table (optional).
2897  * - Ret 1: array of integer pairs.
2898  */
intf_get_villages(lua_State * L)2899 int game_lua_kernel::intf_get_villages(lua_State *L)
2900 {
2901 	std::vector<map_location> locs = map().villages();
2902 	lua_newtable(L);
2903 	int i = 1;
2904 
2905 	vconfig filter = luaW_checkvconfig(L, 1);
2906 
2907 	filter_context & fc = game_state_;
2908 	for(std::vector<map_location>::const_iterator it = locs.begin(); it != locs.end(); ++it) {
2909 		bool matches = terrain_filter(filter, &fc).match(*it);
2910 		if (matches) {
2911 			lua_createtable(L, 2, 0);
2912 			lua_pushinteger(L, it->wml_x());
2913 			lua_rawseti(L, -2, 1);
2914 			lua_pushinteger(L, it->wml_y());
2915 			lua_rawseti(L, -2, 2);
2916 			lua_rawseti(L, -2, i);
2917 			++i;
2918 		}
2919 	}
2920 	return 1;
2921 }
2922 
2923 /**
2924  * Matches a location against the given filter.
2925  * - Arg 1: location.
2926  * - Arg 2: WML table.
2927  * - Arg 3: Optional reference unit (teleport_unit)
2928  * - Ret 1: boolean.
2929  */
intf_match_location(lua_State * L)2930 int game_lua_kernel::intf_match_location(lua_State *L)
2931 {
2932 	map_location loc = luaW_checklocation(L, 1);
2933 	vconfig filter = luaW_checkvconfig(L, 2, true);
2934 
2935 	if (filter.null()) {
2936 		lua_pushboolean(L, true);
2937 		return 1;
2938 	}
2939 
2940 	filter_context & fc = game_state_;
2941 	const terrain_filter t_filter(filter, &fc);
2942 	if(luaW_isunit(L, 3)) {
2943 		lua_pushboolean(L, t_filter.match(loc, *luaW_tounit(L, 3)));
2944 	} else {
2945 		lua_pushboolean(L, t_filter.match(loc));
2946 	}
2947 	return 1;
2948 }
2949 
2950 
2951 
2952 /**
2953  * Matches a side against the given filter.
2954  * - Args 1: side number.
2955  * - Arg 2: WML table.
2956  * - Ret 1: boolean.
2957  */
intf_match_side(lua_State * L)2958 int game_lua_kernel::intf_match_side(lua_State *L)
2959 {
2960 	vconfig filter = luaW_checkvconfig(L, 2, true);
2961 
2962 	if (filter.null()) {
2963 		lua_pushboolean(L, true);
2964 		return 1;
2965 	}
2966 
2967 	filter_context & fc = game_state_;
2968 	side_filter s_filter(filter, &fc);
2969 
2970 	if(team* t = luaW_toteam(L, 1)) {
2971 		lua_pushboolean(L, s_filter.match(*t));
2972 	} else {
2973 		unsigned side = luaL_checkinteger(L, 1) - 1;
2974 		if (side >= teams().size()) return 0;
2975 		lua_pushboolean(L, s_filter.match(side + 1));
2976 	}
2977 	return 1;
2978 }
2979 
intf_set_side_id(lua_State * L)2980 int game_lua_kernel::intf_set_side_id(lua_State *L)
2981 {
2982 	int team_i = luaL_checkinteger(L, 1) - 1;
2983 	std::string flag = luaL_optlstring(L, 2, "", nullptr);
2984 	std::string color = luaL_optlstring(L, 3, "", nullptr);
2985 
2986 	if(flag.empty() && color.empty()) {
2987 		return 0;
2988 	}
2989 	if(team_i < 0 || static_cast<size_t>(team_i) >= teams().size()) {
2990 		return luaL_error(L, "set_side_id: side number %d out of range", team_i);
2991 	}
2992 	team& side = teams()[team_i];
2993 
2994 	if(!color.empty()) {
2995 		side.set_color(color);
2996 	}
2997 	if(!flag.empty()) {
2998 		side.set_flag(flag);
2999 	}
3000 
3001 	game_display_->reinit_flags_for_side(team_i);
3002 	return 0;
3003 }
3004 
intf_modify_ai(lua_State * L,const char * action)3005 static int intf_modify_ai(lua_State *L, const char* action)
3006 {
3007 	int side_num = luaL_checkinteger(L, 1);
3008 	std::string path = luaL_checkstring(L, 2);
3009 	config cfg {
3010 		"action", action,
3011 		"path", path
3012 	};
3013 	if(strcmp(action, "delete") == 0) {
3014 		ai::manager::get_singleton().modify_active_ai_for_side(side_num, cfg);
3015 		return 0;
3016 	}
3017 	config component = luaW_checkconfig(L, 3);
3018 	size_t len = std::string::npos, open_brak = path.find_last_of('[');
3019 	size_t dot = path.find_last_of('.');
3020 	if(open_brak != len) {
3021 		len = open_brak - dot - 1;
3022 	}
3023 	cfg.add_child(path.substr(dot + 1, len), component);
3024 	ai::manager::get_singleton().modify_active_ai_for_side(side_num, cfg);
3025 	return 0;
3026 }
3027 
intf_switch_ai(lua_State * L)3028 static int intf_switch_ai(lua_State *L)
3029 {
3030 	int side_num = luaL_checkinteger(L, 1);
3031 	if(lua_isstring(L, 2)) {
3032 		std::string file = luaL_checkstring(L, 2);
3033 		if(!ai::manager::get_singleton().add_ai_for_side_from_file(side_num, file)) {
3034 			std::string err = formatter() << "Could not load AI for side " << side_num << " from file " << file;
3035 			lua_pushlstring(L, err.c_str(), err.length());
3036 			return lua_error(L);
3037 		}
3038 	} else {
3039 		ai::manager::get_singleton().add_ai_for_side_from_config(side_num, luaW_checkconfig(L, 2));
3040 	}
3041 	return 0;
3042 }
3043 
intf_append_ai(lua_State * L)3044 static int intf_append_ai(lua_State *L)
3045 {
3046 	int side_num = luaL_checkinteger(L, 1);
3047 	config cfg = luaW_checkconfig(L, 2);
3048 	if(!cfg.has_child("ai")) {
3049 		cfg = config {"ai", cfg};
3050 	}
3051 	bool added_dummy_stage = false;
3052 	if(!cfg.child("ai").has_child("stage")) {
3053 		added_dummy_stage = true;
3054 		cfg.child("ai").add_child("stage", config {"name", "empty"});
3055 	}
3056 	ai::configuration::expand_simplified_aspects(side_num, cfg);
3057 	if(added_dummy_stage) {
3058 		for(auto iter = cfg.ordered_begin(); iter != cfg.ordered_end(); iter++) {
3059 			if(iter->key == "stage" && iter->cfg["name"] == "empty") {
3060 				iter = cfg.erase(iter);
3061 			}
3062 		}
3063 	}
3064 	ai::manager::get_singleton().append_active_ai_for_side(side_num, cfg.child("ai"));
3065 	return 0;
3066 }
3067 
3068 /**
3069  * Returns a proxy table array for all sides matching the given SSF.
3070  * - Arg 1: SSF
3071  * - Ret 1: proxy table array
3072  */
intf_get_sides(lua_State * L)3073 int game_lua_kernel::intf_get_sides(lua_State* L)
3074 {
3075 	LOG_LUA << "intf_get_sides called: this = " << std::hex << this << std::dec << " myname = " << my_name() << std::endl;
3076 	std::vector<int> sides;
3077 	const vconfig ssf = luaW_checkvconfig(L, 1, true);
3078 	if(ssf.null()) {
3079 		for (unsigned side_number = 1; side_number <= teams().size(); ++side_number) {
3080 			sides.push_back(side_number);
3081 		}
3082 	} else {
3083 		filter_context & fc = game_state_;
3084 
3085 		side_filter filter(ssf, &fc);
3086 		sides = filter.get_teams();
3087 	}
3088 
3089 	lua_settop(L, 0);
3090 	lua_createtable(L, sides.size(), 0);
3091 	unsigned index = 1;
3092 	for(int side : sides) {
3093 		luaW_pushteam(L, board().get_team(side));
3094 		lua_rawseti(L, -2, index);
3095 		++index;
3096 	}
3097 
3098 	return 1;
3099 }
3100 
3101 /**
3102  * .Returns information about the global traits known to the engine.
3103  * - Ret 1: Table with named fields holding wml tables describing the traits.
3104  */
intf_get_traits(lua_State * L)3105 static int intf_get_traits(lua_State* L)
3106 {
3107 	lua_newtable(L);
3108 	for(const config& trait : unit_types.traits()) {
3109 		const std::string& id = trait["id"];
3110 		//It seems the engine does nowhere check the id field for emptyness or duplicates
3111 		//(also not later on).
3112 		//However, the worst thing to happen is that the trait read later overwrites the older one,
3113 		//and this is not the right place for such checks.
3114 		lua_pushstring(L, id.c_str());
3115 		luaW_pushconfig(L, trait);
3116 		lua_rawset(L, -3);
3117 	}
3118 	return 1;
3119 }
3120 
3121 /**
3122  * Adds a modification to a unit.
3123  * - Arg 1: unit.
3124  * - Arg 2: string.
3125  * - Arg 3: WML table.
3126  * - Arg 4: (optional) Whether to add to [modifications] - default true
3127  */
intf_add_modification(lua_State * L)3128 static int intf_add_modification(lua_State *L)
3129 {
3130 	unit& u = luaW_checkunit(L, 1);
3131 	char const *m = luaL_checkstring(L, 2);
3132 	std::string sm = m;
3133 	if (sm == "advance") { // Maintain backwards compatibility
3134 		sm = "advancement";
3135 		deprecated_message("\"advance\" modification type", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use \"advancement\" instead.");
3136 	}
3137 	if (sm != "advancement" && sm != "object" && sm != "trait") {
3138 		return luaL_argerror(L, 2, "unknown modification type");
3139 	}
3140 	bool write_to_mods = true;
3141 	if (!lua_isnone(L, 4)) {
3142 		write_to_mods = luaW_toboolean(L, 4);
3143 	}
3144 	if(sm.empty()) {
3145 		write_to_mods = false;
3146 	}
3147 
3148 	config cfg = luaW_checkconfig(L, 3);
3149 	u.add_modification(sm, cfg, !write_to_mods);
3150 	return 0;
3151 }
3152 
3153 /**
3154  * Removes modifications from a unit
3155  * - Arg 1: unit
3156  * - Arg 2: table (filter as [filter_wml])
3157  * - Arg 3: type of modification (default "object")
3158  */
intf_remove_modifications(lua_State * L)3159 static int intf_remove_modifications(lua_State *L)
3160 {
3161 	unit& u = luaW_checkunit(L, 1);
3162 	config filter = luaW_checkconfig(L, 2);
3163 	std::string tag = luaL_optstring(L, 3, "object");
3164 	//TODO
3165 	if(filter.attribute_count() == 1 && filter.all_children_count() == 0 && filter.attribute_range().front().first == "duration") {
3166 		u.expire_modifications(filter["duration"]);
3167 	} else {
3168 		for(config& obj : u.get_modifications().child_range(tag)) {
3169 			if(obj.matches(filter)) {
3170 				obj["duration"] = "now";
3171 			}
3172 		}
3173 		u.expire_modifications("now");
3174 	}
3175 	return 0;
3176 }
3177 
3178 /**
3179  * Advances a unit if the unit has enough xp.
3180  * - Arg 1: unit.
3181  * - Arg 2: optional boolean whether to animate the advancement.
3182  * - Arg 3: optional boolean whether to fire advancement events.
3183  */
intf_advance_unit(lua_State * L)3184 static int intf_advance_unit(lua_State *L)
3185 {
3186 	events::command_disabler command_disabler;
3187 	//TODO: check whether the unit is on the map.
3188 	unit& u = luaW_checkunit(L, 1, true);
3189 	advance_unit_params par(u.get_location());
3190 	if(lua_isboolean(L, 2)) {
3191 		par.animate(luaW_toboolean(L, 2));
3192 	}
3193 	if(lua_isboolean(L, 3)) {
3194 		par.fire_events(luaW_toboolean(L, 3));
3195 	}
3196 	advance_unit_at(par);
3197 	return 0;
3198 }
3199 
3200 
3201 /**
3202  * Adds a new known unit type to the help system.
3203  * - Arg 1: string.
3204  */
intf_add_known_unit(lua_State * L)3205 static int intf_add_known_unit(lua_State *L)
3206 {
3207 	char const *ty = luaL_checkstring(L, 1);
3208 	if(!unit_types.find(ty))
3209 	{
3210 		std::stringstream ss;
3211 		ss << "unknown unit type: '" << ty << "'";
3212 		return luaL_argerror(L, 1, ss.str().c_str());
3213 	}
3214 	preferences::encountered_units().insert(ty);
3215 	return 0;
3216 }
3217 
3218 /**
3219  * Adds an overlay on a tile.
3220  * - Arg 1: location.
3221  * - Arg 2: WML table.
3222  */
intf_add_tile_overlay(lua_State * L)3223 int game_lua_kernel::intf_add_tile_overlay(lua_State *L)
3224 {
3225 	map_location loc = luaW_checklocation(L, 1);
3226 	config cfg = luaW_checkconfig(L, 2);
3227 
3228 	if (game_display_) {
3229 		game_display_->add_overlay(loc, cfg["image"], cfg["halo"],
3230 			cfg["team_name"], cfg["name"], cfg["visible_in_fog"].to_bool(true));
3231 	}
3232 	return 0;
3233 }
3234 
3235 /**
3236  * Removes an overlay from a tile.
3237  * - Arg 1: location.
3238  * - Arg 2: optional string.
3239  */
intf_remove_tile_overlay(lua_State * L)3240 int game_lua_kernel::intf_remove_tile_overlay(lua_State *L)
3241 {
3242 	map_location loc = luaW_checklocation(L, 1);
3243 	char const *m = lua_tostring(L, 2);
3244 
3245 	if (m) {
3246 		if (game_display_) {
3247 			game_display_->remove_single_overlay(loc, m);
3248 		}
3249 	} else {
3250 		if (game_display_) {
3251 			game_display_->remove_overlay(loc);
3252 		}
3253 	}
3254 	return 0;
3255 }
3256 
intf_log_replay(lua_State * L)3257 int game_lua_kernel::intf_log_replay(lua_State* L)
3258 {
3259 	replay& recorder = play_controller_.get_replay();
3260 	const int nargs = lua_gettop(L);
3261 	if(nargs < 2 || nargs > 3) {
3262 		return luaL_error(L, "Wrong number of arguments to ai.log_replay() - should be 2 or 3 arguments.");
3263 	}
3264 	const std::string key = nargs == 2 ? luaL_checkstring(L, 1) : luaL_checkstring(L, 2);
3265 	config cfg;
3266 	if(nargs == 2) {
3267 		recorder.add_log_data(key, luaL_checkstring(L, 2));
3268 	} else if(luaW_toconfig(L, 3, cfg)) {
3269 		recorder.add_log_data(luaL_checkstring(L, 1), key, cfg);
3270 	} else if(!lua_isstring(L, 3)) {
3271 		return luaL_argerror(L, 3, "accepts only string or config");
3272 	} else {
3273 		recorder.add_log_data(luaL_checkstring(L, 1), key, luaL_checkstring(L, 3));
3274 	}
3275 	return 0;
3276 }
3277 
3278 /// Adding new events
intf_add_event(lua_State * L)3279 int game_lua_kernel::intf_add_event(lua_State *L)
3280 {
3281 	vconfig cfg(luaW_checkvconfig(L, 1));
3282 	game_events::manager & man = *game_state_.events_manager_;
3283 
3284 	if (!cfg["delayed_variable_substitution"].to_bool(true)) {
3285 		man.add_event_handler(cfg.get_parsed_config());
3286 	} else {
3287 		man.add_event_handler(cfg.get_config());
3288 	}
3289 	return 0;
3290 }
3291 
intf_remove_event(lua_State * L)3292 int game_lua_kernel::intf_remove_event(lua_State *L)
3293 {
3294 	game_state_.events_manager_->remove_event_handler(luaL_checkstring(L, 1));
3295 	return 0;
3296 }
3297 
intf_color_adjust(lua_State * L)3298 int game_lua_kernel::intf_color_adjust(lua_State *L)
3299 {
3300 	if (game_display_) {
3301 		vconfig cfg(luaW_checkvconfig(L, 1));
3302 
3303 		game_display_->adjust_color_overlay(cfg["red"], cfg["green"], cfg["blue"]);
3304 		game_display_->invalidate_all();
3305 		game_display_->draw(true,true);
3306 	}
3307 	return 0;
3308 }
3309 
3310 /**
3311  * Delays engine for a while.
3312  * - Arg 1: integer.
3313  * - Arg 2: boolean (optional).
3314  */
intf_delay(lua_State * L)3315 int game_lua_kernel::intf_delay(lua_State *L)
3316 {
3317 	if(gamedata().phase() == game_data::PRELOAD || gamedata().phase() == game_data::PRESTART || gamedata().phase() == game_data::INITIAL) {
3318 		//don't call play_slice if the game ui is not active yet.
3319 		return 0;
3320 	}
3321 	events::command_disabler command_disabler;
3322 	lua_Integer delay = luaL_checkinteger(L, 1);
3323 	if(delay == 0) {
3324 		play_controller_.play_slice(false);
3325 		return 0;
3326 	}
3327 	if(luaW_toboolean(L, 2) && game_display_ && game_display_->turbo_speed() > 0) {
3328 		delay /= game_display_->turbo_speed();
3329 	}
3330 	const unsigned final = SDL_GetTicks() + delay;
3331 	do {
3332 		play_controller_.play_slice(false);
3333 		CVideo::delay(10);
3334 	} while (static_cast<int>(final - SDL_GetTicks()) > 0);
3335 	return 0;
3336 }
3337 
intf_label(lua_State * L)3338 int game_lua_kernel::intf_label(lua_State *L)
3339 {
3340 	if (game_display_) {
3341 		vconfig cfg(luaW_checkvconfig(L, 1));
3342 
3343 		game_display &screen = *game_display_;
3344 
3345 		terrain_label label(screen.labels(), cfg.get_config());
3346 
3347 		screen.labels().set_label(label.location(), label.text(), label.creator(), label.team_name(), label.color(),
3348 				label.visible_in_fog(), label.visible_in_shroud(), label.immutable(), label.category(), label.tooltip());
3349 	}
3350 	return 0;
3351 }
3352 
intf_redraw(lua_State * L)3353 int game_lua_kernel::intf_redraw(lua_State *L)
3354 {
3355 	if (game_display_) {
3356 		game_display & screen = *game_display_;
3357 
3358 		vconfig cfg(luaW_checkvconfig(L, 1));
3359 		bool clear_shroud(luaW_toboolean(L, 2));
3360 
3361 		// We do this twice so any applicable redraws happen both before and after
3362 		// any events caused by redrawing shroud are fired
3363 		bool result = screen.maybe_rebuild();
3364 		if (!result) {
3365 			screen.invalidate_all();
3366 		}
3367 
3368 		if (clear_shroud) {
3369 			side_filter filter(cfg, &game_state_);
3370 			for (const int side : filter.get_teams()){
3371 				actions::clear_shroud(side);
3372 			}
3373 			screen.recalculate_minimap();
3374 		}
3375 
3376 		result = screen.maybe_rebuild();
3377 		if (!result) {
3378 			screen.invalidate_all();
3379 		}
3380 
3381 		screen.draw(true,true);
3382 	}
3383 	return 0;
3384 }
3385 
3386 /**
3387  * Lua frontend to the modify_ai functionality
3388  * - Arg 1: config.
3389  */
intf_modify_ai_old(lua_State * L)3390 static int intf_modify_ai_old(lua_State *L)
3391 {
3392 	config cfg;
3393 	luaW_toconfig(L, 1, cfg);
3394 	int side = cfg["side"];
3395 	deprecated_message("wesnoth.modify_ai", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use wesnoth.add_ai_component, wesnoth.delete_ai_component, or wesnoth.change_ai_component.");
3396 	ai::manager::get_singleton().modify_active_ai_for_side(side, cfg);
3397 	return 0;
3398 }
3399 
cfun_exec_candidate_action(lua_State * L)3400 static int cfun_exec_candidate_action(lua_State *L)
3401 {
3402 	bool exec = luaW_toboolean(L, -1);
3403 	lua_pop(L, 1);
3404 
3405 	lua_getfield(L, -1, "ca_ptr");
3406 
3407 	ai::candidate_action *ca = static_cast<ai::candidate_action*>(lua_touserdata(L, -1));
3408 	lua_pop(L, 2);
3409 	if (exec) {
3410 		ca->execute();
3411 		return 0;
3412 	}
3413 	lua_pushinteger(L, ca->evaluate());
3414 	return 1;
3415 }
3416 
cfun_exec_stage(lua_State * L)3417 static int cfun_exec_stage(lua_State *L)
3418 {
3419 	lua_getfield(L, -1, "stg_ptr");
3420 	ai::stage *stg = static_cast<ai::stage*>(lua_touserdata(L, -1));
3421 	lua_pop(L, 2);
3422 	stg->play_stage();
3423 	return 0;
3424 }
3425 
push_component(lua_State * L,ai::component * c,const std::string & ct="")3426 static void push_component(lua_State *L, ai::component* c, const std::string &ct = "")
3427 {
3428 	lua_createtable(L, 0, 0); // Table for a component
3429 
3430 	lua_pushstring(L, "name");
3431 	lua_pushstring(L, c->get_name().c_str());
3432 	lua_rawset(L, -3);
3433 
3434 	lua_pushstring(L, "engine");
3435 	lua_pushstring(L, c->get_engine().c_str());
3436 	lua_rawset(L, -3);
3437 
3438 	lua_pushstring(L, "id");
3439 	lua_pushstring(L, c->get_id().c_str());
3440 	lua_rawset(L, -3);
3441 
3442 	if (ct == "candidate_action") {
3443 		lua_pushstring(L, "ca_ptr");
3444 		lua_pushlightuserdata(L, c);
3445 		lua_rawset(L, -3);
3446 
3447 		lua_pushstring(L, "exec");
3448 		lua_pushcclosure(L, &cfun_exec_candidate_action, 0);
3449 		lua_rawset(L, -3);
3450 	}
3451 
3452 	if (ct == "stage") {
3453 		lua_pushstring(L, "stg_ptr");
3454 		lua_pushlightuserdata(L, c);
3455 		lua_rawset(L, -3);
3456 
3457 		lua_pushstring(L, "exec");
3458 		lua_pushcclosure(L, &cfun_exec_stage, 0);
3459 		lua_rawset(L, -3);
3460 	}
3461 
3462 
3463 	std::vector<std::string> c_types = c->get_children_types();
3464 
3465 	for (std::vector<std::string>::const_iterator t = c_types.begin(); t != c_types.end(); ++t)
3466 	{
3467 		std::vector<ai::component*> children = c->get_children(*t);
3468 		std::string type = *t;
3469 		if (type == "aspect" || type == "goal" || type == "engine")
3470 		{
3471 			continue;
3472 		}
3473 
3474 		lua_pushstring(L, type.c_str());
3475 		lua_createtable(L, 0, 0); // this table will be on top of the stack during recursive calls
3476 
3477 		for (std::vector<ai::component*>::const_iterator i = children.begin(); i != children.end(); ++i)
3478 		{
3479 			lua_pushstring(L, (*i)->get_name().c_str());
3480 			push_component(L, *i, type);
3481 			lua_rawset(L, -3);
3482 
3483 			//if (type == "candidate_action")
3484 			//{
3485 			//	ai::candidate_action *ca = dynamic_cast<ai::candidate_action*>(*i);
3486 			//	ca->execute();
3487 			//}
3488 		}
3489 
3490 		lua_rawset(L, -3); // setting the child table
3491 	}
3492 
3493 
3494 }
3495 
3496 /**
3497  * Debug access to the ai tables
3498  * - Arg 1: int
3499  * - Ret 1: ai table
3500  */
intf_debug_ai(lua_State * L)3501 static int intf_debug_ai(lua_State *L)
3502 {
3503 	if (!game_config::debug) { // This function works in debug mode only
3504 		return 0;
3505 	}
3506 	int side = lua_tointeger(L, 1);
3507 	lua_pop(L, 1);
3508 
3509 	ai::component* c = ai::manager::get_singleton().get_active_ai_holder_for_side_dbg(side).get_component(nullptr, "");
3510 
3511 	// Bad, but works
3512 	std::vector<ai::component*> engines = c->get_children("engine");
3513 	ai::engine_lua* lua_engine = nullptr;
3514 	for (std::vector<ai::component*>::const_iterator i = engines.begin(); i != engines.end(); ++i)
3515 	{
3516 		if ((*i)->get_name() == "lua")
3517 		{
3518 			lua_engine = dynamic_cast<ai::engine_lua *>(*i);
3519 		}
3520 	}
3521 
3522 	// Better way, but doesn't work
3523 	//ai::component* e = ai::manager::get_singleton().get_active_ai_holder_for_side_dbg(side).get_component(c, "engine[lua]");
3524 	//ai::engine_lua* lua_engine = dynamic_cast<ai::engine_lua *>(e);
3525 
3526 	if (lua_engine == nullptr)
3527 	{
3528 		//no lua engine is defined for this side.
3529 		//so set up a dummy engine
3530 
3531 		ai::ai_composite * ai_ptr = dynamic_cast<ai::ai_composite *>(c);
3532 
3533 		assert(ai_ptr);
3534 
3535 		ai::ai_context& ai_context = ai_ptr->get_ai_context();
3536 		config cfg = ai::configuration::get_default_ai_parameters();
3537 
3538 		lua_engine = new ai::engine_lua(ai_context, cfg);
3539 		LOG_LUA << "Created new dummy lua-engine for debug_ai(). \n";
3540 
3541 		//and add the dummy engine as a component
3542 		//to the manager, so we could use it later
3543 		cfg.add_child("engine", lua_engine->to_config());
3544 		ai::component_manager::add_component(c, "engine[]", cfg);
3545 	}
3546 
3547 	lua_engine->push_ai_table(); // stack: [-1: ai_context]
3548 
3549 	lua_pushstring(L, "components");
3550 	push_component(L, c); // stack: [-1: component tree; -2: ai context]
3551 	lua_rawset(L, -3);
3552 
3553 	return 1;
3554 }
3555 
3556 /// Allow undo sets the flag saying whether the event has mutated the game to false.
intf_allow_end_turn(lua_State * L)3557 int game_lua_kernel::intf_allow_end_turn(lua_State * L)
3558 {
3559 	gamedata().set_allow_end_turn(luaW_toboolean(L, 1));
3560 	return 0;
3561 }
3562 
3563 /// Allow undo sets the flag saying whether the event has mutated the game to false.
intf_allow_undo(lua_State * L)3564 int game_lua_kernel::intf_allow_undo(lua_State * L)
3565 {
3566 	if(lua_isboolean(L, 1)) {
3567 		play_controller_.pump().set_undo_disabled(!luaW_toboolean(L, 1));
3568 	}
3569 	else {
3570 		play_controller_.pump().set_undo_disabled(false);
3571 	}
3572 	return 0;
3573 }
3574 
intf_cancel_action(lua_State *)3575 int game_lua_kernel::intf_cancel_action(lua_State*)
3576 {
3577 	play_controller_.pump().set_action_canceled();
3578 	return 0;
3579 }
3580 
3581 /// Adding new time_areas dynamically with Standard Location Filters.
intf_add_time_area(lua_State * L)3582 int game_lua_kernel::intf_add_time_area(lua_State * L)
3583 {
3584 	log_scope("time_area");
3585 
3586 	vconfig cfg(luaW_checkvconfig(L, 1));
3587 	const std::string id = cfg["id"];
3588 
3589 	std::set<map_location> locs;
3590 	const terrain_filter filter(cfg, &game_state_);
3591 	filter.get_locations(locs, true);
3592 	config parsed_cfg = cfg.get_parsed_config();
3593 	tod_man().add_time_area(id, locs, parsed_cfg);
3594 	LOG_LUA << "Lua inserted time_area '" << id << "'\n";
3595 	return 0;
3596 }
3597 
3598 /// Removing new time_areas dynamically with Standard Location Filters.
intf_remove_time_area(lua_State * L)3599 int game_lua_kernel::intf_remove_time_area(lua_State * L)
3600 {
3601 	log_scope("remove_time_area");
3602 
3603 	const char * id = luaL_checkstring(L, 1);
3604 	tod_man().remove_time_area(id);
3605 	LOG_LUA << "Lua removed time_area '" << id << "'\n";
3606 
3607 	return 0;
3608 }
3609 
3610 /// Replacing the current time of day schedule.
intf_replace_schedule(lua_State * L)3611 int game_lua_kernel::intf_replace_schedule(lua_State * L)
3612 {
3613 	vconfig cfg = luaW_checkvconfig(L, 1);
3614 
3615 	if(cfg.get_children("time").empty()) {
3616 		ERR_LUA << "attempted to to replace ToD schedule with empty schedule" << std::endl;
3617 	} else {
3618 		tod_man().replace_schedule(cfg.get_parsed_config());
3619 		if (game_display_) {
3620 			game_display_->new_turn();
3621 		}
3622 		LOG_LUA << "replaced ToD schedule\n";
3623 	}
3624 	return 0;
3625 }
3626 
intf_set_time_of_day(lua_State * L)3627 int game_lua_kernel::intf_set_time_of_day(lua_State * L)
3628 {
3629 	if(!game_display_) {
3630 		return 0;
3631 	}
3632 	std::string area_id;
3633 	size_t area_i = 0;
3634 	if (lua_isstring(L, 2)) {
3635 		area_id = lua_tostring(L, 1);
3636 		std::vector<std::string> area_ids = tod_man().get_area_ids();
3637 		area_i = std::distance(area_ids.begin(), std::find(area_ids.begin(), area_ids.end(), area_id));
3638 		if(area_i >= area_ids.size()) {
3639 			return luaL_argerror(L, 1, "invalid time area ID");
3640 		}
3641 	}
3642 	int is_num = false;
3643 	int new_time = lua_tonumberx(L, 1, &is_num) - 1;
3644 	const std::vector<time_of_day>& times = area_id.empty()
3645 		? tod_man().times()
3646 		: tod_man().times(area_i);
3647 	int num_times = times.size();
3648 	if(!is_num) {
3649 		std::string time_id = luaL_checkstring(L, 1);
3650 		new_time = 0;
3651 		for(const time_of_day& time : times) {
3652 			if(time_id == time.id) {
3653 				break;
3654 			}
3655 			new_time++;
3656 		}
3657 		if(new_time >= num_times) {
3658 			return luaL_argerror(L, 1, "invalid time of day ID");
3659 		}
3660 	}
3661 	if(new_time < 0 || new_time >= num_times) {
3662 		return luaL_argerror(L, 1, "invalid time of day index");
3663 	}
3664 
3665 	if(area_id.empty()) {
3666 		tod_man().set_current_time(new_time);
3667 	} else {
3668 		tod_man().set_current_time(new_time, area_i);
3669 	}
3670 	return 0;
3671 }
3672 
intf_scroll(lua_State * L)3673 int game_lua_kernel::intf_scroll(lua_State * L)
3674 {
3675 	int x = luaL_checkinteger(L, 1), y = luaL_checkinteger(L, 2);
3676 
3677 	if (game_display_) {
3678 		game_display_->scroll(x, y, true);
3679 		game_display_->draw(true, true);
3680 	}
3681 
3682 	return 0;
3683 }
3684 
3685 namespace {
3686 	struct lua_report_generator : reports::generator
3687 	{
3688 		lua_State *mState;
3689 		std::string name;
lua_report_generator__anon384f78800411::lua_report_generator3690 		lua_report_generator(lua_State *L, const std::string &n)
3691 			: mState(L), name(n) {}
3692 		virtual config generate(reports::context & rc);
3693 	};
3694 
generate(reports::context &)3695 	config lua_report_generator::generate(reports::context & /*rc*/)
3696 	{
3697 		lua_State *L = mState;
3698 		config cfg;
3699 		if (!luaW_getglobal(L, "wesnoth", "theme_items", name))
3700 			return cfg;
3701 		if (!luaW_pcall(L, 0, 1)) return cfg;
3702 		luaW_toconfig(L, -1, cfg);
3703 		lua_pop(L, 1);
3704 		return cfg;
3705 	}
3706 }//unnamed namespace for lua_report_generator
3707 
3708 /**
3709  * Executes its upvalue as a theme item generator.
3710  */
impl_theme_item(lua_State * L,std::string m)3711 int game_lua_kernel::impl_theme_item(lua_State *L, std::string m)
3712 {
3713 	reports::context temp_context = reports::context(board(), *game_display_, tod_man(), play_controller_.get_whiteboard(), play_controller_.get_mouse_handler_base());
3714 	luaW_pushconfig(L, reports_.generate_report(m.c_str(), temp_context , true));
3715 	return 1;
3716 }
3717 
3718 /**
3719  * Creates a field of the theme_items table and returns it (__index metamethod).
3720  */
impl_theme_items_get(lua_State * L)3721 int game_lua_kernel::impl_theme_items_get(lua_State *L)
3722 {
3723 	char const *m = luaL_checkstring(L, 2);
3724 	lua_cpp::push_closure(L, std::bind(&game_lua_kernel::impl_theme_item, this, _1, std::string(m)), 0);
3725 	lua_pushvalue(L, 2);
3726 	lua_pushvalue(L, -2);
3727 	lua_rawset(L, 1);
3728 	reports_.register_generator(m, new lua_report_generator(L, m));
3729 	return 1;
3730 }
3731 
3732 /**
3733  * Sets a field of the theme_items table (__newindex metamethod).
3734  */
impl_theme_items_set(lua_State * L)3735 int game_lua_kernel::impl_theme_items_set(lua_State *L)
3736 {
3737 	char const *m = luaL_checkstring(L, 2);
3738 	lua_pushvalue(L, 2);
3739 	lua_pushvalue(L, 3);
3740 	lua_rawset(L, 1);
3741 	reports_.register_generator(m, new lua_report_generator(L, m));
3742 	return 0;
3743 }
3744 
3745 /**
3746  * Gets all the WML variables currently set.
3747  * - Ret 1: WML table
3748  */
intf_get_all_vars(lua_State * L)3749 int game_lua_kernel::intf_get_all_vars(lua_State *L) {
3750 	luaW_pushconfig(L, gamedata().get_variables());
3751 	return 1;
3752 }
3753 
3754 /**
3755  * Teeleports a unit to a location.
3756  * Arg 1: unit
3757  * Arg 2: target location
3758  * Arg 3: bool (ignore_passability)
3759  * Arg 4: bool (clear_shroud)
3760  * Arg 5: bool (animate)
3761  */
intf_teleport(lua_State * L)3762 int game_lua_kernel::intf_teleport(lua_State *L)
3763 {
3764 	events::command_disabler command_disabler;
3765 	unit_ptr u = luaW_checkunit_ptr(L, 1, true);
3766 	map_location dst = luaW_checklocation(L, 2);
3767 	bool check_passability = !luaW_toboolean(L, 3);
3768 	bool clear_shroud = luaW_toboolean(L, 4);
3769 	bool animate = luaW_toboolean(L, 5);
3770 
3771 	if (dst == u->get_location() || !map().on_board(dst)) {
3772 		return 0;
3773 	}
3774 	const map_location vacant_dst = find_vacant_tile(dst, pathfind::VACANT_ANY, check_passability ? u.get() : nullptr);
3775 	if (!map().on_board(vacant_dst)) {
3776 		return 0;
3777 	}
3778 	// Clear the destination hex before the move (so the animation can be seen).
3779 	actions::shroud_clearer clearer;
3780 	if ( clear_shroud ) {
3781 		clearer.clear_dest(vacant_dst, *u);
3782 	}
3783 
3784 	map_location src_loc = u->get_location();
3785 
3786 	std::vector<map_location> teleport_path;
3787 	teleport_path.push_back(src_loc);
3788 	teleport_path.push_back(vacant_dst);
3789 	unit_display::move_unit(teleport_path, u, animate);
3790 
3791 	units().move(src_loc, vacant_dst);
3792 	unit::clear_status_caches();
3793 
3794 	u = &*units().find(vacant_dst);
3795 	u->anim_comp().set_standing();
3796 
3797 	if ( clear_shroud ) {
3798 		// Now that the unit is visibly in position, clear the shroud.
3799 		clearer.clear_unit(vacant_dst, *u);
3800 	}
3801 
3802 	if (map().is_village(vacant_dst)) {
3803 		actions::get_village(vacant_dst, u->side());
3804 	}
3805 
3806 	game_display_->invalidate_unit_after_move(src_loc, vacant_dst);
3807 	game_display_->draw();
3808 
3809 	// Sighted events.
3810 	clearer.fire_events();
3811 	return 0;
3812 }
3813 
3814 /**
3815  * Removes a sound source by its ID
3816  * Arg 1: sound source ID
3817  */
intf_remove_sound_source(lua_State * L)3818 int game_lua_kernel::intf_remove_sound_source(lua_State *L)
3819 {
3820 	soundsource::manager* man = play_controller_.get_soundsource_man();
3821 	std::string id = luaL_checkstring(L, 1);
3822 	man->remove(id);
3823 	return 0;
3824 }
3825 
3826 /**
3827  * Add a new sound source
3828  * Arg 1: Table containing keyword arguments
3829  */
intf_add_sound_source(lua_State * L)3830 int game_lua_kernel::intf_add_sound_source(lua_State *L)
3831 {
3832 	soundsource::manager* man = play_controller_.get_soundsource_man();
3833 	config cfg = luaW_checkconfig(L, 1);
3834 	try {
3835 		soundsource::sourcespec spec(cfg);
3836 		man->add(spec);
3837 		man->update();
3838 	} catch (const bad_lexical_cast &) {
3839 		ERR_LUA << "Error when parsing sound_source config: invalid parameter." << std::endl;
3840 		ERR_LUA << "sound_source config was: " << cfg.debug() << std::endl;
3841 		ERR_LUA << "Skipping this sound source..." << std::endl;
3842 	}
3843 	return 0;
3844 }
3845 
3846 /**
3847  * Get an existing sound source
3848  * Arg 1: The sound source ID
3849  * Return: Config of sound source info, or nil if it didn't exist
3850  * This is a copy of the sound source info, so you need to call
3851  * add_sound_source again after changing it.
3852  */
intf_get_sound_source(lua_State * L)3853 int game_lua_kernel::intf_get_sound_source(lua_State *L)
3854 {
3855 	soundsource::manager* man = play_controller_.get_soundsource_man();
3856 	std::string id = luaL_checkstring(L, 1);
3857 	config cfg = man->get(id);
3858 	if(cfg.empty()) {
3859 		return 0;
3860 	}
3861 	// Sound sources do not know their own string ID
3862 	// Thus, we need to add this manually
3863 	cfg["id"] = id;
3864 	luaW_pushconfig(L, cfg);
3865 	return 1;
3866 }
3867 
3868 /**
3869  * Logs a message
3870  * Arg 1: (optional) Logger; "wml" for WML errors or deprecations
3871  * Arg 2: Message
3872  * Arg 3: Whether to print to chat (always true if arg 1 is "wml")
3873  */
intf_log(lua_State * L)3874 int game_lua_kernel::intf_log(lua_State *L)
3875 {
3876 	const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) : "";
3877 	const std::string& msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
3878 
3879 	if(logger == "wml" || logger == "WML") {
3880 		lg::wml_error() << msg << '\n';
3881 	} else {
3882 		bool in_chat = luaW_toboolean(L, -1);
3883 		game_state_.events_manager_->pump().put_wml_message(logger,msg,in_chat);
3884 	}
3885 	return 0;
3886 }
3887 
intf_get_fog_or_shroud(lua_State * L,bool fog)3888 int game_lua_kernel::intf_get_fog_or_shroud(lua_State *L, bool fog)
3889 {
3890 	int side = luaL_checknumber(L, 1);
3891 	map_location loc = luaW_checklocation(L, 2);
3892 	if(side < 1 || static_cast<size_t>(side) > teams().size()) {
3893 		std::string error = "side " + std::to_string(side) + " does not exist";
3894 		return luaL_argerror(L, 1, error.c_str());
3895 	}
3896 
3897 	team& t = board().get_team(side);
3898 	lua_pushboolean(L, fog ? t.fogged(loc) : t.shrouded(loc));
3899 	return 1;
3900 }
3901 
3902 /**
3903  * Implements the lifting and resetting of fog via WML.
3904  * Keeping affect_normal_fog as false causes only the fog override to be affected.
3905  * Otherwise, fog lifting will be implemented similar to normal sight (cannot be
3906  * individually reset and ends at the end of the turn), and fog resetting will, in
3907  * addition to removing overrides, extend the specified teams' normal fog to all
3908  * hexes.
3909  *
3910  * Arg 1: (optional) Side number, or list of side numbers
3911  * Arg 2: List of locations; each is a two-element array or a table with x and y keys
3912  * Arg 3: (optional) boolean
3913  */
intf_toggle_fog(lua_State * L,const bool clear)3914 int game_lua_kernel::intf_toggle_fog(lua_State *L, const bool clear)
3915 {
3916 	bool affect_normal_fog = false;
3917 	if(lua_isboolean(L, -1)) {
3918 		affect_normal_fog = luaW_toboolean(L, -1);
3919 	}
3920 	std::set<int> sides;
3921 	if(lua_isnumber(L, 1)) {
3922 		sides.insert(lua_tonumber(L, 1));
3923 	} else if(lua_istable(L, 1) && lua_istable(L, 2)) {
3924 		const auto& v = lua_check<std::vector<int>>(L, 1);
3925 		sides.insert(v.begin(), v.end());
3926 	} else {
3927 		for(const team& t : teams()) {
3928 			sides.insert(t.side()+1);
3929 		}
3930 	}
3931 	const auto& v_locs = lua_check<std::vector<map_location>>(L, lua_istable(L, 2) ? 2 : 1);
3932 	std::set<map_location> locs(v_locs.begin(), v_locs.end());
3933 
3934 	for(const int &side_num : sides) {
3935 		if(side_num < 1 || static_cast<size_t>(side_num) > teams().size()) {
3936 			continue;
3937 		}
3938 		team &t = board().get_team(side_num);
3939 		if(!clear) {
3940 			// Extend fog.
3941 			t.remove_fog_override(locs);
3942 			if(affect_normal_fog) {
3943 				t.refog();
3944 			}
3945 		} else if(!affect_normal_fog) {
3946 			// Force the locations clear of fog.
3947 			t.add_fog_override(locs);
3948 		} else {
3949 			// Simply clear fog from the locations.
3950 			for(const map_location &hex : locs) {
3951 				t.clear_fog(hex);
3952 			}
3953 		}
3954 	}
3955 
3956 	// Flag a screen update.
3957 	game_display_->recalculate_minimap();
3958 	game_display_->invalidate_all();
3959 	return 0;
3960 }
3961 
3962 // Invokes a synced command
intf_invoke_synced_command(lua_State * L)3963 static int intf_invoke_synced_command(lua_State* L)
3964 {
3965 	const std::string name = luaL_checkstring(L, 1);
3966 	auto it = synced_command::registry().find(name);
3967 	config cmd;
3968 	if(it == synced_command::registry().end()) {
3969 		// Custom command
3970 		if(!luaW_getglobal(L, "wesnoth", "custom_synced_commands", name)) {
3971 			return luaL_argerror(L, 1, "Unknown synced command");
3972 		}
3973 		config& cmd_tag = cmd.child_or_add("custom_command");
3974 		cmd_tag["name"] = name;
3975 		if(!lua_isnoneornil(L, 2)) {
3976 			cmd_tag.add_child("data", luaW_checkconfig(L, 2));
3977 		}
3978 	} else {
3979 		// Built-in command
3980 		cmd.add_child(name, luaW_checkconfig(L, 2));
3981 	}
3982 	// Now just forward to the WML action.
3983 	luaW_getglobal(L, "wesnoth", "wml_actions", "do_command");
3984 	luaW_pushconfig(L, cmd);
3985 	luaW_pcall(L, 1, 0);
3986 	return 0;
3987 }
3988 
3989 // END CALLBACK IMPLEMENTATION
3990 
board()3991 game_board & game_lua_kernel::board() {
3992 	return game_state_.board_;
3993 }
3994 
units()3995 unit_map & game_lua_kernel::units() {
3996 	return game_state_.board_.units_;
3997 }
3998 
teams()3999 std::vector<team> & game_lua_kernel::teams() {
4000 	return game_state_.board_.teams_;
4001 }
4002 
map() const4003 const gamemap & game_lua_kernel::map() const {
4004 	return game_state_.board_.map();
4005 }
4006 
gamedata()4007 game_data & game_lua_kernel::gamedata() {
4008 	return game_state_.gamedata_;
4009 }
4010 
tod_man()4011 tod_manager & game_lua_kernel::tod_man() {
4012 	return game_state_.tod_manager_;
4013 }
4014 
get_event_info()4015 const game_events::queued_event & game_lua_kernel::get_event_info() {
4016 	return *queued_events_.top();
4017 }
4018 
4019 
game_lua_kernel(game_state & gs,play_controller & pc,reports & reports_object)4020 game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports & reports_object)
4021 	: lua_kernel_base()
4022 	, game_display_(nullptr)
4023 	, game_state_(gs)
4024 	, play_controller_(pc)
4025 	, reports_(reports_object)
4026 	, level_lua_()
4027 	, queued_events_()
4028 	, map_locked_(0)
4029 {
4030 	static game_events::queued_event default_queued_event("_from_lua", "", map_location(), map_location(), config());
4031 	queued_events_.push(&default_queued_event);
4032 
4033 	lua_State *L = mState;
4034 
4035 	cmd_log_ << "Registering game-specific wesnoth lib functions...\n";
4036 
4037 	// Put some callback functions in the scripting environment.
4038 	static luaL_Reg const callbacks[] {
4039 		{ "add_known_unit",           &intf_add_known_unit           },
4040 		{ "add_modification",         &intf_add_modification         },
4041 		{ "advance_unit",             &intf_advance_unit             },
4042 		{ "copy_unit",                &intf_copy_unit                },
4043 		{ "create_animator",          &dispatch<&game_lua_kernel::intf_create_animator>          },
4044 		{ "create_unit",              &intf_create_unit              },
4045 		{ "debug_ai",                 &intf_debug_ai                 },
4046 		{ "eval_conditional",         &intf_eval_conditional         },
4047 		{ "get_era",                  &intf_get_era                  },
4048 		{ "get_traits",               &intf_get_traits               },
4049 		{ "get_viewing_side",         &intf_get_viewing_side         },
4050 		{ "invoke_synced_command",    &intf_invoke_synced_command    },
4051 		{ "modify_ai",                &intf_modify_ai_old            },
4052 		{ "remove_modifications",     &intf_remove_modifications     },
4053 		{ "set_music",                &intf_set_music                },
4054 		{ "sound_volume",             &intf_sound_volume             },
4055 		{ "transform_unit",           &intf_transform_unit           },
4056 		{ "unit_defense",             &intf_unit_defense             },
4057 		{ "unit_movement_cost",       &intf_unit_movement_cost       },
4058 		{ "unit_vision_cost",         &intf_unit_vision_cost         },
4059 		{ "unit_jamming_cost",        &intf_unit_jamming_cost        },
4060 		{ "unit_resistance",          &intf_unit_resistance          },
4061 		{ "unsynced",                 &intf_do_unsynced              },
4062 		{ "add_event_handler",         &dispatch<&game_lua_kernel::intf_add_event                  >        },
4063 		{ "add_fog",                   &dispatch2<&game_lua_kernel::intf_toggle_fog, false         >        },
4064 		{ "add_tile_overlay",          &dispatch<&game_lua_kernel::intf_add_tile_overlay           >        },
4065 		{ "add_time_area",             &dispatch<&game_lua_kernel::intf_add_time_area              >        },
4066 		{ "add_sound_source",          &dispatch<&game_lua_kernel::intf_add_sound_source           >        },
4067 		{ "allow_end_turn",            &dispatch<&game_lua_kernel::intf_allow_end_turn             >        },
4068 		{ "allow_undo",                &dispatch<&game_lua_kernel::intf_allow_undo                 >        },
4069 		{ "append_ai",                 &intf_append_ai                                                      },
4070 		{ "cancel_action",             &dispatch<&game_lua_kernel::intf_cancel_action              >        },
4071 		{ "clear_menu_item",           &dispatch<&game_lua_kernel::intf_clear_menu_item            >        },
4072 		{ "clear_messages",            &dispatch<&game_lua_kernel::intf_clear_messages             >        },
4073 		{ "color_adjust",              &dispatch<&game_lua_kernel::intf_color_adjust               >        },
4074 		{ "delay",                     &dispatch<&game_lua_kernel::intf_delay                      >        },
4075 		{ "end_turn",                  &dispatch<&game_lua_kernel::intf_end_turn                   >        },
4076 		{ "end_level",                 &dispatch<&game_lua_kernel::intf_end_level                  >        },
4077 		{ "erase_unit",                &dispatch<&game_lua_kernel::intf_erase_unit                 >        },
4078 		{ "extract_unit",              &dispatch<&game_lua_kernel::intf_extract_unit               >        },
4079 		{ "find_cost_map",             &dispatch<&game_lua_kernel::intf_find_cost_map              >        },
4080 		{ "find_path",                 &dispatch<&game_lua_kernel::intf_find_path                  >        },
4081 		{ "find_reach",                &dispatch<&game_lua_kernel::intf_find_reach                 >        },
4082 		{ "find_vacant_tile",          &dispatch<&game_lua_kernel::intf_find_vacant_tile           >        },
4083 		{ "fire_event",                &dispatch2<&game_lua_kernel::intf_fire_event, false         >        },
4084 		{ "fire_event_by_id",          &dispatch2<&game_lua_kernel::intf_fire_event, true          >        },
4085 		{ "float_label",               &dispatch<&game_lua_kernel::intf_float_label                >        },
4086 		{ "gamestate_inspector",       &dispatch<&game_lua_kernel::intf_gamestate_inspector        >        },
4087 		{ "get_all_vars",              &dispatch<&game_lua_kernel::intf_get_all_vars               >        },
4088 		{ "get_end_level_data",        &dispatch<&game_lua_kernel::intf_get_end_level_data         >        },
4089 		{ "get_locations",             &dispatch<&game_lua_kernel::intf_get_locations              >        },
4090 		{ "get_map_size",              &dispatch<&game_lua_kernel::intf_get_map_size               >        },
4091 		{ "get_mouseover_tile",        &dispatch<&game_lua_kernel::intf_get_mouseover_tile         >        },
4092 		{ "get_recall_units",          &dispatch<&game_lua_kernel::intf_get_recall_units           >        },
4093 		{ "get_selected_tile",         &dispatch<&game_lua_kernel::intf_get_selected_tile          >        },
4094 		{ "get_sides",                 &dispatch<&game_lua_kernel::intf_get_sides                  >        },
4095 		{ "get_sound_source",          &dispatch<&game_lua_kernel::intf_get_sound_source           >        },
4096 		{ "get_starting_location",     &dispatch<&game_lua_kernel::intf_get_starting_location      >        },
4097 		{ "get_terrain",               &dispatch<&game_lua_kernel::intf_get_terrain                >        },
4098 		{ "get_terrain_info",          &dispatch<&game_lua_kernel::intf_get_terrain_info           >        },
4099 		{ "get_time_of_day",           &dispatch<&game_lua_kernel::intf_get_time_of_day            >        },
4100 		{ "get_unit",                  &dispatch<&game_lua_kernel::intf_get_unit                   >        },
4101 		{ "get_units",                 &dispatch<&game_lua_kernel::intf_get_units                  >        },
4102 		{ "get_variable",              &dispatch<&game_lua_kernel::intf_get_variable               >        },
4103 		{ "get_side_variable",         &dispatch<&game_lua_kernel::intf_get_side_variable          >        },
4104 		{ "get_villages",              &dispatch<&game_lua_kernel::intf_get_villages               >        },
4105 		{ "get_village_owner",         &dispatch<&game_lua_kernel::intf_get_village_owner          >        },
4106 		{ "get_displayed_unit",        &dispatch<&game_lua_kernel::intf_get_displayed_unit         >        },
4107 		{ "highlight_hex",             &dispatch<&game_lua_kernel::intf_highlight_hex              >        },
4108 		{ "is_enemy",                  &dispatch<&game_lua_kernel::intf_is_enemy                   >        },
4109 		{ "label",                     &dispatch<&game_lua_kernel::intf_label                      >        },
4110 		{ "lock_view",                 &dispatch<&game_lua_kernel::intf_lock_view                  >        },
4111 		{ "log_replay",                &dispatch<&game_lua_kernel::intf_log_replay                 >        },
4112 		{ "log",                       &dispatch<&game_lua_kernel::intf_log                        >        },
4113 		{ "match_location",            &dispatch<&game_lua_kernel::intf_match_location             >        },
4114 		{ "match_side",                &dispatch<&game_lua_kernel::intf_match_side                 >        },
4115 		{ "match_unit",                &dispatch<&game_lua_kernel::intf_match_unit                 >        },
4116 		{ "message",                   &dispatch<&game_lua_kernel::intf_message                    >        },
4117 		{ "open_help",                 &dispatch<&game_lua_kernel::intf_open_help                  >        },
4118 		{ "play_sound",                &dispatch<&game_lua_kernel::intf_play_sound                 >        },
4119 		{ "print",                     &dispatch<&game_lua_kernel::intf_print                      >        },
4120 		{ "put_recall_unit",           &dispatch<&game_lua_kernel::intf_put_recall_unit            >        },
4121 		{ "put_unit",                  &dispatch<&game_lua_kernel::intf_put_unit                   >        },
4122 		{ "redraw",                    &dispatch<&game_lua_kernel::intf_redraw                     >        },
4123 		{ "remove_event_handler",      &dispatch<&game_lua_kernel::intf_remove_event               >        },
4124 		{ "remove_fog",                &dispatch2<&game_lua_kernel::intf_toggle_fog, true          >        },
4125 		{ "remove_tile_overlay",       &dispatch<&game_lua_kernel::intf_remove_tile_overlay        >        },
4126 		{ "remove_time_area",          &dispatch<&game_lua_kernel::intf_remove_time_area           >        },
4127 		{ "remove_sound_source",       &dispatch<&game_lua_kernel::intf_remove_sound_source        >        },
4128 		{ "replace_schedule",          &dispatch<&game_lua_kernel::intf_replace_schedule           >        },
4129 		{ "scroll",                    &dispatch<&game_lua_kernel::intf_scroll                     >        },
4130 		{ "scroll_to_tile",            &dispatch<&game_lua_kernel::intf_scroll_to_tile             >        },
4131 		{ "select_hex",                &dispatch<&game_lua_kernel::intf_select_hex                 >        },
4132 		{ "set_time_of_day",           &dispatch<&game_lua_kernel::intf_set_time_of_day            >        },
4133 		{ "deselect_hex",              &dispatch<&game_lua_kernel::intf_deselect_hex               >        },
4134 		{ "select_unit",               &dispatch<&game_lua_kernel::intf_select_unit                >        },
4135 		{ "skip_messages",             &dispatch<&game_lua_kernel::intf_skip_messages              >        },
4136 		{ "is_fogged",                 &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true   >        },
4137 		{ "is_shrouded",               &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false  >        },
4138 		{ "is_skipping_messages",      &dispatch<&game_lua_kernel::intf_is_skipping_messages       >        },
4139 		{ "set_end_campaign_credits",  &dispatch<&game_lua_kernel::intf_set_end_campaign_credits   >        },
4140 		{ "set_end_campaign_text",     &dispatch<&game_lua_kernel::intf_set_end_campaign_text      >        },
4141 		{ "set_menu_item",             &dispatch<&game_lua_kernel::intf_set_menu_item              >        },
4142 		{ "set_next_scenario",         &dispatch<&game_lua_kernel::intf_set_next_scenario          >        },
4143 		{ "set_side_id",               &dispatch<&game_lua_kernel::intf_set_side_id                >        },
4144 		{ "set_terrain",               &dispatch<&game_lua_kernel::intf_set_terrain                >        },
4145 		{ "set_variable",              &dispatch<&game_lua_kernel::intf_set_variable               >        },
4146 		{ "set_side_variable",         &dispatch<&game_lua_kernel::intf_set_side_variable          >        },
4147 		{ "set_village_owner",         &dispatch<&game_lua_kernel::intf_set_village_owner          >        },
4148 		{ "simulate_combat",           &dispatch<&game_lua_kernel::intf_simulate_combat            >        },
4149 		{ "switch_ai",                 &intf_switch_ai                                                      },
4150 		{ "synchronize_choice",        &intf_synchronize_choice                                             },
4151 		{ "synchronize_choices",       &intf_synchronize_choices                                            },
4152 		{ "zoom",                      &dispatch<&game_lua_kernel::intf_zoom                       >        },
4153 		{ "teleport",                  &dispatch<&game_lua_kernel::intf_teleport                   >        },
4154 		{ "unit_ability",              &dispatch<&game_lua_kernel::intf_unit_ability               >        },
4155 		{ "view_locked",               &dispatch<&game_lua_kernel::intf_view_locked                >        },
4156 		{ "place_shroud",              &dispatch2<&game_lua_kernel::intf_shroud_op, true  >                 },
4157 		{ "remove_shroud",             &dispatch2<&game_lua_kernel::intf_shroud_op, false >                 },
4158 		{ nullptr, nullptr }
4159 	};
4160 	std::vector<lua_cpp::Reg> const cpp_callbacks {
4161 		{"add_ai_component", std::bind(intf_modify_ai, _1, "add")},
4162 		{"delete_ai_component", std::bind(intf_modify_ai, _1, "delete")},
4163 		{"change_ai_component", std::bind(intf_modify_ai, _1, "change")},
4164 		{nullptr, nullptr}
4165 	};
4166 	lua_getglobal(L, "wesnoth");
4167 	if (!lua_istable(L,-1)) {
4168 		lua_newtable(L);
4169 	}
4170 	luaL_setfuncs(L, callbacks, 0);
4171 	lua_cpp::set_functions(L, cpp_callbacks);
4172 
4173 	if(play_controller_.get_classification().campaign_type == game_classification::CAMPAIGN_TYPE::TEST) {
4174 		static luaL_Reg const test_callbacks[] {
4175 			{ "fire_wml_menu_item",        &dispatch<&game_lua_kernel::intf_fire_wml_menu_item         >        },
4176 			{ nullptr, nullptr }
4177 		};
4178 		luaL_setfuncs(L, test_callbacks , 0);
4179 	}
4180 
4181 	lua_setglobal(L, "wesnoth");
4182 
4183 	// Create the getside metatable.
4184 	cmd_log_ << lua_team::register_metatable(L);
4185 
4186 	// Create the gettype metatable.
4187 	cmd_log_ << lua_unit_type::register_metatable(L);
4188 
4189 	//Create the getrace metatable
4190 	cmd_log_ << lua_race::register_metatable(L);
4191 
4192 	//Create the unit metatables
4193 	cmd_log_ << lua_units::register_metatables(L);
4194 	cmd_log_ << lua_units::register_attacks_metatables(L);
4195 
4196 	// Create the vconfig metatable.
4197 	cmd_log_ << lua_common::register_vconfig_metatable(L);
4198 
4199 	// Create the unit_types table
4200 	cmd_log_ << lua_unit_type::register_table(L);
4201 
4202 	// Create the ai elements table.
4203 	cmd_log_ << "Adding ai elements table...\n";
4204 
4205 	ai::lua_ai_context::init(L);
4206 
4207 	// Create the current variable with its metatable.
4208 	cmd_log_ << "Adding wesnoth current table...\n";
4209 
4210 	lua_getglobal(L, "wesnoth");
4211 	lua_newuserdata(L, 0);
4212 	lua_createtable(L, 0, 2);
4213 	lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_current_get>);
4214 	lua_setfield(L, -2, "__index");
4215 	lua_pushstring(L, "current config");
4216 	lua_setfield(L, -2, "__metatable");
4217 	lua_setmetatable(L, -2);
4218 	lua_setfield(L, -2, "current");
4219 	lua_pop(L, 1);
4220 
4221 	// Create the playlist table with its metatable
4222 	cmd_log_ << lua_audio::register_table(L);
4223 
4224 	// Create the wml_actions table.
4225 	cmd_log_ << "Adding wml_actions table...\n";
4226 
4227 	lua_getglobal(L, "wesnoth");
4228 	lua_newtable(L);
4229 	lua_setfield(L, -2, "wml_actions");
4230 	lua_pop(L, 1);
4231 
4232 	// Create the wml_conditionals table.
4233 	cmd_log_ << "Adding wml_conditionals table...\n";
4234 
4235 	lua_getglobal(L, "wesnoth");
4236 	lua_newtable(L);
4237 	lua_setfield(L, -2, "wml_conditionals");
4238 	lua_pop(L, 1);
4239 	set_wml_condition("have_unit", &game_events::builtin_conditions::have_unit);
4240 	set_wml_condition("have_location", &game_events::builtin_conditions::have_location);
4241 	set_wml_condition("variable", &game_events::builtin_conditions::variable_matches);
4242 
4243 	// Create the effects table.
4244 	cmd_log_ << "Adding effects table...\n";
4245 
4246 	lua_getglobal(L, "wesnoth");
4247 	lua_newtable(L);
4248 	lua_setfield(L, -2, "effects");
4249 	lua_pop(L, 1);
4250 
4251 	// Create the custom_synced_commands table.
4252 	cmd_log_ << "Adding custom_synced_commands table...\n";
4253 
4254 	lua_getglobal(L, "wesnoth");
4255 	lua_newtable(L);
4256 	lua_setfield(L, -2, "custom_synced_commands");
4257 	lua_pop(L, 1);
4258 
4259 	// Create the game_events table.
4260 	cmd_log_ << "Adding game_events table...\n";
4261 
4262 	lua_getglobal(L, "wesnoth");
4263 	lua_newtable(L);
4264 	lua_setfield(L, -2, "game_events");
4265 	push_locations_table(L);
4266 	lua_setfield(L, -2, "special_locations");
4267 	lua_pop(L, 1);
4268 
4269 	// Create the theme_items table.
4270 	cmd_log_ << "Adding theme_items table...\n";
4271 
4272 	lua_getglobal(L, "wesnoth");
4273 	lua_newtable(L);
4274 	lua_createtable(L, 0, 2);
4275 	lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_get>);
4276 	lua_setfield(L, -2, "__index");
4277 	lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_theme_items_set>);
4278 	lua_setfield(L, -2, "__newindex");
4279 	lua_setmetatable(L, -2);
4280 	lua_setfield(L, -2, "theme_items");
4281 	lua_pop(L, 1);
4282 
4283 	lua_settop(L, 0);
4284 
4285 	for(const auto& handler : game_events::wml_action::registry())
4286 	{
4287 		set_wml_action(handler.first, handler.second);
4288 	}
4289 	luaW_getglobal(L, "wesnoth", "effects");
4290 	for(const std::string& effect : unit::builtin_effects) {
4291 		lua_pushstring(L, effect.c_str());
4292 		push_builtin_effect();
4293 		lua_rawset(L, -3);
4294 	}
4295 	lua_settop(L, 0);
4296 }
4297 
initialize(const config & level)4298 void game_lua_kernel::initialize(const config& level)
4299 {
4300 	lua_State *L = mState;
4301 	assert(level_lua_.empty());
4302 	level_lua_.append_children(level, "lua");
4303 	// Create the sides table.
4304 	// note:
4305 	// This table is redundant to the return value of wesnoth.get_sides({}).
4306 	// Still needed for backwards compatibility.
4307 	lua_settop(L, 0);
4308 	lua_getglobal(L, "wesnoth");
4309 
4310 	lua_pushstring(L, "get_sides");
4311 	lua_rawget(L, -2);
4312 	lua_createtable(L, 0, 0);
4313 
4314 	if (!protected_call(1, 1, std::bind(&lua_kernel_base::log_error, this, _1, _2))) {
4315 		cmd_log_ << "Failed to compute wesnoth.sides\n";
4316 	} else {
4317 		lua_setfield(L, -2, "sides");
4318 		cmd_log_ << "Added wesnoth.sides\n";
4319 	}
4320 
4321 	//Create the races table.
4322 	cmd_log_ << "Adding races table...\n";
4323 
4324 	lua_settop(L, 0);
4325 	lua_getglobal(L, "wesnoth");
4326 	luaW_pushracetable(L);
4327 	lua_setfield(L, -2, "races");
4328 	lua_pop(L, 1);
4329 
4330 	// Execute the preload scripts.
4331 	cmd_log_ << "Running preload scripts...\n";
4332 
4333 	game_config::load_config(game_lua_kernel::preload_config);
4334 	for (const config &cfg : game_lua_kernel::preload_scripts) {
4335 		run_lua_tag(cfg);
4336 	}
4337 	for (const config &cfg : level_lua_.child_range("lua")) {
4338 		run_lua_tag(cfg);
4339 	}
4340 }
4341 
set_game_display(game_display * gd)4342 void game_lua_kernel::set_game_display(game_display * gd) {
4343 	game_display_ = gd;
4344 }
4345 
4346 /// These are the child tags of [scenario] (and the like) that are handled
4347 /// elsewhere (in the C++ code).
4348 /// Any child tags not in this list will be passed to Lua's on_load event.
4349 static char const *handled_file_tags[] {
4350 	"color_palette", "color_range", "display", "end_level_data", "era",
4351 	"event", "generator", "label", "lua", "map", "menu_item",
4352 	"modification", "music", "options", "side", "sound_source",
4353 	"story", "terrain_graphics", "time", "time_area", "tunnel",
4354 	"undo_stack", "variables"
4355 };
4356 
is_handled_file_tag(const std::string & s)4357 static bool is_handled_file_tag(const std::string &s)
4358 {
4359 	for (char const *t : handled_file_tags) {
4360 		if (s == t) return true;
4361 	}
4362 	return false;
4363 }
4364 
4365 /**
4366  * Executes the game_events.on_load function and passes to it all the
4367  * scenario tags not yet handled.
4368  */
load_game(const config & level)4369 void game_lua_kernel::load_game(const config& level)
4370 {
4371 	lua_State *L = mState;
4372 
4373 	if (!luaW_getglobal(L, "wesnoth", "game_events", "on_load"))
4374 		return;
4375 
4376 	lua_newtable(L);
4377 	int k = 1;
4378 	for (const config::any_child &v : level.all_children_range())
4379 	{
4380 		if (is_handled_file_tag(v.key)) continue;
4381 		lua_createtable(L, 2, 0);
4382 		lua_pushstring(L, v.key.c_str());
4383 		lua_rawseti(L, -2, 1);
4384 		luaW_pushconfig(L, v.cfg);
4385 		lua_rawseti(L, -2, 2);
4386 		lua_rawseti(L, -2, k++);
4387 	}
4388 
4389 	luaW_pcall(L, 1, 0, true);
4390 }
4391 
4392 /**
4393  * Executes the game_events.on_save function and adds to @a cfg the
4394  * returned tags. Also flushes the [lua] tags.
4395  */
save_game(config & cfg)4396 void game_lua_kernel::save_game(config &cfg)
4397 {
4398 	lua_State *L = mState;
4399 
4400 	if (!luaW_getglobal(L, "wesnoth", "game_events", "on_save"))
4401 		return;
4402 
4403 	if (!luaW_pcall(L, 0, 1, false))
4404 		return;
4405 
4406 	config v;
4407 	luaW_toconfig(L, -1, v);
4408 	lua_pop(L, 1);
4409 
4410 	for (;;)
4411 	{
4412 		config::all_children_iterator i = v.ordered_begin();
4413 		if (i == v.ordered_end()) break;
4414 		if (is_handled_file_tag(i->key))
4415 		{
4416 			/*
4417 			 * It seems the only tags appearing in the config v variable here
4418 			 * are the core-lua-handled (currently [item] and [objectives])
4419 			 * and the extra UMC ones.
4420 			 */
4421 			const std::string m = "Tag is already used: [" + i->key + "]";
4422 			log_error(m.c_str());
4423 			v.erase(i);
4424 			continue;
4425 		}
4426 		cfg.splice_children(v, i->key);
4427 	}
4428 }
4429 
4430 /**
4431  * Executes the game_events.on_event function.
4432  * Returns false if there was no lua handler for this event
4433  */
run_event(const game_events::queued_event & ev)4434 bool game_lua_kernel::run_event(const game_events::queued_event& ev)
4435 {
4436 	lua_State *L = mState;
4437 
4438 	if (!luaW_getglobal(L, "wesnoth", "game_events", "on_event"))
4439 		return false;
4440 
4441 	queued_event_context dummy(&ev, queued_events_);
4442 	lua_pushstring(L, ev.name.c_str());
4443 	luaW_pcall(L, 1, 0, false);
4444 	return true;
4445 }
4446 
custom_command(const std::string & name,const config & cfg)4447 void game_lua_kernel::custom_command(const std::string& name, const config& cfg)
4448 {
4449 	lua_State *L = mState;
4450 
4451 	if (!luaW_getglobal(L, "wesnoth", "custom_synced_commands", name)) {
4452 		return;
4453 	}
4454 	luaW_pushconfig(L, cfg);
4455 	luaW_pcall(L, 1, 0, false);
4456 }
4457 
4458 /**
4459  * Applies its upvalue as an effect
4460  * Arg 1: The unit to apply to
4461  * Arg 3: The [effect] tag contents
4462  * Arg 3: If false, only build description
4463  * Return: The description of the effect
4464  */
cfun_builtin_effect(lua_State * L)4465 int game_lua_kernel::cfun_builtin_effect(lua_State *L)
4466 {
4467 	std::string which_effect = lua_tostring(L, lua_upvalueindex(1));
4468 	bool need_apply = luaW_toboolean(L, lua_upvalueindex(2));
4469 	// Argument 1 is the implicit "self" argument, which isn't needed here
4470 	lua_unit u(luaW_checkunit(L, 2));
4471 	config cfg = luaW_checkconfig(L, 3);
4472 
4473 	// The times= key is supposed to be ignored by the effect function.
4474 	// However, just in case someone doesn't realize this, we will set it to 1 here.
4475 	cfg["times"] = 1;
4476 
4477 	if(need_apply) {
4478 		u->apply_builtin_effect(which_effect, cfg);
4479 		return 0;
4480 	} else {
4481 		std::string description = u->describe_builtin_effect(which_effect, cfg);
4482 		lua_pushstring(L, description.c_str());
4483 		return 1;
4484 	}
4485 }
4486 
4487 /**
4488 * Registers a function for use as an effect handler.
4489 */
push_builtin_effect()4490 void game_lua_kernel::push_builtin_effect()
4491 {
4492 	lua_State *L = mState;
4493 
4494 	// The effect name is at the top of the stack
4495 	int str_i = lua_gettop(L);
4496 	lua_newtable(L); // The functor table
4497 	lua_newtable(L); // The functor metatable
4498 	lua_pushstring(L, "__call");
4499 	lua_pushvalue(L, str_i);
4500 	lua_pushboolean(L, true);
4501 	lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
4502 	lua_rawset(L, -3); // Set the call metafunction
4503 	lua_pushstring(L, "__descr");
4504 	lua_pushvalue(L, str_i);
4505 	lua_pushboolean(L, false);
4506 	lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_builtin_effect>, 2);
4507 	lua_rawset(L, -3); // Set the descr "metafunction"
4508 	lua_setmetatable(L, -2); // Apply the metatable to the functor table
4509 }
4510 
4511 
4512 /**
4513  * Executes its upvalue as a wml action.
4514  */
cfun_wml_action(lua_State * L)4515 int game_lua_kernel::cfun_wml_action(lua_State *L)
4516 {
4517 	game_events::wml_action::handler h = reinterpret_cast<game_events::wml_action::handler>
4518 		(lua_touserdata(L, lua_upvalueindex(1)));
4519 
4520 	vconfig vcfg = luaW_checkvconfig(L, 1);
4521 	h(get_event_info(), vcfg);
4522 	return 0;
4523 }
4524 
4525 /**
4526  * Registers a function for use as an action handler.
4527  */
set_wml_action(const std::string & cmd,game_events::wml_action::handler h)4528 void game_lua_kernel::set_wml_action(const std::string& cmd, game_events::wml_action::handler h)
4529 {
4530 	lua_State *L = mState;
4531 
4532 	lua_getglobal(L, "wesnoth");
4533 	lua_pushstring(L, "wml_actions");
4534 	lua_rawget(L, -2);
4535 	lua_pushstring(L, cmd.c_str());
4536 	lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
4537 	lua_pushcclosure(L, &dispatch<&game_lua_kernel::cfun_wml_action>, 1);
4538 	lua_rawset(L, -3);
4539 	lua_pop(L, 2);
4540 }
4541 
4542 using wml_conditional_handler = bool(*)(const vconfig&);
4543 
4544 /**
4545  * Executes its upvalue as a wml condition and returns the result.
4546  */
cfun_wml_condition(lua_State * L)4547 static int cfun_wml_condition(lua_State *L)
4548 {
4549 	wml_conditional_handler h = reinterpret_cast<wml_conditional_handler>
4550 		(lua_touserdata(L, lua_upvalueindex(1)));
4551 
4552 	vconfig vcfg = luaW_checkvconfig(L, 1);
4553 	lua_pushboolean(L, h(vcfg));
4554 	return 1;
4555 }
4556 
4557 /**
4558  * Registers a function for use as a conditional handler.
4559  */
set_wml_condition(const std::string & cmd,wml_conditional_handler h)4560 void game_lua_kernel::set_wml_condition(const std::string& cmd, wml_conditional_handler h)
4561 {
4562 	lua_State *L = mState;
4563 
4564 	lua_getglobal(L, "wesnoth");
4565 	lua_pushstring(L, "wml_conditionals");
4566 	lua_rawget(L, -2);
4567 	lua_pushstring(L, cmd.c_str());
4568 	lua_pushlightuserdata(L, reinterpret_cast<void *>(h));
4569 	lua_pushcclosure(L, &cfun_wml_condition, 1);
4570 	lua_rawset(L, -3);
4571 	lua_pop(L, 2);
4572 }
4573 
4574 /**
4575  * Runs a command from an event handler.
4576  * @return true if there is a handler for the command.
4577  * @note @a cfg should be either volatile or long-lived since the Lua
4578  *       code may grab it for an arbitrary long time.
4579  */
run_wml_action(const std::string & cmd,const vconfig & cfg,const game_events::queued_event & ev)4580 bool game_lua_kernel::run_wml_action(const std::string& cmd, const vconfig& cfg,
4581 	const game_events::queued_event& ev)
4582 {
4583 	lua_State *L = mState;
4584 
4585 
4586 	if (!luaW_getglobal(L, "wesnoth", "wml_actions", cmd))
4587 		return false;
4588 
4589 	queued_event_context dummy(&ev, queued_events_);
4590 	luaW_pushvconfig(L, cfg);
4591 	luaW_pcall(L, 1, 0, true);
4592 	return true;
4593 }
4594 
4595 
4596 /**
4597  * Evaluates a WML conidition.
4598  *
4599  * @returns Whether the condition passed.
4600  * @note    @a cfg should be either volatile or long-lived since the Lua
4601  *          code may grab it for an arbitrarily long time.
4602  */
run_wml_conditional(const std::string & cmd,const vconfig & cfg)4603 bool game_lua_kernel::run_wml_conditional(const std::string& cmd, const vconfig& cfg)
4604 {
4605 	lua_State* L = mState;
4606 
4607 	// If an invalid coniditional tag is used, consider it a pass.
4608 	if(!luaW_getglobal(L, "wesnoth", "wml_conditionals", cmd)) {
4609 		lg::wml_error() << "unknown conditional wml: [" << cmd << "]\n";
4610 		return true;
4611 	}
4612 
4613 	luaW_pushvconfig(L, cfg);
4614 
4615 	// Any runtime error is considered a fail.
4616 	if(!luaW_pcall(L, 1, 1, true)) {
4617 		return false;
4618 	}
4619 
4620 	bool b = luaW_toboolean(L, -1);
4621 
4622 	lua_pop(L, 1);
4623 	return b;
4624 }
4625 
4626 
4627 /**
4628 * Runs a script from a location filter.
4629 * The script is an already compiled function given by its name.
4630 */
run_filter(char const * name,const map_location & l)4631 bool game_lua_kernel::run_filter(char const *name, const map_location& l)
4632 {
4633 	lua_pushinteger(mState, l.wml_x());
4634 	lua_pushinteger(mState, l.wml_y());
4635 	return run_filter(name, 2);
4636 }
4637 /**
4638 * Runs a script from a unit filter.
4639 * The script is an already compiled function given by its name.
4640 */
run_filter(char const * name,const unit & u)4641 bool game_lua_kernel::run_filter(char const *name, const unit& u)
4642 {
4643 	lua_State *L = mState;
4644 	unit_map::const_unit_iterator ui = units().find(u.get_location());
4645 	if (!ui.valid()) return false;
4646 	// Pass the unit as argument.
4647 	luaW_pushunit(L, ui->underlying_id());
4648 
4649 	return run_filter(name, 1);
4650 }
4651 /**
4652 * Runs a script from a filter.
4653 * The script is an already compiled function given by its name.
4654 */
run_filter(char const * name,int nArgs)4655 bool game_lua_kernel::run_filter(char const *name, int nArgs)
4656 {
4657 	map_locker(this);
4658 	lua_State *L = mState;
4659 	// Get the user filter by name.
4660 	const std::vector<std::string>& path = utils::split(name, '.', utils::STRIP_SPACES);
4661 	if (!luaW_getglobal(L, path))
4662 	{
4663 		std::string message = std::string() + "function " + name + " not found";
4664 		log_error(message.c_str(), "Lua SUF Error");
4665 		//we pushed nothing and can safeley return.
4666 		return false;
4667 	}
4668 	lua_insert(L, -nArgs - 1);
4669 
4670 	if (!luaW_pcall(L, nArgs, 1)) return false;
4671 
4672 	bool b = luaW_toboolean(L, -1);
4673 	lua_pop(L, 1);
4674 	return b;
4675 }
4676 
apply_effect(const std::string & name,unit & u,const config & cfg,bool need_apply)4677 std::string game_lua_kernel::apply_effect(const std::string& name, unit& u, const config& cfg, bool need_apply)
4678 {
4679 	lua_State *L = mState;
4680 	int top = lua_gettop(L);
4681 	std::string descr;
4682 	// Stack: nothing
4683 	lua_unit* lu = luaW_pushlocalunit(L, u);
4684 	// Stack: unit
4685 	// (Note: The unit needs to be on the stack twice to prevent untimely GC.)
4686 	luaW_pushconfig(L, cfg);
4687 	// Stack: unit, cfg
4688 	if(luaW_getglobal(L, "wesnoth", "effects", name)) {
4689 		map_locker(this);
4690 		// Stack: unit, cfg, effect
4691 		if(lua_istable(L, -1)) {
4692 			// Effect is implemented by a table with __call and __descr
4693 			if(need_apply) {
4694 				lua_pushvalue(L, -1);
4695 				// Stack: unit, cfg, effect, effect
4696 				lua_pushvalue(L, top + 1);
4697 				// Stack: unit, cfg, effect, effect, unit
4698 				lua_pushvalue(L, top + 2);
4699 				// Stack: unit, cfg, effect, effect, unit, cfg
4700 				luaW_pcall(L, 2, 0);
4701 				// Stack: unit, cfg, effect
4702 			}
4703 			if(luaL_getmetafield(L, -1, "__descr")) {
4704 				// Stack: unit, cfg, effect, __descr
4705 				if(lua_isstring(L, -1)) {
4706 					// __descr was a static string
4707 					descr = lua_tostring(L, -1);
4708 				} else {
4709 					lua_pushvalue(L, -2);
4710 					// Stack: unit, cfg, effect, __descr, effect
4711 					lua_pushvalue(L, top + 1);
4712 					// Stack: unit, cfg, effect, __descr, effect, unit
4713 					lua_pushvalue(L, top + 2);
4714 					// Stack: unit, cfg, effect, __descr, effect, unit, cfg
4715 					luaW_pcall(L, 3, 1);
4716 					if(lua_isstring(L, -1) && !lua_isnumber(L, -1)) {
4717 						descr = lua_tostring(L, -1);
4718 					} else {
4719 						ERR_LUA << "Effect __descr metafunction should have returned a string, but instead returned ";
4720 						if(lua_isnone(L, -1)) {
4721 							ERR_LUA << "nothing";
4722 						} else {
4723 							ERR_LUA << lua_typename(L, lua_type(L, -1));
4724 						}
4725 					}
4726 				}
4727 			}
4728 		} else if(need_apply) {
4729 			// Effect is assumed to be a simple function; no description is provided
4730 			lua_pushvalue(L, top + 1);
4731 			// Stack: unit, cfg, effect, unit
4732 			lua_pushvalue(L, top + 2);
4733 			// Stack: unit, cfg, effect, unit, cfg
4734 			luaW_pcall(L, 2, 0);
4735 			// Stack: unit, cfg
4736 		}
4737 	}
4738 	lua_settop(L, top);
4739 	lu->clear_ref();
4740 	return descr;
4741 }
4742 
create_lua_ai_context(char const * code,ai::engine_lua * engine)4743 ai::lua_ai_context* game_lua_kernel::create_lua_ai_context(char const *code, ai::engine_lua *engine)
4744 {
4745 	return ai::lua_ai_context::create(mState,code,engine);
4746 }
4747 
create_lua_ai_action_handler(char const * code,ai::lua_ai_context & context)4748 ai::lua_ai_action_handler* game_lua_kernel::create_lua_ai_action_handler(char const *code, ai::lua_ai_context &context)
4749 {
4750 	return ai::lua_ai_action_handler::create(mState,code,context);
4751 }
4752 
mouse_over_hex_callback(const map_location & loc)4753 void game_lua_kernel::mouse_over_hex_callback(const map_location& loc)
4754 {
4755 	lua_State *L = mState;
4756 
4757 	if (!luaW_getglobal(L, "wesnoth", "game_events", "on_mouse_move")) {
4758 		return;
4759 	}
4760 	lua_push(L, loc.wml_x());
4761 	lua_push(L, loc.wml_y());
4762 	luaW_pcall(L, 2, 0, false);
4763 	return;
4764 }
4765 
select_hex_callback(const map_location & loc)4766 void game_lua_kernel::select_hex_callback(const map_location& loc)
4767 {
4768 	lua_State *L = mState;
4769 
4770 	if (!luaW_getglobal(L, "wesnoth", "game_events", "on_mouse_action")) {
4771 		return;
4772 	}
4773 	lua_push(L, loc.wml_x());
4774 	lua_push(L, loc.wml_y());
4775 	luaW_pcall(L, 2, 0, false);
4776 	return;
4777 }
4778