From dfa0c15ce045705f05487d623dc7beca6c945b4b Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Sun, 21 May 2017 07:40:55 -0500 Subject: [PATCH] [CSM] Add function to get the definition of items (#5732) Add node def and item def documentation. Please be ready for merge! --- doc/client_lua_api.md | 98 +++++++++++- src/script/common/c_content.cpp | 255 ++++++++++++++++++++++++++++++++ src/script/common/c_content.h | 18 +++ src/script/lua_api/l_client.cpp | 47 ++++++ src/script/lua_api/l_client.h | 10 +- 5 files changed, 425 insertions(+), 3 deletions(-) diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index 5b70c2a67..ce0746df4 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -989,6 +989,96 @@ Can be obtained via `minetest.get_meta(pos)`. * `inventory`: `{list1 = {}, ...}}` ----------------- +### Definitions +* `minetest.get_node_def(nodename)` + * Returns [node definition](#node-definition) table of `nodename` +* `minetest.get_item_def(itemstring)` + * Returns item definition table of `itemstring` + +#### Node Definition + +```lua + { + has_on_construct = bool, -- Whether the node has the on_construct callback defined + has_on_destruct = bool, -- Whether the node has the on_destruct callback defined + has_after_destruct = bool, -- Whether the node has the after_destruct callback defined + name = string, -- The name of the node e.g. "air", "default:dirt" + groups = table, -- The groups of the node + paramtype = string, -- Paramtype of the node + paramtype2 = string, -- ParamType2 of the node + drawtype = string, -- Drawtype of the node + mesh = , -- Mesh name if existant + minimap_color = , -- Color of node on minimap *May not exist* + visual_scale = number, -- Visual scale of node + alpha = number, -- Alpha of the node. Only used for liquids + color = , -- Color of node *May not exist* + palette_name = , -- Filename of palette *May not exist* + palette = <{ -- List of colors + Color, + Color + }>, + waving = number, -- 0 of not waving, 1 if waving + connect_sides = number, -- Used for connected nodes + connects_to = { -- List of nodes to connect to + "node1", + "node2" + }, + post_effect_color = Color, -- Color overlayed on the screen when the player is in the node + leveled = number, -- Max level for node + sunlight_propogates = bool, -- Whether light passes through the block + light_source = number, -- Light emitted by the block + is_ground_content = bool, -- Whether caves should cut through the node + walkable = bool, -- Whether the player collides with the node + pointable = bool, -- Whether the player can select the node + diggable = bool, -- Whether the player can dig the node + climbable = bool, -- Whether the player can climb up the node + buildable_to = bool, -- Whether the player can replace the node by placing a node on it + rightclickable = bool, -- Whether the player can place nodes pointing at this node + damage_per_second = number, -- HP of damage per second when the player is in the node + liquid_type = , -- A string containing "none", "flowing", or "source" *May not exist* + liquid_alternative_flowing = , -- Alternative node for liquid *May not exist* + liquid_alternative_source = , -- Alternative node for liquid *May not exist* + liquid_viscosity = , -- How fast the liquid flows *May not exist* + liquid_renewable = , -- Whether the liquid makes an infinite source *May not exist* + liquid_range = , -- How far the liquid flows *May not exist* + drowning = bool, -- Whether the player will drown in the node + floodable = bool, -- Whether nodes will be replaced by liquids (flooded) + node_box = table, -- Nodebox to draw the node with + collision_box = table, -- Nodebox to set the collision area + selection_box = table, -- Nodebox to set the area selected by the player + sounds = { -- Table of sounds that the block makes + sound_footstep = SimpleSoundSpec, + sound_dig = SimpleSoundSpec, + sound_dug = SimpleSoundSpec + }, + legacy_facedir_simple = bool, -- Whether to use old facedir + legacy_wallmounted = bool -- Whether to use old wallmounted + } +``` + +#### Item Definition + +```lua + { + name = string, -- Name of the item e.g. "default:stone" + description = string, -- Description of the item e.g. "Stone" + type = string, -- Item type: "none", "node", "craftitem", "tool" + inventory_image = string, -- Image in the inventory + wield_image = string, -- Image in wieldmesh + palette_image = string, -- Image for palette + color = Color, -- Color for item + wield_scale = Vector, -- Wieldmesh scale + stack_max = number, -- Number of items stackable together + usable = bool, -- Has on_use callback defined + liquids_pointable = bool, -- Whether you can point at liquids with the item + tool_capabilities = , -- If the item is a tool, tool capabiltites of the item + groups = table, -- Groups of the item + sound_place = SimpleSoundSpec, -- Sound played when placed + sound_place_failed = SimpleSoundSpec, -- Sound played when placement failed + node_placement_prediction = string -- Node placed in client until server catches up + } +``` +----------------- ### Chat command definition (`register_chatcommand`) @@ -1014,7 +1104,7 @@ Most text can contain escape sequences, that can for example color the text. There are a few exceptions: tab headers, dropdowns and vertical labels can't. The following functions provide escape sequences: * `minetest.get_color_escape_sequence(color)`: - * `color` is a ColorString + * `color` is a [ColorString](#colorstring) * The escape sequence sets the text color to `color` * `minetest.colorize(color, message)`: * Equivalent to: @@ -1022,7 +1112,7 @@ The following functions provide escape sequences: message .. minetest.get_color_escape_sequence("#ffffff")` * `color.get_background_escape_sequence(color)` - * `color` is a ColorString + * `color` is a [ColorString](#colorstring) * The escape sequence sets the background of the whole text element to `color`. Only defined for item descriptions and tooltips. * `color.strip_foreground_colors(str)` @@ -1047,3 +1137,7 @@ Named colors are also supported and are equivalent to To specify the value of the alpha channel, append `#AA` to the end of the color name (e.g. `colorname#08`). For named colors the hexadecimal string representing the alpha value must (always) be two hexadecimal digits. + +`Color` +------------- +`{a = alpha, r = red, g = green, b = blue}` defines an ARGB8 color. diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 7eb1d094b..c0e29abf0 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_schematic.h" #include "noise.h" #include "util/pointedthing.h" +#include "debug.h" // For FATAL_ERROR #include struct EnumString es_TileAnimationType[] = @@ -128,6 +129,51 @@ void push_item_definition(lua_State *L, const ItemDefinition &i) lua_setfield(L, -2, "description"); } +void push_item_definition_full(lua_State *L, const ItemDefinition &i) +{ + std::string type(es_ItemType[(int)i.type].str); + + lua_newtable(L); + lua_pushstring(L, i.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, i.description.c_str()); + lua_setfield(L, -2, "description"); + lua_pushstring(L, type.c_str()); + lua_setfield(L, -2, "type"); + lua_pushstring(L, i.inventory_image.c_str()); + lua_setfield(L, -2, "inventory_image"); + lua_pushstring(L, i.wield_image.c_str()); + lua_setfield(L, -2, "wield_image"); + lua_pushstring(L, i.palette_image.c_str()); + lua_setfield(L, -2, "palette_image"); + push_ARGB8(L, i.color); + lua_setfield(L, -2, "color"); + push_v3f(L, i.wield_scale); + lua_setfield(L, -2, "wield_scale"); + lua_pushinteger(L, i.stack_max); + lua_setfield(L, -2, "stack_max"); + lua_pushboolean(L, i.usable); + lua_setfield(L, -2, "usable"); + lua_pushboolean(L, i.liquids_pointable); + lua_setfield(L, -2, "liquids_pointable"); + if (i.type == ITEM_TOOL) { + push_tool_capabilities(L, ToolCapabilities( + i.tool_capabilities->full_punch_interval, + i.tool_capabilities->max_drop_level, + i.tool_capabilities->groupcaps, + i.tool_capabilities->damageGroups)); + lua_setfield(L, -2, "tool_capabilities"); + } + push_groups(L, i.groups); + lua_setfield(L, -2, "groups"); + push_soundspec(L, i.sound_place); + lua_setfield(L, -2, "sound_place"); + push_soundspec(L, i.sound_place_failed); + lua_setfield(L, -2, "sound_place_failed"); + lua_pushstring(L, i.node_placement_prediction.c_str()); + lua_setfield(L, -2, "node_placement_prediction"); +} + /******************************************************************************/ void read_object_properties(lua_State *L, int index, ObjectProperties *prop, IItemDefManager *idef) @@ -669,6 +715,204 @@ ContentFeatures read_content_features(lua_State *L, int index) return f; } +void push_content_features(lua_State *L, const ContentFeatures &c) +{ + std::string paramtype(ScriptApiNode::es_ContentParamType[(int)c.param_type].str); + std::string paramtype2(ScriptApiNode::es_ContentParamType2[(int)c.param_type_2].str); + std::string drawtype(ScriptApiNode::es_DrawType[(int)c.drawtype].str); + std::string liquid_type(ScriptApiNode::es_LiquidType[(int)c.liquid_type].str); + + /* Missing "tiles" because I don't see a usecase (at least not yet). */ + + lua_newtable(L); + lua_pushboolean(L, c.has_on_construct); + lua_setfield(L, -2, "has_on_construct"); + lua_pushboolean(L, c.has_on_destruct); + lua_setfield(L, -2, "has_on_destruct"); + lua_pushboolean(L, c.has_after_destruct); + lua_setfield(L, -2, "has_after_destruct"); + lua_pushstring(L, c.name.c_str()); + lua_setfield(L, -2, "name"); + push_groups(L, c.groups); + lua_setfield(L, -2, "groups"); + lua_pushstring(L, paramtype.c_str()); + lua_setfield(L, -2, "paramtype"); + lua_pushstring(L, paramtype2.c_str()); + lua_setfield(L, -2, "paramtype2"); + lua_pushstring(L, drawtype.c_str()); + lua_setfield(L, -2, "drawtype"); + if (!c.mesh.empty()) { + lua_pushstring(L, c.mesh.c_str()); + lua_setfield(L, -2, "mesh"); + } +#ifndef SERVER + push_ARGB8(L, c.minimap_color); // I know this is not set-able w/ register_node, + lua_setfield(L, -2, "minimap_color"); // but the people need to know! +#endif + lua_pushnumber(L, c.visual_scale); + lua_setfield(L, -2, "visual_scale"); + lua_pushnumber(L, c.alpha); + lua_setfield(L, -2, "alpha"); + if (!c.palette_name.empty()) { + push_ARGB8(L, c.color); + lua_setfield(L, -2, "color"); + + lua_pushstring(L, c.palette_name.c_str()); + lua_setfield(L, -2, "palette_name"); + + push_palette(L, c.palette); + lua_setfield(L, -2, "palette"); + } + lua_pushnumber(L, c.waving); + lua_setfield(L, -2, "waving"); + lua_pushnumber(L, c.connect_sides); + lua_setfield(L, -2, "connect_sides"); + + lua_newtable(L); + u16 i = 1; + for (std::vector::const_iterator it = c.connects_to.begin(); + it != c.connects_to.end(); ++it) { + lua_pushlstring(L, it->c_str(), it->size()); + lua_rawseti(L, -2, i); + } + lua_setfield(L, -2, "connects_to"); + + push_ARGB8(L, c.post_effect_color); + lua_setfield(L, -2, "post_effect_color"); + lua_pushnumber(L, c.leveled); + lua_setfield(L, -2, "leveled"); + lua_pushboolean(L, c.sunlight_propagates); + lua_setfield(L, -2, "sunlight_propagates"); + lua_pushnumber(L, c.light_source); + lua_setfield(L, -2, "light_source"); + lua_pushboolean(L, c.is_ground_content); + lua_setfield(L, -2, "is_ground_content"); + lua_pushboolean(L, c.walkable); + lua_setfield(L, -2, "walkable"); + lua_pushboolean(L, c.pointable); + lua_setfield(L, -2, "pointable"); + lua_pushboolean(L, c.diggable); + lua_setfield(L, -2, "diggable"); + lua_pushboolean(L, c.climbable); + lua_setfield(L, -2, "climbable"); + lua_pushboolean(L, c.buildable_to); + lua_setfield(L, -2, "buildable_to"); + lua_pushboolean(L, c.rightclickable); + lua_setfield(L, -2, "rightclickable"); + lua_pushnumber(L, c.damage_per_second); + lua_setfield(L, -2, "damage_per_second"); + if (c.isLiquid()) { + lua_pushstring(L, liquid_type.c_str()); + lua_setfield(L, -2, "liquid_type"); + lua_pushstring(L, c.liquid_alternative_flowing.c_str()); + lua_setfield(L, -2, "liquid_alternative_flowing"); + lua_pushstring(L, c.liquid_alternative_source.c_str()); + lua_setfield(L, -2, "liquid_alternative_source"); + lua_pushnumber(L, c.liquid_viscosity); + lua_setfield(L, -2, "liquid_viscosity"); + lua_pushboolean(L, c.liquid_renewable); + lua_setfield(L, -2, "liquid_renewable"); + lua_pushnumber(L, c.liquid_range); + lua_setfield(L, -2, "liquid_range"); + } + lua_pushnumber(L, c.drowning); + lua_setfield(L, -2, "drowning"); + lua_pushboolean(L, c.floodable); + lua_setfield(L, -2, "floodable"); + push_nodebox(L, c.node_box); + lua_setfield(L, -2, "node_box"); + push_nodebox(L, c.selection_box); + lua_setfield(L, -2, "selection_box"); + push_nodebox(L, c.collision_box); + lua_setfield(L, -2, "collision_box"); + lua_newtable(L); + push_soundspec(L, c.sound_footstep); + lua_setfield(L, -2, "sound_footstep"); + push_soundspec(L, c.sound_dig); + lua_setfield(L, -2, "sound_dig"); + push_soundspec(L, c.sound_dug); + lua_setfield(L, -2, "sound_dug"); + lua_setfield(L, -2, "sounds"); + lua_pushboolean(L, c.legacy_facedir_simple); + lua_setfield(L, -2, "legacy_facedir_simple"); + lua_pushboolean(L, c.legacy_wallmounted); + lua_setfield(L, -2, "legacy_wallmounted"); +} + +/******************************************************************************/ +void push_nodebox(lua_State *L, const NodeBox &box) +{ + lua_newtable(L); + switch (box.type) + { + case NODEBOX_REGULAR: + lua_pushstring(L, "regular"); + lua_setfield(L, -2, "type"); + break; + case NODEBOX_LEVELED: + case NODEBOX_FIXED: + lua_pushstring(L, "fixed"); + lua_setfield(L, -2, "type"); + push_box(L, box.fixed); + lua_setfield(L, -2, "fixed"); + break; + case NODEBOX_WALLMOUNTED: + lua_pushstring(L, "wallmounted"); + lua_setfield(L, -2, "type"); + push_aabb3f(L, box.wall_top); + lua_setfield(L, -2, "wall_top"); + push_aabb3f(L, box.wall_bottom); + lua_setfield(L, -2, "wall_bottom"); + push_aabb3f(L, box.wall_side); + lua_setfield(L, -2, "wall_side"); + break; + case NODEBOX_CONNECTED: + lua_pushstring(L, "connected"); + lua_setfield(L, -2, "type"); + push_box(L, box.connect_top); + lua_setfield(L, -2, "connect_top"); + push_box(L, box.connect_bottom); + lua_setfield(L, -2, "connect_bottom"); + push_box(L, box.connect_front); + lua_setfield(L, -2, "connect_front"); + push_box(L, box.connect_back); + lua_setfield(L, -2, "connect_back"); + push_box(L, box.connect_left); + lua_setfield(L, -2, "connect_left"); + push_box(L, box.connect_right); + lua_setfield(L, -2, "connect_right"); + break; + default: + FATAL_ERROR("Invalid box.type"); + break; + } +} + +void push_box(lua_State *L, const std::vector &box) +{ + lua_newtable(L); + u8 i = 1; + for (std::vector::const_iterator it = box.begin(); + it != box.end(); ++it) { + push_aabb3f(L, (*it)); + lua_rawseti(L, -2, i); + } +} + +/******************************************************************************/ +void push_palette(lua_State *L, const std::vector *palette) +{ + lua_createtable(L, palette->size(), 0); + int newTable = lua_gettop(L); + int index = 1; + std::vector::const_iterator iter; + for (iter = palette->begin(); iter != palette->end(); ++iter) { + push_ARGB8(L, (*iter)); + lua_rawseti(L, newTable, index); + index++; + } +} + /******************************************************************************/ void read_server_sound_params(lua_State *L, int index, ServerSoundParams ¶ms) @@ -719,6 +963,17 @@ void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec) } } +void push_soundspec(lua_State *L, const SimpleSoundSpec &spec) +{ + lua_newtable(L); + lua_pushstring(L, spec.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushnumber(L, spec.gain); + lua_setfield(L, -2, "gain"); + lua_pushnumber(L, spec.fade); + lua_setfield(L, -2, "fade"); +} + /******************************************************************************/ NodeBox read_nodebox(lua_State *L, int index) { diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 28d8b1e8c..9b8796297 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -39,6 +39,7 @@ extern "C" { #include "util/string.h" #include "itemgroup.h" #include "itemdef.h" +#include "c_types.h" namespace Json { class Value; } @@ -64,8 +65,20 @@ class Schematic; ContentFeatures read_content_features (lua_State *L, int index); +void push_content_features (lua_State *L, + const ContentFeatures &c); + +void push_nodebox (lua_State *L, + const NodeBox &box); +void push_box (lua_State *L, + const std::vector &box); + +void push_palette (lua_State *L, + const std::vector *palette); + TileDef read_tiledef (lua_State *L, int index, u8 drawtype); + void read_soundspec (lua_State *L, int index, SimpleSoundSpec &spec); NodeBox read_nodebox (lua_State *L, int index); @@ -90,6 +103,9 @@ void read_item_definition (lua_State *L, int index, const ItemDefinition &defaul ItemDefinition &def); void push_item_definition (lua_State *L, const ItemDefinition &i); +void push_item_definition_full (lua_State *L, + const ItemDefinition &i); + void read_object_properties (lua_State *L, int index, ObjectProperties *prop, IItemDefManager *idef); @@ -147,6 +163,8 @@ std::vector read_items (lua_State *L, void read_soundspec (lua_State *L, int index, SimpleSoundSpec &spec); +void push_soundspec (lua_State *L, + const SimpleSoundSpec &spec); bool string_to_enum (const EnumString *spec, int &result, diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index 22045e06d..87cfa00fe 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mainmenumanager.h" #include "map.h" #include "util/string.h" +#include "nodedef.h" extern MainGameCallback *g_gamecallback; @@ -260,6 +261,50 @@ int ModApiClient::l_get_server_info(lua_State *L) return 1; } +// get_item_def(itemstring) +int ModApiClient::l_get_item_def(lua_State *L) +{ + IGameDef *gdef = getGameDef(L); + assert(gdef); + + IItemDefManager *idef = gdef->idef(); + assert(idef); + + if (!lua_isstring(L, 1)) + return 0; + + const std::string &name(lua_tostring(L, 1)); + if (!idef->isKnown(name)) + return 0; + const ItemDefinition &def = idef->get(name); + + push_item_definition_full(L, def); + + return 1; +} + +// get_node_def(nodename) +int ModApiClient::l_get_node_def(lua_State *L) +{ + IGameDef *gdef = getGameDef(L); + assert(gdef); + + INodeDefManager *ndef = gdef->ndef(); + assert(ndef); + + if (!lua_isstring(L, 1)) + return 0; + + const std::string &name = lua_tostring(L, 1); + const ContentFeatures &cf = ndef->get(ndef->getId(name)); + if (cf.name != name) // Unknown node. | name = , cf.name = ignore + return 0; + + push_content_features(L, cf); + + return 1; +} + int ModApiClient::l_take_screenshot(lua_State *L) { Client *client = getClient(L); @@ -286,5 +331,7 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(sound_play); API_FCT(sound_stop); API_FCT(get_server_info); + API_FCT(get_item_def); + API_FCT(get_node_def); API_FCT(take_screenshot); } diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index 2267a4c9d..9813a5604 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define L_CLIENT_H_ #include "lua_api/l_base.h" +#include "itemdef.h" +#include "tool.h" class ModApiClient : public ModApiBase { @@ -38,7 +40,7 @@ private: // get_player_names() static int l_get_player_names(lua_State *L); - // show_formspec(name, fornspec) + // show_formspec(name, formspec) static int l_show_formspec(lua_State *L); // send_respawn() @@ -74,6 +76,12 @@ private: // get_server_info() static int l_get_server_info(lua_State *L); + + // get_item_def(itemstring) + static int l_get_item_def(lua_State *L); + + // get_node_def(nodename) + static int l_get_node_def(lua_State *L); static int l_take_screenshot(lua_State *L);