Lua API: Add __dir metamethod to units metatable
This implements a new system for registering attributes, adapted from the system for widget attributes.
This commit is contained in:
parent
ab022c09ae
commit
d4278fa1be
11 changed files with 782 additions and 307 deletions
|
@ -1010,6 +1010,8 @@
|
|||
<Unit filename="../../src/scripting/debug_lua.hpp" />
|
||||
<Unit filename="../../src/scripting/game_lua_kernel.cpp" />
|
||||
<Unit filename="../../src/scripting/game_lua_kernel.hpp" />
|
||||
<Unit filename="../../src/scripting/lua_attributes.cpp" />
|
||||
<Unit filename="../../src/scripting/lua_attributes.hpp" />
|
||||
<Unit filename="../../src/scripting/lua_audio.cpp" />
|
||||
<Unit filename="../../src/scripting/lua_audio.hpp" />
|
||||
<Unit filename="../../src/scripting/lua_color.cpp" />
|
||||
|
|
|
@ -1046,6 +1046,8 @@
|
|||
<Unit filename="../../src/scripting/debug_lua.hpp" />
|
||||
<Unit filename="../../src/scripting/game_lua_kernel.cpp" />
|
||||
<Unit filename="../../src/scripting/game_lua_kernel.hpp" />
|
||||
<Unit filename="../../src/scripting/lua_attributes.cpp" />
|
||||
<Unit filename="../../src/scripting/lua_attributes.hpp" />
|
||||
<Unit filename="../../src/scripting/lua_audio.cpp" />
|
||||
<Unit filename="../../src/scripting/lua_audio.hpp" />
|
||||
<Unit filename="../../src/scripting/lua_color.cpp" />
|
||||
|
|
|
@ -718,6 +718,8 @@
|
|||
91254C462C4FE519007695D3 /* optional_fwd.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 91254C452C4FE519007695D3 /* optional_fwd.hpp */; };
|
||||
913D26771D3C9697002FF3AB /* name_generator_factory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 913D26751D3C9697002FF3AB /* name_generator_factory.cpp */; };
|
||||
914F2F861D35253900A42440 /* location_palette.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 914F2F841D35253900A42440 /* location_palette.cpp */; };
|
||||
9154743D2C8FBAC800EB1C94 /* lua_attributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9154743C2C8FBAC700EB1C94 /* lua_attributes.cpp */; };
|
||||
915474422C8FBACE00EB1C94 /* lua_attributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9154743C2C8FBAC700EB1C94 /* lua_attributes.cpp */; };
|
||||
915C68EA1DF1DCB000594B07 /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 915C68E81DF1DCB000594B07 /* color.cpp */; };
|
||||
915C68EB1DF1DCB000594B07 /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 915C68E81DF1DCB000594B07 /* color.cpp */; };
|
||||
915C68EC1DF1DCB000594B07 /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 915C68E81DF1DCB000594B07 /* color.cpp */; };
|
||||
|
@ -1615,7 +1617,7 @@
|
|||
26A04033A9545CFE8A226FBD /* test_schema_self_validator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = test_schema_self_validator.cpp; sourceTree = "<group>"; };
|
||||
27764FB68F02032F1C0B6748 /* statistics_record.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = statistics_record.cpp; sourceTree = "<group>"; };
|
||||
2CFD4922B64EA6C9F71F71A2 /* preferences.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = preferences.hpp; path = preferences/preferences.hpp; sourceTree = "<group>"; };
|
||||
3D284B9A81882806D8B25006 /* spritesheet_generator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = spritesheet_generator.hpp; path = spritesheet_generator.hpp; sourceTree = "<group>"; };
|
||||
3D284B9A81882806D8B25006 /* spritesheet_generator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = spritesheet_generator.hpp; sourceTree = "<group>"; };
|
||||
46081FED2B2F0F6A006ACAD7 /* libpcre2-8.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libpcre2-8.0.dylib"; path = "lib/libpcre2-8.0.dylib"; sourceTree = "<group>"; };
|
||||
46081FF02B2F103E006ACAD7 /* libwebpdemux.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libwebpdemux.2.dylib; path = lib/libwebpdemux.2.dylib; sourceTree = "<group>"; };
|
||||
46081FF32B2F11F3006ACAD7 /* libsharpyuv.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsharpyuv.0.dylib; path = lib/libsharpyuv.0.dylib; sourceTree = "<group>"; };
|
||||
|
@ -2148,7 +2150,7 @@
|
|||
46F92F0C2174FEC000602C1C /* text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = text.cpp; path = font/text.cpp; sourceTree = "<group>"; };
|
||||
46F92F0D2174FEC000602C1C /* text.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = text.hpp; path = font/text.hpp; sourceTree = "<group>"; };
|
||||
46F92F0E2174FEC000602C1C /* constants.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = constants.cpp; path = font/constants.cpp; sourceTree = "<group>"; };
|
||||
492144E3AE22C0C7A2D5F27C /* spritesheet_generator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = spritesheet_generator.cpp; path = spritesheet_generator.cpp; sourceTree = "<group>"; };
|
||||
492144E3AE22C0C7A2D5F27C /* spritesheet_generator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = spritesheet_generator.cpp; sourceTree = "<group>"; };
|
||||
4944F41A1354FBFF0027E614 /* teleport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = teleport.cpp; sourceTree = "<group>"; };
|
||||
49478712172FF6F8002B7ABA /* tristate_button.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tristate_button.cpp; sourceTree = "<group>"; };
|
||||
49478713172FF6F8002B7ABA /* tristate_button.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tristate_button.hpp; sourceTree = "<group>"; };
|
||||
|
@ -2264,6 +2266,8 @@
|
|||
913D26761D3C9697002FF3AB /* name_generator_factory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = name_generator_factory.hpp; sourceTree = "<group>"; };
|
||||
914F2F841D35253900A42440 /* location_palette.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = location_palette.cpp; sourceTree = "<group>"; };
|
||||
914F2F851D35253900A42440 /* location_palette.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = location_palette.hpp; sourceTree = "<group>"; };
|
||||
9154743B2C8FBA5D00EB1C94 /* lua_attributes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lua_attributes.hpp; sourceTree = "<group>"; };
|
||||
9154743C2C8FBAC700EB1C94 /* lua_attributes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lua_attributes.cpp; sourceTree = "<group>"; };
|
||||
915C68E81DF1DCB000594B07 /* color.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = color.cpp; sourceTree = "<group>"; };
|
||||
915C68E91DF1DCB000594B07 /* color.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = color.hpp; sourceTree = "<group>"; };
|
||||
915C68F31DF1F78600594B07 /* libintl.8.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libintl.8.dylib; path = lib/libintl.8.dylib; sourceTree = "<group>"; };
|
||||
|
@ -5005,6 +5009,8 @@
|
|||
91B621E21B76BAF300B00E0F /* context.hpp */,
|
||||
EC218EA21A106673007C910C /* game_lua_kernel.cpp */,
|
||||
91B621E41B76BB0100B00E0F /* game_lua_kernel.hpp */,
|
||||
9154743C2C8FBAC700EB1C94 /* lua_attributes.cpp */,
|
||||
9154743B2C8FBA5D00EB1C94 /* lua_attributes.hpp */,
|
||||
EC3863621EB6286E0048B0C8 /* lua_audio.cpp */,
|
||||
EC3863631EB6286E0048B0C8 /* lua_audio.hpp */,
|
||||
461DC52B241F836200B9DD10 /* lua_color.cpp */,
|
||||
|
@ -5557,6 +5563,7 @@
|
|||
EC669C261DFC95AF00172EED /* surface.cpp in Sources */,
|
||||
46F92DAB2174F6A300602C1C /* unit_recall.cpp in Sources */,
|
||||
B5599B7B0EC62181008DD061 /* config_cache.cpp in Sources */,
|
||||
9154743D2C8FBAC800EB1C94 /* lua_attributes.cpp in Sources */,
|
||||
46F92EF22174FE5600602C1C /* show_dialog.cpp in Sources */,
|
||||
B5599B7D0EC62181008DD061 /* config.cpp in Sources */,
|
||||
EC0680291EA920A300EEE03B /* random_deterministic.cpp in Sources */,
|
||||
|
@ -6312,6 +6319,7 @@
|
|||
91E356B21CACC82D00774252 /* aspect.cpp in Sources */,
|
||||
91E356B31CACC82D00774252 /* component.cpp in Sources */,
|
||||
46F92D862174F6A300602C1C /* vertical_list.cpp in Sources */,
|
||||
915474422C8FBACE00EB1C94 /* lua_attributes.cpp in Sources */,
|
||||
91E356B41CACC82D00774252 /* contexts.cpp in Sources */,
|
||||
46F92DF02174F6A400602C1C /* install_dependencies.cpp in Sources */,
|
||||
46D60150255AFA6000E072F0 /* options.cpp in Sources */,
|
||||
|
|
|
@ -328,6 +328,7 @@ savegame.cpp
|
|||
scripting/application_lua_kernel.cpp
|
||||
scripting/debug_lua.cpp
|
||||
scripting/game_lua_kernel.cpp
|
||||
scripting/lua_attributes.cpp
|
||||
scripting/lua_audio.cpp
|
||||
scripting/lua_color.cpp
|
||||
scripting/lua_common.cpp
|
||||
|
|
|
@ -735,7 +735,7 @@ tree_view_node& tree_view_node::get_child_at(int index)
|
|||
return *children_[index];
|
||||
}
|
||||
|
||||
std::vector<int> tree_view_node::describe_path()
|
||||
std::vector<int> tree_view_node::describe_path() const
|
||||
{
|
||||
if(is_root_node()) {
|
||||
return std::vector<int>();
|
||||
|
|
|
@ -237,7 +237,7 @@ public:
|
|||
/**
|
||||
* Calculates the node indices needed to get from the root node to this node.
|
||||
*/
|
||||
std::vector<int> describe_path();
|
||||
std::vector<int> describe_path() const;
|
||||
|
||||
tree_view_node* get_last_visible_parent_node();
|
||||
tree_view_node* get_node_above();
|
||||
|
|
90
src/scripting/lua_attributes.cpp
Normal file
90
src/scripting/lua_attributes.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright (C) 2024 - 2024
|
||||
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "scripting/lua_attributes.hpp"
|
||||
#include "scripting/lua_common.hpp"
|
||||
#include "scripting/lua_kernel_base.hpp" // for luaW_get_attributes
|
||||
#include "scripting/push_check.hpp"
|
||||
#include <sstream>
|
||||
|
||||
luaW_Registry::luaW_Registry(const std::initializer_list<std::string>& mt) : public_metatable(mt) {
|
||||
private_metatable = public_metatable.back();
|
||||
public_metatable.pop_back();
|
||||
lookup.emplace(private_metatable, std::ref(*this));
|
||||
}
|
||||
|
||||
luaW_Registry::~luaW_Registry() {
|
||||
lookup.erase(private_metatable);
|
||||
}
|
||||
|
||||
int luaW_Registry::get(lua_State* L) {
|
||||
std::string_view str = lua_check<std::string_view>(L, 2);
|
||||
|
||||
auto it = getters.find(std::string(str));
|
||||
if(it != getters.end()) {
|
||||
if(it->second(L, false)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(!public_metatable.empty()) {
|
||||
auto method = public_metatable;
|
||||
method.push_back(std::string(str));
|
||||
if(luaW_getglobal(L, method)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream err;
|
||||
err << "invalid property of " << private_metatable << ": " << str;
|
||||
return luaL_argerror(L, 2, err.str().c_str());
|
||||
}
|
||||
|
||||
int luaW_Registry::set(lua_State* L) {
|
||||
std::string_view str = lua_check<std::string_view>(L, 2);
|
||||
|
||||
auto it = setters.find(std::string(str));
|
||||
if(it != setters.end()) {
|
||||
if(it->second(L, 3, false)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream err;
|
||||
err << "invalid modifiable property of " << private_metatable << ": " << str;
|
||||
return luaL_argerror(L, 2, err.str().c_str());
|
||||
}
|
||||
|
||||
int luaW_Registry::dir(lua_State *L) {
|
||||
std::vector<std::string> keys;
|
||||
// Add any readable keys
|
||||
for(const auto& [key, func] : getters) {
|
||||
if(func(L, true)){
|
||||
keys.push_back(key);
|
||||
}
|
||||
}
|
||||
// Add any writable keys
|
||||
for(const auto& [key, func] : setters) {
|
||||
if(func(L, 0, true)){
|
||||
keys.push_back(key);
|
||||
}
|
||||
}
|
||||
// Add the metatable methods
|
||||
if(!public_metatable.empty()) {
|
||||
luaW_getglobal(L, public_metatable);
|
||||
auto methods = luaW_get_attributes(L, -1);
|
||||
keys.insert(keys.end(), methods.begin(), methods.end());
|
||||
}
|
||||
lua_push(L, keys);
|
||||
return 1;
|
||||
}
|
143
src/scripting/lua_attributes.hpp
Normal file
143
src/scripting/lua_attributes.hpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
Copyright (C) 2009 - 2024
|
||||
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/// Attribute registration system, mainly for objects with a lot of attributes, like units
|
||||
/// Not used for GUI2 widgets, as they're even more complicated with a deep hierarchy.
|
||||
|
||||
#pragma once
|
||||
|
||||
struct lua_State;
|
||||
class t_string;
|
||||
class vconfig;
|
||||
|
||||
#include "config.hpp"
|
||||
#include "map/location.hpp"
|
||||
#include "variable_info.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
template<typename T>
|
||||
std::decay_t<T> lua_check(lua_State *L, int n);
|
||||
|
||||
/// Holds a lookup table for members of one type of object.
|
||||
struct luaW_Registry {
|
||||
inline static std::map<std::string_view /* metatable */, std::reference_wrapper<luaW_Registry>> lookup;
|
||||
using getters_list = std::map<std::string /* attribute */, std::function<bool(lua_State* L,bool nop)>>;
|
||||
/// A map of callbacks that read data from the object.
|
||||
getters_list getters;
|
||||
using setters_list = std::map<std::string, std::function<bool(lua_State* L,int idx,bool nop)>>;
|
||||
/// A map of callbacks that write data to the object.
|
||||
setters_list setters;
|
||||
/// The internal metatable string for the object (from __metatable)
|
||||
std::string private_metatable;
|
||||
/// Optional external metatable for the object (eg "wesnoth", "units")
|
||||
/// All entries of this table will be treated as members of the object.
|
||||
std::vector<std::string> public_metatable;
|
||||
luaW_Registry() = delete;
|
||||
luaW_Registry(const std::initializer_list<std::string>& mt);
|
||||
~luaW_Registry();
|
||||
/// Implement __index metamethod
|
||||
int get(lua_State* L);
|
||||
/// Implement __newindex metamethod
|
||||
int set(lua_State* L);
|
||||
/// Implement __dir metamethod
|
||||
int dir(lua_State* L);
|
||||
};
|
||||
|
||||
template<typename object_type, typename value_type>
|
||||
struct lua_getter
|
||||
{
|
||||
virtual value_type get(lua_State* L, const object_type& obj) const = 0;
|
||||
virtual ~lua_getter() = default;
|
||||
};
|
||||
|
||||
template<typename object_type, typename value_type>
|
||||
struct lua_setter
|
||||
{
|
||||
virtual void set(lua_State* L, object_type& obj, const value_type& value) const = 0;
|
||||
virtual ~lua_setter() = default;
|
||||
};
|
||||
|
||||
template<typename T> struct lua_object_traits;
|
||||
|
||||
template<typename object_type, typename value_type, typename action_type, bool setter>
|
||||
void register_lua_attribute(const char* name)
|
||||
{
|
||||
using obj_traits = lua_object_traits<object_type>;
|
||||
using map_type = std::conditional_t<setter, luaW_Registry::setters_list, luaW_Registry::getters_list>;
|
||||
using callback_type = typename map_type::mapped_type;
|
||||
map_type* map;
|
||||
callback_type fcn;
|
||||
if constexpr(setter) {
|
||||
map = &luaW_Registry::lookup.at(obj_traits::metatable).get().setters;
|
||||
fcn = [action = action_type()](lua_State* L, int idx, bool nop) {
|
||||
if(nop) return true;
|
||||
decltype(auto) obj = obj_traits::get(L, 1);
|
||||
action.set(L, obj, lua_check<value_type>(L, idx));
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
map = &luaW_Registry::lookup.at(obj_traits::metatable).get().getters;
|
||||
fcn = [action = action_type()](lua_State* L, bool nop) {
|
||||
if(nop) return true;
|
||||
lua_push(L, action.get(L, obj_traits::get(L, 1)));
|
||||
return true;
|
||||
};
|
||||
}
|
||||
(*map)[std::string(name)] = fcn;
|
||||
}
|
||||
|
||||
#define LATTR_MAKE_UNIQUE_ID(base, id, obj_name) BOOST_PP_CAT(BOOST_PP_CAT(base, id), BOOST_PP_CAT(_for_, obj_name))
|
||||
|
||||
#define LATTR_GETTER5(name, value_type, obj_type, obj_name, id) \
|
||||
struct LATTR_MAKE_UNIQUE_ID(getter_, id, obj_name) : public lua_getter<obj_type, value_type> { \
|
||||
using object_type = obj_type; \
|
||||
virtual value_type get(lua_State* L, const object_type& obj_name) const override; \
|
||||
}; \
|
||||
struct LATTR_MAKE_UNIQUE_ID(getter_adder_, id, obj_name) { \
|
||||
LATTR_MAKE_UNIQUE_ID(getter_adder_, id, obj_name) () \
|
||||
{ \
|
||||
register_lua_attribute<obj_type, value_type, LATTR_MAKE_UNIQUE_ID(getter_, id, obj_name), false>(name); \
|
||||
} \
|
||||
}; \
|
||||
static LATTR_MAKE_UNIQUE_ID(getter_adder_, id, obj_name) LATTR_MAKE_UNIQUE_ID(getter_adder_instance_, id, obj_name) ; \
|
||||
value_type LATTR_MAKE_UNIQUE_ID(getter_, id, obj_name)::get([[maybe_unused]] lua_State* L, const LATTR_MAKE_UNIQUE_ID(getter_, id, obj_name)::object_type& obj_name) const
|
||||
|
||||
|
||||
#define LATTR_SETTER5(name, value_type, obj_type, obj_name, id) \
|
||||
struct LATTR_MAKE_UNIQUE_ID(setter_, id, obj_name) : public lua_setter<obj_type, value_type> { \
|
||||
using object_type = obj_type; \
|
||||
void set(lua_State* L, object_type& obj_name, const value_type& value) const override; \
|
||||
}; \
|
||||
struct LATTR_MAKE_UNIQUE_ID(setter_adder_, id, obj_name) { \
|
||||
LATTR_MAKE_UNIQUE_ID(setter_adder_, id, obj_name) ()\
|
||||
{ \
|
||||
register_lua_attribute<obj_type, value_type, LATTR_MAKE_UNIQUE_ID(setter_, id, obj_name), true>(name); \
|
||||
} \
|
||||
}; \
|
||||
static LATTR_MAKE_UNIQUE_ID(setter_adder_, id, obj_name) LATTR_MAKE_UNIQUE_ID(setter_adder_instance_, id, obj_name); \
|
||||
void LATTR_MAKE_UNIQUE_ID(setter_, id, obj_name)::set([[maybe_unused]] lua_State* L, LATTR_MAKE_UNIQUE_ID(setter_, id, obj_name)::object_type& obj_name, const value_type& value) const
|
||||
|
||||
|
||||
/**
|
||||
* @param name: string attribute name
|
||||
* @param value_type: the type of the attribute, for example int or std::string
|
||||
* @param obj_type: the type of the object, for example lua_unit
|
||||
* @param obj_name: a name for the variable that will hold the object
|
||||
*/
|
||||
#define LATTR_GETTER(name, value_type, obj_type, obj_name) LATTR_GETTER5(name, value_type, obj_type, obj_name, __LINE__)
|
||||
|
||||
#define LATTR_SETTER(name, value_type, obj_type, obj_name) LATTR_SETTER5(name, value_type, obj_type, obj_name, __LINE__)
|
|
@ -21,15 +21,17 @@
|
|||
#include "map/location.hpp" // for map_location
|
||||
#include "map/map.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "scripting/lua_attributes.hpp"
|
||||
#include "scripting/lua_common.hpp"
|
||||
#include "scripting/lua_unit_attacks.hpp"
|
||||
#include "scripting/push_check.hpp"
|
||||
#include "units/unit.hpp"
|
||||
#include "units/map.hpp"
|
||||
#include "units/animation_component.hpp"
|
||||
#include "utils/optional_fwd.hpp"
|
||||
#include "game_version.hpp"
|
||||
#include "deprecation.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
static lg::log_domain log_scripting_lua("scripting/lua");
|
||||
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
|
||||
|
@ -269,6 +271,492 @@ static int impl_unit_tostring(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#define UNIT_GETTER(name, type) LATTR_GETTER(name, type, unit, u)
|
||||
#define UNIT_SETTER(name, type) LATTR_SETTER(name, type, unit, u)
|
||||
luaW_Registry unitReg{"wesnoth", "units", getunitKey};
|
||||
|
||||
template<> struct lua_object_traits<lua_unit*> {
|
||||
inline static auto metatable = getunitKey;
|
||||
inline static lua_unit* get(lua_State* L, int n) {
|
||||
auto lu = luaW_tounit_ref(L, n);
|
||||
if(!lu) unit_show_error(L, n, LU_NOT_UNIT);
|
||||
return lu;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct lua_object_traits<unit> {
|
||||
inline static auto metatable = getunitKey;
|
||||
inline static unit& get(lua_State* L, int n) {
|
||||
return luaW_checkunit(L, n);
|
||||
}
|
||||
};
|
||||
|
||||
static void handle_unit_move(lua_State* L, lua_unit* lu, map_location dst) {
|
||||
if(!lu->on_map()) {
|
||||
(*lu)->set_location(dst);
|
||||
} else {
|
||||
unit& u = *lu->get();
|
||||
|
||||
// Handle moving an on-map unit
|
||||
game_board* gb = resources::gameboard;
|
||||
|
||||
if(!gb) {
|
||||
return;
|
||||
}
|
||||
|
||||
map_location src = u.get_location();
|
||||
|
||||
// TODO: could probably be relegated to a helper function.
|
||||
if(src != dst) {
|
||||
// If the dst isn't on the map, the unit will be clobbered. Guard against that.
|
||||
if(!gb->map().on_board(dst)) {
|
||||
std::string err_msg = formatter() << "destination hex not on map (excluding border): " << dst;
|
||||
return void(luaL_argerror(L, 2, err_msg.c_str()));
|
||||
}
|
||||
|
||||
auto [unit_iterator, success] = gb->units().move(src, dst);
|
||||
|
||||
if(success) {
|
||||
unit_iterator->anim_comp().set_standing();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LATTR_GETTER("valid", utils::optional<std::string>, lua_unit*, lu) {
|
||||
const unit* pu = lu->get();
|
||||
if(!pu) {
|
||||
return utils::nullopt;
|
||||
}
|
||||
using namespace std::literals;
|
||||
if(lu->on_map()) {
|
||||
return "map"s;
|
||||
} else if(lu->on_recall_list()) {
|
||||
return "recall"s;
|
||||
}
|
||||
return "private"s;
|
||||
}
|
||||
|
||||
UNIT_GETTER("x", int) {
|
||||
return u.get_location().wml_x();
|
||||
}
|
||||
|
||||
LATTR_SETTER("x", int, lua_unit*, lu) {
|
||||
if(!lu->get()) return;
|
||||
map_location loc = (*lu)->get_location();
|
||||
loc.set_wml_x(value);
|
||||
handle_unit_move(L, lu, loc);
|
||||
}
|
||||
|
||||
UNIT_GETTER("y", int) {
|
||||
return u.get_location().wml_y();
|
||||
}
|
||||
|
||||
LATTR_SETTER("y", int, lua_unit*, lu) {
|
||||
if(!lu->get()) return;
|
||||
map_location loc = (*lu)->get_location();
|
||||
loc.set_wml_y(value);
|
||||
handle_unit_move(L, lu, loc);
|
||||
}
|
||||
|
||||
UNIT_GETTER("loc", map_location) {
|
||||
return u.get_location();
|
||||
}
|
||||
|
||||
LATTR_SETTER("loc", map_location, lua_unit*, lu) {
|
||||
if(!lu->get()) return;
|
||||
handle_unit_move(L, lu, value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("goto", map_location) {
|
||||
return u.get_goto();
|
||||
}
|
||||
|
||||
UNIT_SETTER("goto", map_location) {
|
||||
u.set_goto(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("side", int) {
|
||||
return u.side();
|
||||
}
|
||||
|
||||
UNIT_SETTER("side", int) {
|
||||
u.set_side(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("id", std::string) {
|
||||
return u.id();
|
||||
}
|
||||
|
||||
LATTR_SETTER("id", std::string, lua_unit*, lu) {
|
||||
if(!lu->get()) return;
|
||||
if(!lu->on_map()) luaL_argerror(L, 3, "can't modify id of on-map unit");
|
||||
(*lu)->set_id(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("type", std::string) {
|
||||
return u.type_id();
|
||||
}
|
||||
|
||||
UNIT_GETTER("image_mods", std::string) {
|
||||
return u.effect_image_mods();
|
||||
}
|
||||
|
||||
UNIT_GETTER("usage", std::string) {
|
||||
return u.usage();
|
||||
}
|
||||
|
||||
UNIT_SETTER("usage", std::string) {
|
||||
u.set_usage(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("ellipse", std::string) {
|
||||
return u.image_ellipse();
|
||||
}
|
||||
|
||||
UNIT_SETTER("ellipse", std::string) {
|
||||
u.set_image_ellipse(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("halo", std::string) {
|
||||
return u.image_halo();
|
||||
}
|
||||
|
||||
UNIT_SETTER("halo", std::string) {
|
||||
u.set_image_halo(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("hitpoints", int) {
|
||||
return u.hitpoints();
|
||||
}
|
||||
|
||||
UNIT_SETTER("hitpoints", int) {
|
||||
u.set_hitpoints(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("max_hitpoints", int) {
|
||||
return u.max_hitpoints();
|
||||
}
|
||||
|
||||
UNIT_SETTER("max_hitpoints", int) {
|
||||
u.set_max_hitpoints(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("experience", int) {
|
||||
return u.experience();
|
||||
}
|
||||
|
||||
UNIT_SETTER("experience", int) {
|
||||
u.set_experience(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("max_experience", int) {
|
||||
return u.max_experience();
|
||||
}
|
||||
|
||||
UNIT_SETTER("max_experience", int) {
|
||||
u.set_max_experience(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("recall_cost", int) {
|
||||
return u.recall_cost();
|
||||
}
|
||||
|
||||
UNIT_SETTER("recall_cost", int) {
|
||||
u.set_recall_cost(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("moves", int) {
|
||||
return u.movement_left();
|
||||
}
|
||||
|
||||
UNIT_SETTER("moves", int) {
|
||||
u.set_movement(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("max_moves", int) {
|
||||
return u.total_movement();
|
||||
}
|
||||
|
||||
UNIT_SETTER("max_moves", int) {
|
||||
u.set_total_movement(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("max_attacks", int) {
|
||||
return u.max_attacks();
|
||||
}
|
||||
|
||||
UNIT_SETTER("max_attacks", int) {
|
||||
u.set_max_attacks(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("attacks_left", int) {
|
||||
return u.attacks_left();
|
||||
}
|
||||
|
||||
UNIT_SETTER("attacks_left", int) {
|
||||
u.set_attacks(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("vision", int) {
|
||||
return u.vision();
|
||||
}
|
||||
|
||||
UNIT_GETTER("jamming", int) {
|
||||
return u.jamming();
|
||||
}
|
||||
|
||||
UNIT_GETTER("name", t_string) {
|
||||
return u.name();
|
||||
}
|
||||
|
||||
UNIT_SETTER("name", t_string) {
|
||||
u.set_name(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("description", t_string) {
|
||||
return u.unit_description();
|
||||
}
|
||||
|
||||
UNIT_SETTER("description", t_string) {
|
||||
u.set_unit_description(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("canrecruit", bool) {
|
||||
return u.can_recruit();
|
||||
}
|
||||
|
||||
UNIT_SETTER("canrecruit", bool) {
|
||||
u.set_can_recruit(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("renamable", bool) {
|
||||
return !u.unrenamable();
|
||||
}
|
||||
|
||||
UNIT_SETTER("renamable", bool) {
|
||||
u.set_unrenamable(!value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("level", int) {
|
||||
return u.level();
|
||||
}
|
||||
|
||||
UNIT_SETTER("level", int) {
|
||||
u.set_level(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("cost", int) {
|
||||
return u.cost();
|
||||
}
|
||||
|
||||
UNIT_GETTER("extra_recruit", std::vector<std::string>) {
|
||||
return u.recruits();
|
||||
}
|
||||
|
||||
UNIT_SETTER("extra_recruit", std::vector<std::string>) {
|
||||
u.set_recruits(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("advances_to", std::vector<std::string>) {
|
||||
return u.advances_to();
|
||||
}
|
||||
|
||||
UNIT_SETTER("advances_to", std::vector<std::string>) {
|
||||
u.set_advances_to(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("alignment", std::string) {
|
||||
return unit_alignments::get_string(u.alignment());
|
||||
}
|
||||
|
||||
UNIT_SETTER("alignment", lua_index_raw) {
|
||||
auto alignment = unit_alignments::get_enum(lua_check<std::string_view>(L, value.index));
|
||||
if(!alignment) luaL_argerror(L, value.index, "invalid unit alignment");
|
||||
u.set_alignment(*alignment);
|
||||
}
|
||||
|
||||
UNIT_GETTER("upkeep", lua_index_raw) {
|
||||
unit::upkeep_t upkeep = u.upkeep_raw();
|
||||
|
||||
// Need to keep these separate in order to ensure an int value is always used if applicable.
|
||||
if(int* v = utils::get_if<int>(&upkeep)) {
|
||||
lua_push(L, *v);
|
||||
} else {
|
||||
const std::string type = utils::visit(unit::upkeep_type_visitor(), upkeep);
|
||||
lua_push(L, type);
|
||||
}
|
||||
return lua_index_raw(L);
|
||||
}
|
||||
|
||||
UNIT_SETTER("upkeep", lua_index_raw) {
|
||||
if(lua_isnumber(L, value.index)) {
|
||||
u.set_upkeep(static_cast<int>(luaL_checkinteger(L, 3)));
|
||||
return;
|
||||
}
|
||||
auto v = lua_check<std::string_view>(L, value.index);
|
||||
if(v == "loyal" || v == "free") {
|
||||
u.set_upkeep(unit::upkeep_loyal());
|
||||
} else if(v == "full") {
|
||||
u.set_upkeep(unit::upkeep_full());
|
||||
} else {
|
||||
std::string err_msg = "unknown upkeep value of unit: ";
|
||||
err_msg += v;
|
||||
luaL_argerror(L, 2, err_msg.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
UNIT_GETTER("advancements", std::vector<config>) {
|
||||
return u.modification_advancements();
|
||||
}
|
||||
|
||||
UNIT_SETTER("advancements", std::vector<config>) {
|
||||
u.set_advancements(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("overlays", std::vector<std::string>) {
|
||||
return u.overlays();
|
||||
}
|
||||
|
||||
UNIT_GETTER("traits", std::vector<std::string>) {
|
||||
return u.get_traits_list();
|
||||
}
|
||||
|
||||
UNIT_GETTER("abilities", std::vector<std::string>) {
|
||||
return u.get_ability_list();
|
||||
}
|
||||
|
||||
UNIT_GETTER("status", lua_index_raw) {
|
||||
(void)u;
|
||||
lua_createtable(L, 1, 0);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_rawseti(L, -2, 1);
|
||||
luaL_setmetatable(L, ustatusKey);
|
||||
return lua_index_raw(L);
|
||||
}
|
||||
|
||||
UNIT_GETTER("variables", lua_index_raw) {
|
||||
(void)u;
|
||||
lua_createtable(L, 1, 0);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_rawseti(L, -2, 1);
|
||||
luaL_setmetatable(L, unitvarKey);
|
||||
return lua_index_raw(L);
|
||||
}
|
||||
|
||||
UNIT_GETTER("attacks", lua_index_raw) {
|
||||
(void)u;
|
||||
push_unit_attacks_table(L, 1);
|
||||
return lua_index_raw(L);
|
||||
}
|
||||
|
||||
UNIT_GETTER("petrified", bool) {
|
||||
deprecated_message("(unit).petrified", DEP_LEVEL::INDEFINITE, {1,17,0}, "use (unit).status.petrified instead");
|
||||
return u.incapacitated();
|
||||
}
|
||||
|
||||
UNIT_GETTER("animations", std::vector<std::string>) {
|
||||
return u.anim_comp().get_flags();
|
||||
}
|
||||
|
||||
UNIT_GETTER("recall_filter", config) {
|
||||
return u.recall_filter();
|
||||
}
|
||||
|
||||
UNIT_SETTER("recall_filter", config) {
|
||||
u.set_recall_filter(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("hidden", bool) {
|
||||
return u.get_hidden();
|
||||
}
|
||||
|
||||
UNIT_SETTER("hidden", bool) {
|
||||
u.set_hidden(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("resting", bool) {
|
||||
return u.resting();
|
||||
}
|
||||
|
||||
UNIT_SETTER("resting", bool) {
|
||||
u.set_resting(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("flying", bool) {
|
||||
return u.is_flying();
|
||||
}
|
||||
|
||||
UNIT_GETTER("fearless", bool) {
|
||||
return u.is_fearless();
|
||||
}
|
||||
|
||||
UNIT_GETTER("healthy", bool) {
|
||||
return u.is_healthy();
|
||||
}
|
||||
|
||||
UNIT_GETTER("zoc", bool) {
|
||||
return u.get_emit_zoc();
|
||||
}
|
||||
|
||||
UNIT_SETTER("zoc", bool) {
|
||||
u.set_emit_zoc(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("role", std::string) {
|
||||
return u.get_role();
|
||||
}
|
||||
|
||||
UNIT_SETTER("role", std::string) {
|
||||
u.set_role(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("race", std::string) {
|
||||
return u.race()->id();
|
||||
}
|
||||
|
||||
UNIT_GETTER("gender", std::string) {
|
||||
return gender_string(u.gender());
|
||||
}
|
||||
|
||||
UNIT_GETTER("variation", std::string) {
|
||||
return u.variation();
|
||||
}
|
||||
|
||||
UNIT_GETTER("undead_variation", std::string) {
|
||||
return u.undead_variation();
|
||||
}
|
||||
|
||||
UNIT_SETTER("undead_variation", std::string) {
|
||||
u.set_undead_variation(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("facing", std::string) {
|
||||
return map_location::write_direction(u.facing());
|
||||
}
|
||||
|
||||
UNIT_SETTER("facing", std::string) {
|
||||
u.set_facing(map_location::parse_direction(value));
|
||||
}
|
||||
|
||||
UNIT_GETTER("portrait", std::string) {
|
||||
return u.big_profile() == u.absolute_image()
|
||||
? u.absolute_image() + u.image_mods() + "~SCALE_SHARP(144,144)"
|
||||
: u.big_profile();
|
||||
}
|
||||
|
||||
UNIT_SETTER("portrait", std::string) {
|
||||
u.set_big_profile(value);
|
||||
}
|
||||
|
||||
UNIT_GETTER("__cfg", config) {
|
||||
config cfg;
|
||||
u.write(cfg);
|
||||
u.get_location().write(cfg);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets some data on a unit (__index metamethod).
|
||||
* - Arg 1: full userdata containing the unit id.
|
||||
|
@ -277,149 +765,7 @@ static int impl_unit_tostring(lua_State* L)
|
|||
*/
|
||||
static int impl_unit_get(lua_State *L)
|
||||
{
|
||||
lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
|
||||
char const *m = luaL_checkstring(L, 2);
|
||||
const unit* pu = lu->get();
|
||||
|
||||
if(strcmp(m, "valid") == 0) {
|
||||
if(!pu) {
|
||||
return 0;
|
||||
}
|
||||
if(lu->on_map()) {
|
||||
lua_pushstring(L, "map");
|
||||
} else if(lu->on_recall_list()) {
|
||||
lua_pushstring(L, "recall");
|
||||
} else {
|
||||
lua_pushstring(L, "private");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!pu) {
|
||||
return luaL_argerror(L, 1, "unknown unit");
|
||||
}
|
||||
|
||||
const unit& u = *pu;
|
||||
|
||||
// Find the corresponding attribute.
|
||||
return_int_attrib("x", u.get_location().wml_x());
|
||||
return_int_attrib("y", u.get_location().wml_y());
|
||||
if(strcmp(m, "loc") == 0) {
|
||||
luaW_pushlocation(L, u.get_location());
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "goto") == 0) {
|
||||
luaW_pushlocation(L, u.get_goto());
|
||||
return 1;
|
||||
}
|
||||
return_int_attrib("side", u.side());
|
||||
return_string_attrib("id", u.id());
|
||||
return_string_attrib("type", u.type_id());
|
||||
return_string_attrib("image_mods", u.effect_image_mods());
|
||||
return_string_attrib("usage", u.usage());
|
||||
return_string_attrib("ellipse", u.image_ellipse());
|
||||
return_string_attrib("halo", u.image_halo());
|
||||
return_int_attrib("hitpoints", u.hitpoints());
|
||||
return_int_attrib("max_hitpoints", u.max_hitpoints());
|
||||
return_int_attrib("experience", u.experience());
|
||||
return_int_attrib("max_experience", u.max_experience());
|
||||
return_int_attrib("recall_cost", u.recall_cost());
|
||||
return_int_attrib("moves", u.movement_left());
|
||||
return_int_attrib("max_moves", u.total_movement());
|
||||
return_int_attrib("max_attacks", u.max_attacks());
|
||||
return_int_attrib("attacks_left", u.attacks_left());
|
||||
return_int_attrib("vision", u.vision());
|
||||
return_int_attrib("jamming", u.jamming());
|
||||
return_tstring_attrib("name", u.name());
|
||||
return_tstring_attrib("description", u.unit_description());
|
||||
return_bool_attrib("canrecruit", u.can_recruit());
|
||||
return_bool_attrib("renamable", !u.unrenamable());
|
||||
return_int_attrib("level", u.level());
|
||||
return_int_attrib("cost", u.cost());
|
||||
|
||||
return_vector_string_attrib("extra_recruit", u.recruits());
|
||||
return_vector_string_attrib("advances_to", u.advances_to());
|
||||
|
||||
if(strcmp(m, "alignment") == 0) {
|
||||
lua_push(L, unit_alignments::get_string(u.alignment()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(strcmp(m, "upkeep") == 0) {
|
||||
unit::upkeep_t upkeep = u.upkeep_raw();
|
||||
|
||||
// Need to keep these separate in order to ensure an int value is always used if applicable.
|
||||
if(int* v = utils::get_if<int>(&upkeep)) {
|
||||
lua_push(L, *v);
|
||||
} else {
|
||||
const std::string type = utils::visit(unit::upkeep_type_visitor(), upkeep);
|
||||
lua_push(L, type);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "advancements") == 0) {
|
||||
lua_push(L, u.modification_advancements());
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "overlays") == 0) {
|
||||
lua_push(L, u.overlays());
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "traits") == 0) {
|
||||
lua_push(L, u.get_traits_list());
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "abilities") == 0) {
|
||||
lua_push(L, u.get_ability_list());
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "status") == 0) {
|
||||
lua_createtable(L, 1, 0);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_rawseti(L, -2, 1);
|
||||
luaL_setmetatable(L, ustatusKey);
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "variables") == 0) {
|
||||
lua_createtable(L, 1, 0);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_rawseti(L, -2, 1);
|
||||
luaL_setmetatable(L, unitvarKey);
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "attacks") == 0) {
|
||||
push_unit_attacks_table(L, 1);
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(m, "petrified") == 0) {
|
||||
deprecated_message("(unit).petrified", DEP_LEVEL::INDEFINITE, {1,17,0}, "use (unit).status.petrified instead");
|
||||
lua_pushboolean(L, u.incapacitated());
|
||||
return 1;
|
||||
}
|
||||
return_vector_string_attrib("animations", u.anim_comp().get_flags());
|
||||
return_cfg_attrib("recall_filter", cfg = u.recall_filter());
|
||||
return_bool_attrib("hidden", u.get_hidden());
|
||||
return_bool_attrib("resting", u.resting());
|
||||
return_bool_attrib("flying", u.is_flying());
|
||||
return_bool_attrib("fearless", u.is_fearless());
|
||||
return_bool_attrib("healthy", u.is_healthy());
|
||||
return_string_attrib("role", u.get_role());
|
||||
return_string_attrib("race", u.race()->id());
|
||||
return_string_attrib("gender", gender_string(u.gender()));
|
||||
return_string_attrib("variation", u.variation());
|
||||
return_string_attrib("undead_variation", u.undead_variation());
|
||||
return_bool_attrib("zoc", u.get_emit_zoc());
|
||||
return_string_attrib("facing", map_location::write_direction(u.facing()));
|
||||
return_string_attrib("portrait", u.big_profile() == u.absolute_image()
|
||||
? u.absolute_image() + u.image_mods() + "~SCALE_SHARP(144,144)"
|
||||
: u.big_profile());
|
||||
return_cfg_attrib("__cfg", u.write(cfg); u.get_location().write(cfg));
|
||||
|
||||
if(luaW_getglobal(L, "wesnoth", "units", m)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return unitReg.get(L);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -430,132 +776,18 @@ static int impl_unit_get(lua_State *L)
|
|||
*/
|
||||
static int impl_unit_set(lua_State *L)
|
||||
{
|
||||
lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
|
||||
char const *m = luaL_checkstring(L, 2);
|
||||
unit* pu = lu->get();
|
||||
if (!pu) return luaL_argerror(L, 1, "unknown unit");
|
||||
unit &u = *pu;
|
||||
return unitReg.set(L);
|
||||
}
|
||||
|
||||
// Find the corresponding attribute.
|
||||
//modify_int_attrib_check_range("side", u.set_side(value), 1, static_cast<int>(teams().size())); TODO: Figure out if this is a good idea, to refer to teams() and make this depend on having a gamestate
|
||||
modify_int_attrib("side", u.set_side(value));
|
||||
modify_int_attrib("moves", u.set_movement(value));
|
||||
modify_int_attrib("max_moves", u.set_total_movement(value));
|
||||
modify_int_attrib("max_attacks", u.set_max_attacks(value));
|
||||
modify_int_attrib("hitpoints", u.set_hitpoints(value));
|
||||
modify_int_attrib("max_hitpoints", u.set_max_hitpoints(value));
|
||||
modify_int_attrib("experience", u.set_experience(value));
|
||||
modify_int_attrib("max_experience", u.set_max_experience(value));
|
||||
modify_int_attrib("recall_cost", u.set_recall_cost(value));
|
||||
modify_int_attrib("attacks_left", u.set_attacks(value));
|
||||
modify_int_attrib("level", u.set_level(value));
|
||||
modify_bool_attrib("resting", u.set_resting(value));
|
||||
modify_tstring_attrib("name", u.set_name(value));
|
||||
modify_tstring_attrib("description", u.set_unit_description(value));
|
||||
modify_string_attrib("portrait", u.set_big_profile(value));
|
||||
modify_string_attrib("role", u.set_role(value));
|
||||
modify_string_attrib("facing", u.set_facing(map_location::parse_direction(value)));
|
||||
modify_string_attrib("usage", u.set_usage(value));
|
||||
modify_string_attrib("undead_variation", u.set_undead_variation(value));
|
||||
modify_string_attrib("ellipse", u.set_image_ellipse(value));
|
||||
modify_string_attrib("halo", u.set_image_halo(value));
|
||||
modify_bool_attrib("hidden", u.set_hidden(value));
|
||||
modify_bool_attrib("zoc", u.set_emit_zoc(value));
|
||||
modify_bool_attrib("canrecruit", u.set_can_recruit(value));
|
||||
modify_bool_attrib("renamable", u.set_unrenamable(!value));
|
||||
modify_cfg_attrib("recall_filter", u.set_recall_filter(cfg));
|
||||
|
||||
modify_vector_string_attrib("extra_recruit", u.set_recruits(value));
|
||||
modify_vector_string_attrib("advances_to", u.set_advances_to(value));
|
||||
if(strcmp(m, "alignment") == 0) {
|
||||
u.set_alignment(lua_enum_check<unit_alignments>(L, 3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(strcmp(m, "advancements") == 0) {
|
||||
u.set_advancements(lua_check<std::vector<config>>(L, 3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(strcmp(m, "upkeep") == 0) {
|
||||
if(lua_isnumber(L, 3)) {
|
||||
u.set_upkeep(static_cast<int>(luaL_checkinteger(L, 3)));
|
||||
return 0;
|
||||
}
|
||||
const char* v = luaL_checkstring(L, 3);
|
||||
if((strcmp(v, "loyal") == 0) || (strcmp(v, "free") == 0)) {
|
||||
u.set_upkeep(unit::upkeep_loyal());
|
||||
} else if(strcmp(v, "full") == 0) {
|
||||
u.set_upkeep(unit::upkeep_full());
|
||||
} else {
|
||||
std::string err_msg = "unknown upkeep value of unit: ";
|
||||
err_msg += v;
|
||||
return luaL_argerror(L, 2, err_msg.c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!lu->on_map()) {
|
||||
map_location loc = u.get_location();
|
||||
modify_int_attrib("x", loc.set_wml_x(value); u.set_location(loc));
|
||||
modify_int_attrib("y", loc.set_wml_y(value); u.set_location(loc));
|
||||
modify_string_attrib("id", u.set_id(value));
|
||||
if(strcmp(m, "loc") == 0) {
|
||||
luaW_tolocation(L, 3, loc);
|
||||
u.set_location(loc);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
const bool is_key_x = strcmp(m, "x") == 0;
|
||||
const bool is_key_y = strcmp(m, "y") == 0;
|
||||
const bool is_loc_key = strcmp(m, "loc") == 0;
|
||||
|
||||
// Handle moving an on-map unit
|
||||
if(is_key_x || is_key_y || is_loc_key) {
|
||||
game_board* gb = resources::gameboard;
|
||||
|
||||
if(!gb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
map_location src = u.get_location();
|
||||
map_location dst = src;
|
||||
|
||||
if(is_key_x) {
|
||||
dst.set_wml_x(luaL_checkinteger(L, 3));
|
||||
} else if(is_key_y) {
|
||||
dst.set_wml_y(luaL_checkinteger(L, 3));
|
||||
} else {
|
||||
dst = luaW_checklocation(L, 3);
|
||||
}
|
||||
|
||||
// TODO: could probably be relegated to a helper function.
|
||||
if(src != dst) {
|
||||
// If the dst isn't on the map, the unit will be clobbered. Guard against that.
|
||||
if(!gb->map().on_board(dst)) {
|
||||
std::string err_msg = formatter() << "destination hex not on map (excluding border): " << dst;
|
||||
return luaL_argerror(L, 2, err_msg.c_str());
|
||||
}
|
||||
|
||||
auto [unit_iterator, success] = gb->units().move(src, dst);
|
||||
|
||||
if(success) {
|
||||
unit_iterator->anim_comp().set_standing();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(strcmp(m, "goto") == 0) {
|
||||
u.set_goto(luaW_checklocation(L, 3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string err_msg = "unknown modifiable property of unit: ";
|
||||
err_msg += m;
|
||||
return luaL_argerror(L, 2, err_msg.c_str());
|
||||
/**
|
||||
* Prints valid attributes on a unit (__dir metamethod).
|
||||
* - Arg 1: full userdata containing the unit id.
|
||||
* - Arg 2: string containing the name of the property.
|
||||
* - Ret 1: a list of attributes.
|
||||
*/
|
||||
static int impl_unit_dir(lua_State *L)
|
||||
{
|
||||
return unitReg.dir(L);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -673,6 +905,8 @@ namespace lua_units {
|
|||
lua_setfield(L, -2, "__index");
|
||||
lua_pushcfunction(L, impl_unit_set);
|
||||
lua_setfield(L, -2, "__newindex");
|
||||
lua_pushcfunction(L, impl_unit_dir);
|
||||
lua_setfield(L, -2, "__dir");
|
||||
lua_pushstring(L, "unit");
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "scripting/lua_unit_type.hpp"
|
||||
#include "scripting/push_check.hpp"
|
||||
#include "scripting/lua_widget.hpp"
|
||||
#include "scripting/lua_attributes.hpp"
|
||||
#include "scripting/lua_widget_attributes.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
|
@ -103,20 +104,6 @@ static tgetters getters;
|
|||
using tsetters = std::map<std::string, std::vector<std::function<bool(lua_State*, int, gui2::widget&, bool)>>>;
|
||||
static tsetters setters;
|
||||
|
||||
template<typename widget_type, typename value_type>
|
||||
struct widget_getter
|
||||
{
|
||||
virtual value_type get(lua_State* L, widget_type& w) const = 0;
|
||||
virtual ~widget_getter() = default;
|
||||
};
|
||||
|
||||
template<typename widget_type, typename value_type>
|
||||
struct widget_setter
|
||||
{
|
||||
virtual void set(lua_State* L, widget_type& w, const value_type& value) const = 0;
|
||||
virtual ~widget_setter() = default;
|
||||
};
|
||||
|
||||
template<typename widget_type, typename value_type, typename action_type, bool setter>
|
||||
void register_widget_attribute(const char* name)
|
||||
{
|
||||
|
@ -151,8 +138,8 @@ void register_widget_attribute(const char* name)
|
|||
}
|
||||
|
||||
#define WIDGET_GETTER4(name, value_type, widgt_type, id) \
|
||||
struct BOOST_PP_CAT(getter_, id) : public widget_getter<widgt_type, value_type> { \
|
||||
value_type get(lua_State* L, widgt_type& w) const override; \
|
||||
struct BOOST_PP_CAT(getter_, id) : public lua_getter<widgt_type, value_type> { \
|
||||
value_type get(lua_State* L, const widgt_type& w) const override; \
|
||||
}; \
|
||||
struct BOOST_PP_CAT(getter_adder_, id) { \
|
||||
BOOST_PP_CAT(getter_adder_, id) () \
|
||||
|
@ -161,11 +148,11 @@ struct BOOST_PP_CAT(getter_adder_, id) { \
|
|||
} \
|
||||
}; \
|
||||
static BOOST_PP_CAT(getter_adder_, id) BOOST_PP_CAT(getter_adder_instance_, id) ; \
|
||||
value_type BOOST_PP_CAT(getter_, id)::get([[maybe_unused]] lua_State* L, widgt_type& w) const
|
||||
value_type BOOST_PP_CAT(getter_, id)::get([[maybe_unused]] lua_State* L, const widgt_type& w) const
|
||||
|
||||
|
||||
#define WIDGET_SETTER4(name, value_type, widgt_type, id) \
|
||||
struct BOOST_PP_CAT(setter_, id) : public widget_setter<widgt_type, value_type> { \
|
||||
struct BOOST_PP_CAT(setter_, id) : public lua_setter<widgt_type, value_type> { \
|
||||
void set(lua_State* L, widgt_type& w, const value_type& value) const override; \
|
||||
}; \
|
||||
struct BOOST_PP_CAT(setter_adder_, id) { \
|
||||
|
@ -431,13 +418,13 @@ WIDGET_SETTER("value_compat,label", t_string, gui2::styled_widget)
|
|||
|
||||
WIDGET_GETTER("type", std::string, gui2::widget)
|
||||
{
|
||||
if(gui2::styled_widget* sw = dynamic_cast<gui2::styled_widget*>(&w)) {
|
||||
if(const gui2::styled_widget* sw = dynamic_cast<const gui2::styled_widget*>(&w)) {
|
||||
return sw->get_control_type();
|
||||
}
|
||||
else if(dynamic_cast<gui2::tree_view_node*>(&w)) {
|
||||
else if(dynamic_cast<const gui2::tree_view_node*>(&w)) {
|
||||
return "tree_view_node";
|
||||
}
|
||||
else if(dynamic_cast<gui2::grid*>(&w)) {
|
||||
else if(dynamic_cast<const gui2::grid*>(&w)) {
|
||||
return "grid";
|
||||
}
|
||||
else {
|
||||
|
@ -539,7 +526,9 @@ int impl_widget_get(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
ERR_LUA << "invalid property of '" << typeid(w).name()<< "' widget :" << str;
|
||||
return luaL_argerror(L, 2, "invalid property of widget");
|
||||
std::string err = "invalid property of widget: ";
|
||||
err += str;
|
||||
return luaL_argerror(L, 2, err.c_str());
|
||||
}
|
||||
|
||||
int impl_widget_set(lua_State* L)
|
||||
|
@ -562,7 +551,9 @@ int impl_widget_set(lua_State* L)
|
|||
|
||||
}
|
||||
ERR_LUA << "invalid modifiable property of '" << typeid(w).name()<< "' widget:" << str;
|
||||
return luaL_argerror(L, 2, "invalid modifiable property of widget");
|
||||
std::string err = "invalid modifiable property of widget: ";
|
||||
err += str;
|
||||
return luaL_argerror(L, 2, err.c_str());
|
||||
}
|
||||
|
||||
int impl_widget_dir(lua_State* L)
|
||||
|
|
|
@ -25,7 +25,11 @@
|
|||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
struct lua_index_raw { int index; };
|
||||
struct lua_index_raw {
|
||||
int index;
|
||||
lua_index_raw(int i) : index(i) {}
|
||||
lua_index_raw(lua_State* L) : index(lua_gettop(L)) {}
|
||||
};
|
||||
|
||||
namespace lua_check_impl
|
||||
{
|
||||
|
@ -66,15 +70,21 @@ namespace lua_check_impl
|
|||
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_same_v<T, lua_index_raw>, lua_index_raw>
|
||||
lua_check(lua_State * /*L*/, int n)
|
||||
lua_check(lua_State * L, int n)
|
||||
{
|
||||
return lua_index_raw{ n };
|
||||
return lua_index_raw{ lua_absindex(L, n) };
|
||||
}
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_same_v<T, lua_index_raw>, lua_index_raw>
|
||||
lua_to_or_default(lua_State * /*L*/, int n, const T& /*def*/)
|
||||
lua_to_or_default(lua_State * L, int n, const T& /*def*/)
|
||||
{
|
||||
return lua_index_raw{ n };
|
||||
return lua_index_raw{ lua_absindex(L, n) };
|
||||
}
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_same_v<T, lua_index_raw>, void>
|
||||
lua_push(lua_State * L, lua_index_raw n)
|
||||
{
|
||||
lua_pushvalue(L, n.index);
|
||||
}
|
||||
|
||||
//std::string
|
||||
|
@ -374,12 +384,6 @@ namespace lua_check_impl
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename T::type lua_enum_check(lua_State *L, int n)
|
||||
{
|
||||
return lua_check_impl::lua_check<T>(L, n);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::decay_t<T> lua_check(lua_State *L, int n)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue