ContentFeatures read_content_features(lua_State *L, int index) { if(index < 0) index = lua_gettop(L) + 1 + index; ContentFeatures f; /* Cache existence of some callbacks */ lua_getfield(L, index, "on_construct"); if(!lua_isnil(L, -1)) f.has_on_construct = true; lua_pop(L, 1); lua_getfield(L, index, "on_destruct"); if(!lua_isnil(L, -1)) f.has_on_destruct = true; lua_pop(L, 1); lua_getfield(L, index, "after_destruct"); if(!lua_isnil(L, -1)) f.has_after_destruct = true; lua_pop(L, 1); lua_getfield(L, index, "on_rightclick"); f.rightclickable = lua_isfunction(L, -1); lua_pop(L, 1); /* Name */ getstringfield(L, index, "name", f.name); /* Groups */ lua_getfield(L, index, "groups"); read_groups(L, -1, f.groups); lua_pop(L, 1); /* Visual definition */ f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", ScriptApiNode::es_DrawType,NDT_NORMAL); getfloatfield(L, index, "visual_scale", f.visual_scale); /* Meshnode model filename */ getstringfield(L, index, "mesh", f.mesh); // tiles = {} lua_getfield(L, index, "tiles"); // If nil, try the deprecated name "tile_images" instead if(lua_isnil(L, -1)){ lua_pop(L, 1); warn_if_field_exists(L, index, "tile_images", "Deprecated; new name is \"tiles\"."); lua_getfield(L, index, "tile_images"); } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ // Read tiledef from value f.tiledef[i] = read_tiledef(L, -1, f.drawtype); // removes value, keeps key for next iteration lua_pop(L, 1); i++; if(i==6){ lua_pop(L, 1); break; } } // Copy last value to all remaining textures if(i >= 1){ TileDef lasttile = f.tiledef[i-1]; while(i < 6){ f.tiledef[i] = lasttile; i++; } } } lua_pop(L, 1); // special_tiles = {} lua_getfield(L, index, "special_tiles"); // If nil, try the deprecated name "special_materials" instead if(lua_isnil(L, -1)){ lua_pop(L, 1); warn_if_field_exists(L, index, "special_materials", "Deprecated; new name is \"special_tiles\"."); lua_getfield(L, index, "special_materials"); } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ // Read tiledef from value f.tiledef_special[i] = read_tiledef(L, -1, f.drawtype); // removes value, keeps key for next iteration lua_pop(L, 1); i++; if(i==CF_SPECIAL_COUNT){ lua_pop(L, 1); break; } } } lua_pop(L, 1); f.alpha = getintfield_default(L, index, "alpha", 255); bool usealpha = getboolfield_default(L, index, "use_texture_alpha", false); if (usealpha) f.alpha = 0; // Read node color. lua_getfield(L, index, "color"); read_color(L, -1, &f.color); lua_pop(L, 1); getstringfield(L, index, "palette", f.palette_name); /* Other stuff */ lua_getfield(L, index, "post_effect_color"); read_color(L, -1, &f.post_effect_color); lua_pop(L, 1); f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", ScriptApiNode::es_ContentParamType, CPT_NONE); f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", ScriptApiNode::es_ContentParamType2, CPT2_NONE); if (f.palette_name != "" && !(f.param_type_2 == CPT2_COLOR || f.param_type_2 == CPT2_COLORED_FACEDIR || f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) warningstream << "Node " << f.name.c_str() << " has a palette, but not a suitable paramtype2." << std::endl; // Warn about some deprecated fields warn_if_field_exists(L, index, "wall_mounted", "Deprecated; use paramtype2 = 'wallmounted'"); warn_if_field_exists(L, index, "light_propagates", "Deprecated; determined from paramtype"); warn_if_field_exists(L, index, "dug_item", "Deprecated; use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item", "Deprecated; use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item_rarity", "Deprecated; use 'drop' field"); warn_if_field_exists(L, index, "metadata_name", "Deprecated; use on_add and metadata callbacks"); // True for all ground-like things like stone and mud, false for eg. trees getboolfield(L, index, "is_ground_content", f.is_ground_content); f.light_propagates = (f.param_type == CPT_LIGHT); getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates); // This is used for collision detection. // Also for general solidness queries. getboolfield(L, index, "walkable", f.walkable); // Player can point to these getboolfield(L, index, "pointable", f.pointable); // Player can dig these getboolfield(L, index, "diggable", f.diggable); // Player can climb these getboolfield(L, index, "climbable", f.climbable); // Player can build on these getboolfield(L, index, "buildable_to", f.buildable_to); // Liquids flow into and replace node getboolfield(L, index, "floodable", f.floodable); // Whether the node is non-liquid, source liquid or flowing liquid f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", ScriptApiNode::es_LiquidType, LIQUID_NONE); // If the content is liquid, this is the flowing version of the liquid. getstringfield(L, index, "liquid_alternative_flowing", f.liquid_alternative_flowing); // If the content is liquid, this is the source version of the liquid. getstringfield(L, index, "liquid_alternative_source", f.liquid_alternative_source); // Viscosity for fluid flow, ranging from 1 to 7, with // 1 giving almost instantaneous propagation and 7 being // the slowest possible f.liquid_viscosity = getintfield_default(L, index, "liquid_viscosity", f.liquid_viscosity); f.liquid_range = getintfield_default(L, index, "liquid_range", f.liquid_range); f.leveled = getintfield_default(L, index, "leveled", f.leveled); getboolfield(L, index, "liquid_renewable", f.liquid_renewable); f.drowning = getintfield_default(L, index, "drowning", f.drowning); // Amount of light the node emits f.light_source = getintfield_default(L, index, "light_source", f.light_source); if (f.light_source > LIGHT_MAX) { warningstream << "Node " << f.name.c_str() << " had greater light_source than " << LIGHT_MAX << ", it was reduced." << std::endl; f.light_source = LIGHT_MAX; } f.damage_per_second = getintfield_default(L, index, "damage_per_second", f.damage_per_second); lua_getfield(L, index, "node_box"); if(lua_istable(L, -1)) f.node_box = read_nodebox(L, -1); lua_pop(L, 1); lua_getfield(L, index, "connects_to"); if (lua_istable(L, -1)) { int table = lua_gettop(L); lua_pushnil(L); while (lua_next(L, table) != 0) { // Value at -1 f.connects_to.push_back(lua_tostring(L, -1)); lua_pop(L, 1); } } lua_pop(L, 1); lua_getfield(L, index, "connect_sides"); if (lua_istable(L, -1)) { int table = lua_gettop(L); lua_pushnil(L); while (lua_next(L, table) != 0) { // Value at -1 std::string side(lua_tostring(L, -1)); // Note faces are flipped to make checking easier if (side == "top") f.connect_sides |= 2; else if (side == "bottom") f.connect_sides |= 1; else if (side == "front") f.connect_sides |= 16; else if (side == "left") f.connect_sides |= 32; else if (side == "back") f.connect_sides |= 4; else if (side == "right") f.connect_sides |= 8; else warningstream << "Unknown value for \"connect_sides\": " << side << std::endl; lua_pop(L, 1); } } lua_pop(L, 1); lua_getfield(L, index, "selection_box"); if(lua_istable(L, -1)) f.selection_box = read_nodebox(L, -1); lua_pop(L, 1); lua_getfield(L, index, "collision_box"); if(lua_istable(L, -1)) f.collision_box = read_nodebox(L, -1); lua_pop(L, 1); f.waving = getintfield_default(L, index, "waving", f.waving); // Set to true if paramtype used to be 'facedir_simple' getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple); // Set to true if wall_mounted used to be set to true getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted); // Sound table lua_getfield(L, index, "sounds"); if(lua_istable(L, -1)){ lua_getfield(L, -1, "footstep"); read_soundspec(L, -1, f.sound_footstep); lua_pop(L, 1); lua_getfield(L, -1, "dig"); read_soundspec(L, -1, f.sound_dig); lua_pop(L, 1); lua_getfield(L, -1, "dug"); read_soundspec(L, -1, f.sound_dug); lua_pop(L, 1); } lua_pop(L, 1); return f; }
ContentFeatures read_content_features(lua_State *L, int index) { if(index < 0) index = lua_gettop(L) + 1 + index; ContentFeatures f; /* Cache existence of some callbacks */ lua_getfield(L, index, "on_construct"); if(!lua_isnil(L, -1)) f.has_on_construct = true; lua_pop(L, 1); lua_getfield(L, index, "on_destruct"); if(!lua_isnil(L, -1)) f.has_on_destruct = true; lua_pop(L, 1); lua_getfield(L, index, "after_destruct"); if(!lua_isnil(L, -1)) f.has_after_destruct = true; lua_pop(L, 1); lua_getfield(L, index, "on_activate"); if(!lua_isnil(L, -1)) { f.has_on_activate = true; f.is_circuit_element = true; } lua_pop(L, 1); lua_getfield(L, index, "on_deactivate"); if(!lua_isnil(L, -1)) { f.has_on_deactivate = true; f.is_circuit_element = true; } lua_pop(L, 1); lua_getfield(L, index, "on_rightclick"); f.rightclickable = lua_isfunction(L, -1); lua_pop(L, 1); /* Name */ getstringfield(L, index, "name", f.name); /* Groups */ lua_getfield(L, index, "groups"); read_groups(L, -1, f.groups); lua_pop(L, 1); /* Visual definition */ f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", ScriptApiNode::es_DrawType,NDT_NORMAL); getfloatfield(L, index, "visual_scale", f.visual_scale); /* Meshnode model filename */ getstringfield(L, index, "mesh", f.mesh); // tiles = {} lua_getfield(L, index, "tiles"); // If nil, try the deprecated name "tile_images" instead if(lua_isnil(L, -1)){ lua_pop(L, 1); warn_if_field_exists(L, index, "tile_images", "Deprecated; new name is \"tiles\"."); lua_getfield(L, index, "tile_images"); } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ // Read tiledef from value f.tiledef[i] = read_tiledef(L, -1); // removes value, keeps key for next iteration lua_pop(L, 1); i++; if(i==6){ lua_pop(L, 1); break; } } // Copy last value to all remaining textures if(i >= 1){ TileDef lasttile = f.tiledef[i-1]; while(i < 6){ f.tiledef[i] = lasttile; i++; } } } lua_pop(L, 1); /* Circuit options */ lua_getfield(L, index, "is_wire"); if(!lua_isnil(L, -1)) { f.is_wire = true; } lua_pop(L, 1); lua_getfield(L, index, "is_wire_connector"); if(!lua_isnil(L, -1)) { f.is_wire_connector = true; } lua_pop(L, 1); lua_getfield(L, index, "wire_connections"); if(!lua_isnil(L, -1) && lua_istable(L, -1)) { // Both can't be set to true f.is_wire |= !f.is_wire_connector; int table = lua_gettop(L); lua_pushnil(L); int i; unsigned char current_shift = 1; for(i = 0; (i < 6) && (lua_next(L, table) != 0); ++i) { f.wire_connections[i] = lua_tonumber(L, -1); f.wire_connections[i] |= current_shift; current_shift <<= 1; lua_pop(L, 1); } if(i < 6) { luaL_error(L, "Wire connectins array must have exactly 6 integer numbers."); } // Convert to two-way wire (one-way may cause undefined behavior) for(i = 0; i < 6; ++i) { for(int j = 0; j < 6; ++j) { f.wire_connections[i] |= f.wire_connections[j] & (1 << i); f.wire_connections[j] |= f.wire_connections[i] & (1 << j); } } } else if(f.is_wire || f.is_wire_connector) { // Assuming that it's a standart wire or wire connector for(int i = 0; i < 6; ++i) { f.wire_connections[i] = 0x3F; } } lua_pop(L, 1); lua_getfield(L, index, "circuit_states"); if(!lua_isnil(L, -1) && lua_istable(L, -1)) { f.is_circuit_element = true; int table = lua_gettop(L); lua_pushnil(L); int i; for(i = 0; (i < 64) && (lua_next(L, table) != 0); ++i) { f.circuit_element_func[i] = lua_tonumber(L, -1); lua_pop(L, 1); } if(i < 64) { luaL_error(L, "Circuit element states table must have exactly 64 integer numbers."); } } lua_pop(L, 1); f.circuit_element_delay = getintfield_default(L, index, "circuit_element_delay", f.circuit_element_delay + 1) - 1; if(f.circuit_element_delay > 100) { luaL_error(L, "\"circuit_element_delay\" must be a positive integer number less than 101"); } // special_tiles = {} lua_getfield(L, index, "special_tiles"); // If nil, try the deprecated name "special_materials" instead if(lua_isnil(L, -1)){ lua_pop(L, 1); warn_if_field_exists(L, index, "special_materials", "Deprecated; new name is \"special_tiles\"."); lua_getfield(L, index, "special_materials"); } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ // Read tiledef from value f.tiledef_special[i] = read_tiledef(L, -1); // removes value, keeps key for next iteration lua_pop(L, 1); i++; if(i==CF_SPECIAL_COUNT){ lua_pop(L, 1); break; } } } lua_pop(L, 1); f.alpha = getintfield_default(L, index, "alpha", 255); bool usealpha = getboolfield_default(L, index, "use_texture_alpha", false); if (usealpha) f.alpha = 0; /* Other stuff */ lua_getfield(L, index, "post_effect_color"); if(!lua_isnil(L, -1)) f.post_effect_color = readARGB8(L, -1); lua_pop(L, 1); f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", ScriptApiNode::es_ContentParamType, CPT_NONE); f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", ScriptApiNode::es_ContentParamType2, CPT2_NONE); // Warn about some deprecated fields warn_if_field_exists(L, index, "wall_mounted", "deprecated: use paramtype2 = 'wallmounted'"); warn_if_field_exists(L, index, "light_propagates", "deprecated: determined from paramtype"); warn_if_field_exists(L, index, "dug_item", "deprecated: use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item", "deprecated: use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item_rarity", "deprecated: use 'drop' field"); warn_if_field_exists(L, index, "metadata_name", "deprecated: use on_add and metadata callbacks"); // True for all ground-like things like stone and mud, false for eg. trees getboolfield(L, index, "is_ground_content", f.is_ground_content); f.light_propagates = (f.param_type == CPT_LIGHT); getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates); // This is used for collision detection. // Also for general solidness queries. getboolfield(L, index, "walkable", f.walkable); // Player can point to these getboolfield(L, index, "pointable", f.pointable); // Player can dig these getboolfield(L, index, "diggable", f.diggable); // Player can climb these getboolfield(L, index, "climbable", f.climbable); // Player can build on these getboolfield(L, index, "buildable_to", f.buildable_to); // Whether the node is non-liquid, source liquid or flowing liquid f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", ScriptApiNode::es_LiquidType, LIQUID_NONE); // If the content is liquid, this is the flowing version of the liquid. getstringfield(L, index, "liquid_alternative_flowing", f.liquid_alternative_flowing); // If the content is liquid, this is the source version of the liquid. getstringfield(L, index, "liquid_alternative_source", f.liquid_alternative_source); // Viscosity for fluid flow, ranging from 1 to 7, with // 1 giving almost instantaneous propagation and 7 being // the slowest possible f.liquid_viscosity = getintfield_default(L, index, "liquid_viscosity", f.liquid_viscosity); f.leveled = getintfield_default(L, index, "leveled", f.leveled); getboolfield(L, index, "liquid_renewable", f.liquid_renewable); getstringfield(L, index, "freeze", f.freeze); getstringfield(L, index, "melt", f.melt); f.drowning = getintfield_default(L, index, "drowning", f.drowning); // Amount of light the node emits f.light_source = getintfield_default(L, index, "light_source", f.light_source); f.damage_per_second = getintfield_default(L, index, "damage_per_second", f.damage_per_second); lua_getfield(L, index, "node_box"); if(lua_istable(L, -1)) f.node_box = read_nodebox(L, -1); lua_pop(L, 1); lua_getfield(L, index, "selection_box"); if(lua_istable(L, -1)) f.selection_box = read_nodebox(L, -1); lua_pop(L, 1); lua_getfield(L, index, "collision_box"); if(lua_istable(L, -1)) f.collision_box = read_nodebox(L, -1); lua_pop(L, 1); f.waving = getintfield_default(L, index, "waving", f.waving); // Set to true if paramtype used to be 'facedir_simple' getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple); // Set to true if wall_mounted used to be set to true getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted); // Sound table lua_getfield(L, index, "sounds"); if(lua_istable(L, -1)){ lua_getfield(L, -1, "footstep"); read_soundspec(L, -1, f.sound_footstep); lua_pop(L, 1); lua_getfield(L, -1, "dig"); read_soundspec(L, -1, f.sound_dig); lua_pop(L, 1); lua_getfield(L, -1, "dug"); read_soundspec(L, -1, f.sound_dug); lua_pop(L, 1); } lua_pop(L, 1); return f; }
ContentFeatures read_content_features(lua_State *L, int index) { if(index < 0) index = lua_gettop(L) + 1 + index; ContentFeatures f; /* Cache existence of some callbacks */ lua_getfield(L, index, "on_construct"); if(!lua_isnil(L, -1)) f.has_on_construct = true; lua_pop(L, 1); lua_getfield(L, index, "on_destruct"); if(!lua_isnil(L, -1)) f.has_on_destruct = true; lua_pop(L, 1); lua_getfield(L, index, "after_destruct"); if(!lua_isnil(L, -1)) f.has_after_destruct = true; lua_pop(L, 1); lua_getfield(L, index, "on_rightclick"); f.rightclickable = lua_isfunction(L, -1); lua_pop(L, 1); /* Name */ getstringfield(L, index, "name", f.name); /* Groups */ lua_getfield(L, index, "groups"); read_groups(L, -1, f.groups); lua_pop(L, 1); /* Visual definition */ f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", ScriptApiNode::es_DrawType,NDT_NORMAL); getfloatfield(L, index, "visual_scale", f.visual_scale); // tiles = {} lua_getfield(L, index, "tiles"); // If nil, try the deprecated name "tile_images" instead if(lua_isnil(L, -1)){ lua_pop(L, 1); warn_if_field_exists(L, index, "tile_images", "Deprecated; new name is \"tiles\"."); lua_getfield(L, index, "tile_images"); } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ // Read tiledef from value f.tiledef[i] = read_tiledef(L, -1); // removes value, keeps key for next iteration lua_pop(L, 1); i++; if(i==6){ lua_pop(L, 1); break; } } // Copy last value to all remaining textures if(i >= 1){ TileDef lasttile = f.tiledef[i-1]; while(i < 6){ f.tiledef[i] = lasttile; i++; } } } lua_pop(L, 1); // special_tiles = {} lua_getfield(L, index, "special_tiles"); // If nil, try the deprecated name "special_materials" instead if(lua_isnil(L, -1)){ lua_pop(L, 1); warn_if_field_exists(L, index, "special_materials", "Deprecated; new name is \"special_tiles\"."); lua_getfield(L, index, "special_materials"); } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ // Read tiledef from value f.tiledef_special[i] = read_tiledef(L, -1); // removes value, keeps key for next iteration lua_pop(L, 1); i++; if(i==6){ lua_pop(L, 1); break; } } } lua_pop(L, 1); f.alpha = getintfield_default(L, index, "alpha", 255); bool usealpha = getboolfield_default(L, index, "use_texture_alpha", false); if (usealpha) f.alpha = 0; /* Other stuff */ lua_getfield(L, index, "post_effect_color"); if(!lua_isnil(L, -1)) f.post_effect_color = readARGB8(L, -1); lua_pop(L, 1); f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", ScriptApiNode::es_ContentParamType, CPT_NONE); f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", ScriptApiNode::es_ContentParamType2, CPT2_NONE); // Warn about some deprecated fields warn_if_field_exists(L, index, "wall_mounted", "deprecated: use paramtype2 = 'wallmounted'"); warn_if_field_exists(L, index, "light_propagates", "deprecated: determined from paramtype"); warn_if_field_exists(L, index, "dug_item", "deprecated: use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item", "deprecated: use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item_rarity", "deprecated: use 'drop' field"); warn_if_field_exists(L, index, "metadata_name", "deprecated: use on_add and metadata callbacks"); // True for all ground-like things like stone and mud, false for eg. trees getboolfield(L, index, "is_ground_content", f.is_ground_content); f.light_propagates = (f.param_type == CPT_LIGHT); getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates); // This is used for collision detection. // Also for general solidness queries. getboolfield(L, index, "walkable", f.walkable); // Player can point to these getboolfield(L, index, "pointable", f.pointable); // Player can dig these getboolfield(L, index, "diggable", f.diggable); // Player can climb these getboolfield(L, index, "climbable", f.climbable); // Player can build on these getboolfield(L, index, "buildable_to", f.buildable_to); // Whether the node is non-liquid, source liquid or flowing liquid f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", ScriptApiNode::es_LiquidType, LIQUID_NONE); // If the content is liquid, this is the flowing version of the liquid. getstringfield(L, index, "liquid_alternative_flowing", f.liquid_alternative_flowing); // If the content is liquid, this is the source version of the liquid. getstringfield(L, index, "liquid_alternative_source", f.liquid_alternative_source); // Viscosity for fluid flow, ranging from 1 to 7, with // 1 giving almost instantaneous propagation and 7 being // the slowest possible f.liquid_viscosity = getintfield_default(L, index, "liquid_viscosity", f.liquid_viscosity); f.liquid_range = getintfield_default(L, index, "liquid_range", f.liquid_range); f.leveled = getintfield_default(L, index, "leveled", f.leveled); getboolfield(L, index, "liquid_renewable", f.liquid_renewable); getstringfield(L, index, "freezemelt", f.freezemelt); f.drowning = getintfield_default(L, index, "drowning", f.drowning); // Amount of light the node emits f.light_source = getintfield_default(L, index, "light_source", f.light_source); f.damage_per_second = getintfield_default(L, index, "damage_per_second", f.damage_per_second); lua_getfield(L, index, "node_box"); if(lua_istable(L, -1)) f.node_box = read_nodebox(L, -1); lua_pop(L, 1); lua_getfield(L, index, "selection_box"); if(lua_istable(L, -1)) f.selection_box = read_nodebox(L, -1); lua_pop(L, 1); // Set to true if paramtype used to be 'facedir_simple' getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple); // Set to true if wall_mounted used to be set to true getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted); // Sound table lua_getfield(L, index, "sounds"); if(lua_istable(L, -1)){ lua_getfield(L, -1, "footstep"); read_soundspec(L, -1, f.sound_footstep); lua_pop(L, 1); lua_getfield(L, -1, "dig"); read_soundspec(L, -1, f.sound_dig); lua_pop(L, 1); lua_getfield(L, -1, "dug"); read_soundspec(L, -1, f.sound_dug); lua_pop(L, 1); } lua_pop(L, 1); return f; }