/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "common/c_content.h"
#include "common/c_converter.h"
#include "common/c_types.h"
#include "nodedef.h"
#include "object_properties.h"
#include "content_sao.h"
#include "cpp_api/s_node.h"
#include "lua_api/l_object.h"
#include "lua_api/l_item.h"
#include "common/c_internal.h"
#include "server.h"
#include "log.h"
#include "tool.h"
#include "serverobject.h"
#include "porting.h"
#include "mapgen/mg_schematic.h"
#include "noise.h"
#include "util/pointedthing.h"
#include "debug.h" // For FATAL_ERROR
#include <json/json.h>
struct EnumString es_TileAnimationType[] =
{
{TAT_NONE, "none"},
{TAT_VERTICAL_FRAMES, "vertical_frames"},
{TAT_SHEET_2D, "sheet_2d"},
{0, NULL},
};
/******************************************************************************/
void read_item_definition(lua_State* L, int index,
const ItemDefinition &default_def, ItemDefinition &def)
{
if (index < 0)
index = lua_gettop(L) + 1 + index;
def.type = (ItemType)getenumfield(L, index, "type",
es_ItemType, ITEM_NONE);
getstringfield(L, index, "name", def.name);
getstringfield(L, index, "description", def.description);
getstringfield(L, index, "inventory_image", def.inventory_image);
getstringfield(L, index, "inventory_overlay", def.inventory_overlay);
getstringfield(L, index, "wield_image", def.wield_image);
getstringfield(L, index, "wield_overlay", def.wield_overlay);
getstringfield(L, index, "palette", def.palette_image);
// Read item color.
lua_getfield(L, index, "color");
read_color(L, -1, &def.color);
lua_pop(L, 1);
lua_getfield(L, index, "wield_scale");
if(lua_istable(L, -1)){
def.wield_scale = check_v3f(L, -1);
}
lua_pop(L, 1);
int stack_max = getintfield_default(L, index, "stack_max", def.stack_max);
def.stack_max = rangelim(stack_max, 1, U16_MAX);
lua_getfield(L, index, "on_use");
def.usable = lua_isfunction(L, -1);
lua_pop(L, 1);
getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
warn_if_field_exists(L, index, "tool_digging_properties",
"Obsolete; use tool_capabilities");
lua_getfield(L, index, "tool_capabilities");
if(lua_istable(L, -1)){
def.tool_capabilities = new ToolCapabilities(
read_tool_capabilities(L, -1));
}
// If name is "" (hand), ensure there are ToolCapabilities
// because it will be looked up there whenever any other item has
// no ToolCapabilities
if (def.name.empty() && def.tool_capabilities == NULL){
def.tool_capabilities = new ToolCapabilities();
}
lua_getfield(L, index, "groups");
read_groups(L, -1, def.groups);
lua_pop(L, 1);
lua_getfield(L, index, "sounds");
if(lua_istable(L, -1)){
lua_getfield(L, -1, "place");
read_soundspec(L, -1, def.sound_place);
lua_pop(L, 1);
lua_getfield(L, -1, "place_failed");
read_soundspec(L, -1, def.sound_place_failed);
lua_pop(L, 1);
}
lua_pop(L, 1);
def.range = getfloatfield_default(L, index, "range", def.range);
// Client shall immediately place this node when player places the item.
// Server will update the precise end result a moment later.
// "" = no prediction
getstringfield(L, index, "node_placement_prediction",
def.node_placement_prediction);
}
/******************************************************************************/
void push_item_definition(lua_State *L, const ItemDefinition &i)
{
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");
}
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.inventory_overlay.c_str());
lua_setfield(L, -2, "inventory_overlay");
lua_pushstring(L, i.wield_image.c_str());
lua_setfield(L, -2, "wield_image");
lua_pushstring(L, i.wield_overlay.c_str());
lua_setfield(L, -2, "wield_overlay");
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, *i.tool_capabilities);
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,
ServerActiveObject *sao, ObjectProperties *prop, IItemDefManager *idef)
{
if(index < 0)
index = lua_gettop(L) + 1 + index;
if(!lua_istable(L, index))
return;
int hp_max = 0;
if (getintfield(L, -1, "hp_max", hp_max)) {
prop->hp_max = (u16)rangelim(hp_max, 0, U16_MAX);
if (prop->hp_max < sao->getHP()) {
PlayerHPChangeReason reason(PlayerHPChangeReason::SET_HP);
sao->setHP(prop->hp_max, reason);
if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER)
sao->getEnv()->getGameDef()->SendPlayerHPOrDie((PlayerSAO *)sao, reason);
}
}
if (getintfield(L, -1, "breath_max", prop->breath_max)) {
if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
PlayerSAO *player = (PlayerSAO *)sao;
if (prop->breath_max < player->getBreath())
player->setBreath(prop->breath_max);
}
}
getboolfield(L, -1, "physical", prop->physical);
getboolfield(L, -1, "collide_with_objects", prop->collideWithObjects);
getfloatfield(L, -1, "weight", prop->weight);
lua_getfield(L, -1, "collisionbox");
bool collisionbox_defined = lua_istable(L, -1);
if (collisionbox_defined)
prop->collisionbox = read_aabb3f(L, -1, 1.0);
lua_pop(L, 1);
lua_getfield(L, -1, "selectionbox");
if (lua_istable(L, -1))
prop->selectionbox = read_aabb3f(L, -1, 1.0);
else if (collisionbox_defined)
prop->selectionbox = prop->collisionbox;
lua_pop(L, 1);
getboolfield(L, -1, "pointable", prop->pointable);
getstringfield(L, -1, "visual", prop->visual);
getstringfield(L, -1, "mesh", prop->mesh);
lua_getfield(L, -1, "visual_size");
if (lua_istable(L, -1)) {
// Backwards compatibility: Also accept { x = ?, y = ? }
v2f scale_xy = read_v2f(L, -1);
f32 scale_z = scale_xy.X;
lua_getfield(L, -1, "z");
if (lua_isnumber(L, -1))
scale_z = lua_tonumber(L, -1);
lua_pop(L, 1);
prop->visual_size = v3f(scale_xy.X, scale_xy.Y, scale_z);
}
lua_pop(L, 1);
lua_getfield(L, -1, "textures");
if(lua_istable(L, -1)){
prop->textures.clear();
int table = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L, table) != 0){
// key at index -2 and value at index -1
if(lua_isstring(L, -1))
prop->textures.emplace_back(lua_tostring(L, -1));
else
prop->textures.emplace_back("");
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
}
lua_pop(L, 1);
lua_getfield(L, -1, "colors");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
prop->colors.clear();
for (lua_pushnil(L); lua_next(L, table); lua_pop(L, 1)) {
video::SColor color(255, 255, 255, 255);
read_color(L, -1, &color);
prop->colors.push_back(color);
}
}
lua_pop(L, 1);
lua_getfield(L, -1, "spritediv");
if(lua_istable(L, -1))
prop->spritediv = read_v2s16(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "initial_sprite_basepos");
if(lua_istable(L, -1))
prop->initial_sprite_basepos = read_v2s16(L, -1);
lua_pop(L, 1);
getboolfield(L, -1, "is_visible", prop->is_visible);
getboolfield(L, -1, "makes_footstep_sound", prop->makes_footstep_sound);
if (getfloatfield(L, -1, "stepheight", prop->stepheight))
|