diff --git a/data/lua/wml-tags.lua b/data/lua/wml-tags.lua index 2d22e7cb20c..a9fbe970bbc 100644 --- a/data/lua/wml-tags.lua +++ b/data/lua/wml-tags.lua @@ -1752,3 +1752,14 @@ wml_actions.unstore_unit = function(cfg) unit:to_recall() end end + +wml_actions.teleport = function(cfg) + local context = wesnoth.current.event_context + local filter = helper.get_child(cfg, "filter") or { x = context.x1, y = context.y1 } + local unit = wesnoth.get_units(filter)[0] + if not unit then + -- No error if no unit matches. + return + end + wesnoth.teleport(unit, cfg.check_passability == false, cfg.clear_shroud ~= false, cfg.animate) +end diff --git a/src/game_events/action_wml.cpp b/src/game_events/action_wml.cpp index 8d761da7da3..0d534cc5597 100644 --- a/src/game_events/action_wml.cpp +++ b/src/game_events/action_wml.cpp @@ -1140,69 +1140,6 @@ WML_HANDLER_FUNCTION(store_time_of_day, /*event_info*/, cfg) } } -WML_HANDLER_FUNCTION(teleport, event_info, cfg) -{ - unit_map::iterator u = resources::units->find(event_info.loc1); - - // Search for a valid unit filter, and if we have one, look for the matching unit - const vconfig & filter = cfg.child("filter"); - if(!filter.null()) { - const unit_filter ufilt(filter, resources::filter_con); - for (u = resources::units->begin(); u != resources::units->end(); ++u){ - if ( ufilt(*u) ) - break; - } - } - - if (u == resources::units->end()) return; - - // We have found a unit that matches the filter - const map_location dst = cfg_to_loc(cfg); - if (dst == u->get_location() || !resources::gameboard->map().on_board(dst)) return; - - const unit* pass_check = NULL; - if (cfg["check_passability"].to_bool(true)) - pass_check = &*u; - const map_location vacant_dst = find_vacant_tile(dst, pathfind::VACANT_ANY, pass_check); - if (!resources::gameboard->map().on_board(vacant_dst)) return; - - // Clear the destination hex before the move (so the animation can be seen). - bool clear_shroud = cfg["clear_shroud"].to_bool(true); - actions::shroud_clearer clearer; - if ( clear_shroud ) { - clearer.clear_dest(vacant_dst, *u); - } - - map_location src_loc = u->get_location(); - - std::vector teleport_path; - teleport_path.push_back(src_loc); - teleport_path.push_back(vacant_dst); - bool animate = cfg["animate"].to_bool(); - unit_display::move_unit(teleport_path, u.get_shared_ptr(), animate); - - resources::units->move(src_loc, vacant_dst); - unit::clear_status_caches(); - - u = resources::units->find(vacant_dst); - u->anim_comp().set_standing(); - - if ( clear_shroud ) { - // Now that the unit is visibly in position, clear the shroud. - clearer.clear_unit(vacant_dst, *u); - } - - if (resources::gameboard->map().is_village(vacant_dst)) { - actions::get_village(vacant_dst, u->side()); - } - - resources::screen->invalidate_unit_after_move(src_loc, vacant_dst); - resources::screen->draw(); - - // Sighted events. - clearer.fire_events(); -} - /// Creating a mask of the terrain WML_HANDLER_FUNCTION(terrain_mask, /*event_info*/, cfg) { diff --git a/src/scripting/game_lua_kernel.cpp b/src/scripting/game_lua_kernel.cpp index e2d64893022..735902205d8 100644 --- a/src/scripting/game_lua_kernel.cpp +++ b/src/scripting/game_lua_kernel.cpp @@ -28,6 +28,7 @@ #include "global.hpp" #include "actions/attack.hpp" // for battle_context_unit_stats, etc +#include "actions/move.hpp" // for clear_shroud #include "actions/vision.hpp" // for clear_shroud #include "ai/composite/ai.hpp" // for ai_composite #include "ai/composite/component.hpp" // for component, etc @@ -4130,6 +4131,65 @@ int game_lua_kernel::intf_get_all_vars(lua_State *L) { return 1; } +/** + * Teeleports a unit to a location. + * Arg 1: unit + * Arg 2,3: taget location + * Arg 4: bool (ignore_passability) + * Arg 5: bool (clear_shroud) + * Arg 6: bool (animate) + */ +int game_lua_kernel::intf_teleport(lua_State *L) +{ + unit_ptr u = luaW_checkunit_ptr(L, 1, true); + map_location dst(luaL_checkinteger(L, 2) - 1, luaL_checkinteger(L, 3) - 1); + bool check_passability = !lua_toboolean(L, 4); + bool clear_shroud = luaW_toboolean(L, 5); + bool animate = luaW_toboolean(L, 6); + + if (dst == u->get_location() || !resources::gameboard->map().on_board(dst)) { + return 0; + } + const map_location vacant_dst = find_vacant_tile(dst, pathfind::VACANT_ANY, check_passability ? u.get() : NULL); + if (!resources::gameboard->map().on_board(vacant_dst)) { + return 0; + } + // Clear the destination hex before the move (so the animation can be seen). + actions::shroud_clearer clearer; + if ( clear_shroud ) { + clearer.clear_dest(vacant_dst, *u); + } + + map_location src_loc = u->get_location(); + + std::vector teleport_path; + teleport_path.push_back(src_loc); + teleport_path.push_back(vacant_dst); + unit_display::move_unit(teleport_path, u, animate); + + resources::units->move(src_loc, vacant_dst); + unit::clear_status_caches(); + + u = &*resources::units->find(vacant_dst); + u->anim_comp().set_standing(); + + if ( clear_shroud ) { + // Now that the unit is visibly in position, clear the shroud. + clearer.clear_unit(vacant_dst, *u); + } + + if (resources::gameboard->map().is_village(vacant_dst)) { + actions::get_village(vacant_dst, u->side()); + } + + resources::screen->invalidate_unit_after_move(src_loc, vacant_dst); + resources::screen->draw(); + + // Sighted events. + clearer.fire_events(); + return 0; +} + // END CALLBACK IMPLEMENTATION game_board & game_lua_kernel::board() { diff --git a/src/scripting/game_lua_kernel.hpp b/src/scripting/game_lua_kernel.hpp index cac40c6d3d3..17c1d3619ad 100644 --- a/src/scripting/game_lua_kernel.hpp +++ b/src/scripting/game_lua_kernel.hpp @@ -159,6 +159,7 @@ class game_lua_kernel : public lua_kernel_base int cfun_wml_action(lua_State *L); int intf_fire_event(lua_State *L); int intf_fire_wml_menu_item(lua_State *L); + int intf_teleport(lua_State *L); //private helpers std::string synced_state(); diff --git a/src/scripting/lua_api.cpp b/src/scripting/lua_api.cpp index 9c74fbf033b..6f7dbd6dae7 100644 --- a/src/scripting/lua_api.cpp +++ b/src/scripting/lua_api.cpp @@ -189,6 +189,20 @@ unit* luaW_tounit(lua_State *L, int index, bool only_on_map) return lu->get(); } +unit_ptr luaW_tounit_ptr(lua_State *L, int index, bool only_on_map) +{ + if (!luaW_hasmetatable(L, index, getunitKey)) return unit_ptr(); + lua_unit *lu = static_cast(lua_touserdata(L, index)); + if (only_on_map && !lu->on_map()) return unit_ptr(); + return lu->get_shared(); +} + +unit_ptr luaW_checkunit_ptr(lua_State *L, int index, bool only_on_map) +{ + unit_ptr u = luaW_tounit(L, index, only_on_map); + if (!u) luaL_typerror(L, index, "unit"); + return u; +} unit& luaW_checkunit(lua_State *L, int index, bool only_on_map) { unit* u = luaW_tounit(L, index, only_on_map); diff --git a/src/scripting/lua_api.hpp b/src/scripting/lua_api.hpp index 4b212a53929..f0487831ccd 100644 --- a/src/scripting/lua_api.hpp +++ b/src/scripting/lua_api.hpp @@ -45,6 +45,13 @@ bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error = false unit& luaW_checkunit(lua_State *L, int index, bool only_on_map = false); class lua_unit; lua_unit* luaW_pushlocalunit(lua_State *L, unit& u); +/** + * Similar to luaW_checkunit/luaW_tounit but returns a unit_ptr, use this instead of + * luaW_checkunit/luaW_tounit when uasing an api that needs unit_ptr. + */ +unit_ptr luaW_tounit_ptr(lua_State *L, int index, bool only_on_map); +unit_ptr luaW_checkunit_ptr(lua_State *L, int index, bool only_on_map); + struct map_location; /**