942 lines
35 KiB
Lua
942 lines
35 KiB
Lua
-- Boilerplate to support localized strings if intllib mod is installed.
|
|
local S = function(s) return s end
|
|
if minetest.get_modpath("intllib") then
|
|
S = intllib.Getter()
|
|
end
|
|
|
|
local function mod_loaded(modname)
|
|
if minetest.get_modpath(modname) then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local has_default = mod_loaded("default")
|
|
local has_mcl = mod_loaded("mcl_core") and
|
|
mod_loaded("mcl_colorblocks") and
|
|
mod_loaded("mcl_formspec") and
|
|
mod_loaded("mcl_player")
|
|
|
|
if not (has_default or has_mcl) then
|
|
error(
|
|
"\n\n" ..
|
|
"mcl_meshnode requires a supported base game to run.\n" ..
|
|
"The following games should satisfy the requirement:\n" ..
|
|
"• Minetest Game\n" ..
|
|
"• MineClone2\n" ..
|
|
"• MineClone5\n" ..
|
|
"• Mineclonia\n"
|
|
)
|
|
end
|
|
|
|
local player_mod = default
|
|
if has_mcl then
|
|
player_mod = mcl_player
|
|
end
|
|
|
|
dofile(minetest.get_modpath(minetest.get_current_modname()).."/api.lua")
|
|
|
|
local ctrl_groups = {choppy=2, oddly_breakable_by_hand=2}
|
|
if has_mcl then
|
|
ctrl_groups = {axey=2, handy=1}
|
|
end
|
|
local has_worldedit = minetest.global_exists("worldedit")
|
|
local is_singleplayer = minetest.is_singleplayer()
|
|
local control_textures = {
|
|
"default_steel_block.png",
|
|
"default_diamond_block.png",
|
|
"default_bronze_block.png",
|
|
"default_obsidian_block.png",
|
|
"default_gold_block.png",
|
|
}
|
|
if has_mcl then
|
|
control_textures = {
|
|
"default_steel_block.png",
|
|
"default_diamond_block.png",
|
|
"mcl_colorblocks_concrete_orange.png",
|
|
"default_obsidian.png",
|
|
"default_gold_block.png",
|
|
}
|
|
end
|
|
|
|
if is_singleplayer then
|
|
meshnode.config.max_radius = 16
|
|
meshnode.config.show_in_creative = true
|
|
meshnode.config.enable_crafting = true
|
|
meshnode.config.disable_privilege = true
|
|
meshnode.config.fake_shading = true
|
|
meshnode.config.autoconf = true
|
|
else
|
|
meshnode.blacklist["default:chest_locked"] = true
|
|
meshnode.blacklist["default:water_source"] = true
|
|
meshnode.blacklist["default:river_water_source"] = true
|
|
meshnode.blacklist["default:lava_source"] = true
|
|
if has_mcl then
|
|
meshnode.blacklist = {}
|
|
-- Nodes similar to default blacklist
|
|
meshnode.blacklist["mcl_core:water_source"] = true
|
|
meshnode.blacklist["mclx_core:river_water_source"] = true
|
|
meshnode.blacklist["mcl_core:lava_source"] = true
|
|
meshnode.blacklist["mcl_nether:nether_lava_source"] = true
|
|
-- Unmovable by design (taken from mesecons)
|
|
meshnode.blacklist["mcl_core:barrier"] = true
|
|
meshnode.blacklist["mcl_core:realm_barrier"] = true
|
|
meshnode.blacklist["mcl_core:void"] = true
|
|
meshnode.blacklist["mcl_core:bedrock"] = true
|
|
meshnode.blacklist["mcl_core:obsidian"] = true
|
|
meshnode.blacklist["mcl_chests:ender_chest"] = true
|
|
meshnode.blacklist["mcl_mobspawners:spawner"] = true
|
|
meshnode.blacklist["mesecons_commandblock:commandblock_off"] = true
|
|
meshnode.blacklist["mesecons_commandblock:commandblock_on"] = true
|
|
meshnode.blacklist["mcl_portals:portal"] = true
|
|
meshnode.blacklist["mcl_portals:portal_end"] = true
|
|
meshnode.blacklist["mcl_portals:end_portal_frame"] = true
|
|
meshnode.blacklist["mcl_portals:end_portal_frame_eye"] = true
|
|
-- Half-piston nodes (taken from mesecons)
|
|
meshnode.blacklist["mesecons_pistons:piston_pusher_normal"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_pusher_sticky"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_up_pusher_normal"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_up_pusher_sticky"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_down_pusher_normal"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_down_pusher_sticky"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_normal_on"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_sticky_on"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_up_normal_on"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_up_sticky_on"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_down_normal_on"] = true
|
|
meshnode.blacklist["mesecons_pistons:piston_down_sticky_on"] = true
|
|
-- Banners (are implemented as node + entity)
|
|
meshnode.blacklist["mcl_banners:hanging_banner"] = true
|
|
meshnode.blacklist["mcl_banners:standing_banner"] = true
|
|
-- Beds (should not oe be cut in half)
|
|
meshnode.blacklist["mcl_beds:bed_black_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_black_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_blue_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_blue_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_brown_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_brown_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_cyan_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_cyan_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_green_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_green_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_grey_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_grey_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_light_blue_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_light_blue_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_lime_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_lime_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_magenta_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_magenta_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_orange_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_orange_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_pink_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_pink_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_purple_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_purple_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_red_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_red_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_silver_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_silver_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_white_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_white_top"] = true
|
|
meshnode.blacklist["mcl_beds:bed_yellow_bottom"] = true
|
|
meshnode.blacklist["mcl_beds:bed_yellow_top"] = true
|
|
-- FIXME: Chests turn invisible in meshnode entity and
|
|
-- stay invisible when restored; double chests seem to
|
|
-- turn into a single chest and a half-chest. The bugs
|
|
-- are mostly due to chest entities not being spawned,
|
|
-- the chest inventory seems to be restored properly …
|
|
--
|
|
-- TODO: Take chests off the blacklist once they work.
|
|
meshnode.blacklist["mcl_chests:black_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:black_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:blue_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:blue_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:brown_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:brown_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:chest"] = true
|
|
meshnode.blacklist["mcl_chests:chest_left"] = true
|
|
meshnode.blacklist["mcl_chests:chest_right"] = true
|
|
meshnode.blacklist["mcl_chests:chest_small"] = true
|
|
meshnode.blacklist["mcl_chests:cyan_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:cyan_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:dark_green_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:dark_green_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:dark_grey_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:dark_grey_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:ender_chest"] = true
|
|
meshnode.blacklist["mcl_chests:ender_chest_small"] = true
|
|
meshnode.blacklist["mcl_chests:green_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:green_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:grey_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:grey_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:lightblue_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:lightblue_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:magenta_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:magenta_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:orange_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:orange_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:pink_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:pink_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:red_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:red_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:trapped_chest"] = true
|
|
meshnode.blacklist["mcl_chests:trapped_chest_left"] = true
|
|
meshnode.blacklist["mcl_chests:trapped_chest_on"] = true
|
|
meshnode.blacklist["mcl_chests:trapped_chest_on_left"] = true
|
|
meshnode.blacklist["mcl_chests:trapped_chest_on_right"] = true
|
|
meshnode.blacklist["mcl_chests:trapped_chest_on_small"] = true
|
|
meshnode.blacklist["mcl_chests:trapped_chest_right"] = true
|
|
meshnode.blacklist["mcl_chests:trapped_chest_small"] = true
|
|
meshnode.blacklist["mcl_chests:violet_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:violet_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:white_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:white_shulker_box_small"] = true
|
|
meshnode.blacklist["mcl_chests:yellow_shulker_box"] = true
|
|
meshnode.blacklist["mcl_chests:yellow_shulker_box_small"] = true
|
|
-- Doors
|
|
meshnode.blacklist["mcl_doors:acacia_door_b_2"] = true
|
|
meshnode.blacklist["mcl_doors:acacia_door_t_1"] = true
|
|
meshnode.blacklist["mcl_doors:acacia_door_t_2"] = true
|
|
meshnode.blacklist["mcl_doors:acacia_trapdoor"] = true
|
|
meshnode.blacklist["mcl_doors:acacia_trapdoor_open"] = true
|
|
meshnode.blacklist["mcl_doors:birch_door_b_1"] = true
|
|
meshnode.blacklist["mcl_doors:birch_door_b_2"] = true
|
|
meshnode.blacklist["mcl_doors:birch_door_t_1"] = true
|
|
meshnode.blacklist["mcl_doors:birch_door_t_2"] = true
|
|
meshnode.blacklist["mcl_doors:birch_trapdoor"] = true
|
|
meshnode.blacklist["mcl_doors:birch_trapdoor_open"] = true
|
|
meshnode.blacklist["mcl_doors:dark_oak_door_b_1"] = true
|
|
meshnode.blacklist["mcl_doors:dark_oak_door_b_2"] = true
|
|
meshnode.blacklist["mcl_doors:dark_oak_door_t_1"] = true
|
|
meshnode.blacklist["mcl_doors:dark_oak_door_t_2"] = true
|
|
meshnode.blacklist["mcl_doors:dark_oak_trapdoor"] = true
|
|
meshnode.blacklist["mcl_doors:dark_oak_trapdoor_open"] = true
|
|
meshnode.blacklist["mcl_doors:iron_door_b_1"] = true
|
|
meshnode.blacklist["mcl_doors:iron_door_b_2"] = true
|
|
meshnode.blacklist["mcl_doors:iron_door_t_1"] = true
|
|
meshnode.blacklist["mcl_doors:iron_door_t_2"] = true
|
|
meshnode.blacklist["mcl_doors:iron_trapdoor"] = true
|
|
meshnode.blacklist["mcl_doors:iron_trapdoor_open"] = true
|
|
meshnode.blacklist["mcl_doors:jungle_door_b_1"] = true
|
|
meshnode.blacklist["mcl_doors:jungle_door_b_2"] = true
|
|
meshnode.blacklist["mcl_doors:jungle_door_t_1"] = true
|
|
meshnode.blacklist["mcl_doors:jungle_door_t_2"] = true
|
|
meshnode.blacklist["mcl_doors:jungle_trapdoor"] = true
|
|
meshnode.blacklist["mcl_doors:jungle_trapdoor_open"] = true
|
|
meshnode.blacklist["mcl_doors:spruce_door_b_1"] = true
|
|
meshnode.blacklist["mcl_doors:spruce_door_b_2"] = true
|
|
meshnode.blacklist["mcl_doors:spruce_door_t_1"] = true
|
|
meshnode.blacklist["mcl_doors:spruce_door_t_2"] = true
|
|
meshnode.blacklist["mcl_doors:spruce_trapdoor"] = true
|
|
meshnode.blacklist["mcl_doors:spruce_trapdoor_open"] = true
|
|
meshnode.blacklist["mcl_doors:trapdoor"] = true
|
|
meshnode.blacklist["mcl_doors:trapdoor_open"] = true
|
|
meshnode.blacklist["mcl_doors:wooden_door_b_1"] = true
|
|
meshnode.blacklist["mcl_doors:wooden_door_b_2"] = true
|
|
meshnode.blacklist["mcl_doors:wooden_door_t_1"] = true
|
|
meshnode.blacklist["mcl_doors:wooden_door_t_2"] = true
|
|
-- Flowerpots (grow to gigantic size)
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_acaciasapling"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_allium"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_azure_bluet"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_birchsapling"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_blue_orchid"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_cactus"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_dandelion"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_darksapling"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_deadbush"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_fern"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_junglesapling"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_mushroom_brown"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_mushroom_red"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_oxeye_daisy"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_poppy"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_sapling"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_sprucesapling"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_tulip_orange"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_tulip_pink"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_tulip_red"] = true
|
|
meshnode.blacklist["mcl_flowerpots:flower_pot_tulip_white"] = true
|
|
-- Double plant nodes
|
|
meshnode.blacklist["mcl_flowers:double_fern"] = true
|
|
meshnode.blacklist["mcl_flowers:double_fern_top"] = true
|
|
meshnode.blacklist["mcl_flowers:double_grass"] = true
|
|
meshnode.blacklist["mcl_flowers:double_grass_top"] = true
|
|
meshnode.blacklist["mcl_flowers:lilac"] = true
|
|
meshnode.blacklist["mcl_flowers:lilac_top"] = true
|
|
meshnode.blacklist["mcl_flowers:peony"] = true
|
|
meshnode.blacklist["mcl_flowers:peony_top"] = true
|
|
meshnode.blacklist["mcl_flowers:rose_bush"] = true
|
|
meshnode.blacklist["mcl_flowers:rose_bush_top"] = true
|
|
meshnode.blacklist["mcl_flowers:sunflower"] = true
|
|
meshnode.blacklist["mcl_flowers:sunflower_top"] = true
|
|
-- Item frame (lose content entity)
|
|
meshnode.blacklist["mcl_itemframes:item_frame"] = true
|
|
-- Signs (lose text entity)
|
|
meshnode.blacklist["mcl_signs:standing_sign"] = true
|
|
meshnode.blacklist["mcl_signs:standing_sign22_5"] = true
|
|
meshnode.blacklist["mcl_signs:standing_sign45"] = true
|
|
meshnode.blacklist["mcl_signs:standing_sign67_5"] = true
|
|
meshnode.blacklist["mcl_signs:wall_sign"] = true
|
|
-- Walls (connect weirdly upon restore)
|
|
meshnode.blacklist["mcl_walls:andesite"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_0"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_1"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_10"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_11"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_12"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_13"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_14"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_15"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_16"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_2"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_21"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_3"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_4"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_5"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_6"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_7"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_8"] = true
|
|
meshnode.blacklist["mcl_walls:andesite_9"] = true
|
|
meshnode.blacklist["mcl_walls:brick"] = true
|
|
meshnode.blacklist["mcl_walls:brick_0"] = true
|
|
meshnode.blacklist["mcl_walls:brick_1"] = true
|
|
meshnode.blacklist["mcl_walls:brick_10"] = true
|
|
meshnode.blacklist["mcl_walls:brick_11"] = true
|
|
meshnode.blacklist["mcl_walls:brick_12"] = true
|
|
meshnode.blacklist["mcl_walls:brick_13"] = true
|
|
meshnode.blacklist["mcl_walls:brick_14"] = true
|
|
meshnode.blacklist["mcl_walls:brick_15"] = true
|
|
meshnode.blacklist["mcl_walls:brick_16"] = true
|
|
meshnode.blacklist["mcl_walls:brick_2"] = true
|
|
meshnode.blacklist["mcl_walls:brick_21"] = true
|
|
meshnode.blacklist["mcl_walls:brick_3"] = true
|
|
meshnode.blacklist["mcl_walls:brick_4"] = true
|
|
meshnode.blacklist["mcl_walls:brick_5"] = true
|
|
meshnode.blacklist["mcl_walls:brick_6"] = true
|
|
meshnode.blacklist["mcl_walls:brick_7"] = true
|
|
meshnode.blacklist["mcl_walls:brick_8"] = true
|
|
meshnode.blacklist["mcl_walls:brick_9"] = true
|
|
meshnode.blacklist["mcl_walls:cobble"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_0"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_1"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_10"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_11"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_12"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_13"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_14"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_15"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_16"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_2"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_21"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_3"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_4"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_5"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_6"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_7"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_8"] = true
|
|
meshnode.blacklist["mcl_walls:cobble_9"] = true
|
|
meshnode.blacklist["mcl_walls:diorite"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_0"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_1"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_10"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_11"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_12"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_13"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_14"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_15"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_16"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_2"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_21"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_3"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_4"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_5"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_6"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_7"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_8"] = true
|
|
meshnode.blacklist["mcl_walls:diorite_9"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_0"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_1"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_10"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_11"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_12"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_13"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_14"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_15"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_16"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_2"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_21"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_3"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_4"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_5"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_6"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_7"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_8"] = true
|
|
meshnode.blacklist["mcl_walls:endbricks_9"] = true
|
|
meshnode.blacklist["mcl_walls:granite"] = true
|
|
meshnode.blacklist["mcl_walls:granite_0"] = true
|
|
meshnode.blacklist["mcl_walls:granite_1"] = true
|
|
meshnode.blacklist["mcl_walls:granite_10"] = true
|
|
meshnode.blacklist["mcl_walls:granite_11"] = true
|
|
meshnode.blacklist["mcl_walls:granite_12"] = true
|
|
meshnode.blacklist["mcl_walls:granite_13"] = true
|
|
meshnode.blacklist["mcl_walls:granite_14"] = true
|
|
meshnode.blacklist["mcl_walls:granite_15"] = true
|
|
meshnode.blacklist["mcl_walls:granite_16"] = true
|
|
meshnode.blacklist["mcl_walls:granite_2"] = true
|
|
meshnode.blacklist["mcl_walls:granite_21"] = true
|
|
meshnode.blacklist["mcl_walls:granite_3"] = true
|
|
meshnode.blacklist["mcl_walls:granite_4"] = true
|
|
meshnode.blacklist["mcl_walls:granite_5"] = true
|
|
meshnode.blacklist["mcl_walls:granite_6"] = true
|
|
meshnode.blacklist["mcl_walls:granite_7"] = true
|
|
meshnode.blacklist["mcl_walls:granite_8"] = true
|
|
meshnode.blacklist["mcl_walls:granite_9"] = true
|
|
end
|
|
end
|
|
|
|
for name, config in pairs(meshnode.config) do
|
|
local setting = minetest.setting_get("meshnode_"..name)
|
|
if type(config) == "number" then
|
|
setting = tonumber(setting)
|
|
elseif type(config) == "boolean" then
|
|
setting = minetest.setting_getbool("meshnode_"..name)
|
|
end
|
|
if setting then
|
|
meshnode.config[name] = setting
|
|
end
|
|
end
|
|
|
|
if meshnode.config.autoconf == true then
|
|
minetest.setting_set("max_objects_per_block", "4096")
|
|
end
|
|
|
|
if meshnode.config.show_in_creative == false then
|
|
ctrl_groups.not_in_creative_inventory = 1
|
|
end
|
|
|
|
local function has_privilege(name)
|
|
if meshnode.config.disable_privilege == true then
|
|
return true
|
|
end
|
|
return minetest.check_player_privs(name, {meshnode=true})
|
|
end
|
|
|
|
local function show_meshnode_formspec(pos, player)
|
|
local name = player:get_player_name()
|
|
if not has_privilege(name) then
|
|
return
|
|
end
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local id = minetest.pos_to_string(pos)
|
|
local entity = meshnode.get_luaentity(id)
|
|
local spos = pos.x..","..pos.y..","..pos.z
|
|
local formspec = ""
|
|
if has_default then
|
|
formspec = "size[8,8]" ..
|
|
default.gui_bg ..
|
|
default.gui_bg_img ..
|
|
default.gui_slots ..
|
|
default.get_hotbar_bg(0,4) ..
|
|
"list[current_player;main;0,4;8,1;]" ..
|
|
"list[current_player;main;0,5.25;8,3;8]" ..
|
|
"list[nodemeta:"..spos..";tool;0.5,1.5;1,1;]"
|
|
end
|
|
if has_mcl then
|
|
formspec = "size[9,8.75]" ..
|
|
mcl_formspec.get_itemslot_bg(0.5,1.5,1,1) ..
|
|
"list[current_player;main;0,4.5;9,3;9]" ..
|
|
mcl_formspec.get_itemslot_bg(0,4.5,9,3) ..
|
|
"list[current_player;main;0,7.74;9,1;]" ..
|
|
mcl_formspec.get_itemslot_bg(0,7.74,9,1) ..
|
|
"list[nodemeta:" .. spos .. ";tool;0.5,1.5;1,1;]"
|
|
end
|
|
local buttons = {}
|
|
if entity and #entity.nodes > 0 then
|
|
if inv:contains_item("tool", "meshnode:glue") then
|
|
table.insert(buttons, {"activate", "Activate meshnode"})
|
|
end
|
|
table.insert(buttons, {"reset", "Reset connections"})
|
|
end
|
|
if meta:get_string("meshnode") ~= "" then
|
|
table.insert(buttons, {"connect_meta", "Connect meta positions"})
|
|
end
|
|
if has_worldedit and worldedit.pos1[name] and worldedit.pos2[name] then
|
|
table.insert(buttons, {"connect_we", "Connect worldedit markers"})
|
|
end
|
|
local y = 0.25
|
|
for _, btn in pairs(buttons) do
|
|
formspec = formspec.."button_exit[2.25,"..y..
|
|
";5,0.5;"..btn[1]..";"..S(btn[2]).."]"
|
|
y = y + 0.9
|
|
end
|
|
minetest.show_formspec(name, "meshnode_"..id, formspec)
|
|
end
|
|
|
|
local function register_scaffold(name, groups)
|
|
groups.not_in_creative_inventory = 1
|
|
groups.cracky = 1
|
|
minetest.register_node(name, {
|
|
paramtype = "light",
|
|
drawtype = "mesh",
|
|
mesh = "meshnode_highlight.obj",
|
|
is_ground_content = false,
|
|
sunlight_propagates = true,
|
|
use_texture_alpha = true,
|
|
tiles = {"meshnode_highlight.png"},
|
|
groups = groups,
|
|
})
|
|
end
|
|
|
|
register_scaffold("meshnode:scaffold", {})
|
|
register_scaffold("meshnode:scaffold_fence", {fence=1})
|
|
register_scaffold("meshnode:scaffold_wall", {wall=1})
|
|
register_scaffold("meshnode:scaffold_pane", {pane=1})
|
|
|
|
if meshnode.config.enable_crafting == true then
|
|
local recipe
|
|
if has_default then
|
|
recipe = {
|
|
{"default:bronzeblock", "default:diamondblock", "default:bronzeblock"},
|
|
{"default:obsidian_block", "default:steelblock", "default:goldblock"},
|
|
{"default:bronzeblock", "default:steelblock", "default:bronzeblock"},
|
|
}
|
|
if meshnode.config.use_archimedes_ships_crafting_recipe then
|
|
recipe = {
|
|
{"group:wood", "group:stick", "group:wood"},
|
|
{"group:stick", "default:steel_ingot", "group:stick"},
|
|
{"group:wood", "group:stick", "group:wood"},
|
|
}
|
|
end
|
|
end
|
|
if has_mcl then
|
|
recipe = {
|
|
{"mcl_colorblocks:concrete_orange", "mcl_core:diamondblock", "mcl_colorblocks:concrete_orange"},
|
|
{"mcl_core:obsidian", "mcl_core:ironblock", "mcl_core:goldblock"},
|
|
{"mcl_colorblocks:concrete_orange", "mcl_core:ironblock", "mcl_colorblocks:concrete_orange"},
|
|
}
|
|
if meshnode.config.use_archimedes_ships_crafting_recipe then
|
|
recipe = {
|
|
{"group:wood", "group:stick", "group:wood"},
|
|
{"group:stick", "mcl_core:iron_ingot", "group:stick"},
|
|
{"group:wood", "group:stick", "group:wood"},
|
|
}
|
|
end
|
|
end
|
|
minetest.register_craft({
|
|
output = "meshnode:controller",
|
|
recipe = recipe
|
|
})
|
|
end
|
|
|
|
minetest.register_privilege("meshnode", "Player can use meshnode controller.")
|
|
|
|
minetest.register_entity("meshnode:mesh", {
|
|
physical = true,
|
|
visual = "wielditem",
|
|
visual_size = {x=0.666, y=0.666},
|
|
on_activate = function(self, staticdata)
|
|
if staticdata == "expired" then
|
|
self.object:remove()
|
|
end
|
|
end,
|
|
get_staticdata = function(self)
|
|
return "expired"
|
|
end,
|
|
})
|
|
|
|
minetest.register_entity("meshnode:ctrl", {
|
|
physical = true,
|
|
visual = "mesh",
|
|
mesh = "meshnode_plant.obj",
|
|
visual_size = {x=1, y=1},
|
|
textures = {"meshnode_trans.png"},
|
|
facedir = 0,
|
|
speed = 0,
|
|
lift = 0,
|
|
nodes = {},
|
|
activated = false,
|
|
animation = "stand",
|
|
on_activate = function(self, staticdata)
|
|
local pos = self.object:getpos()
|
|
local objects = minetest.get_objects_inside_radius(pos, 0.5) or {}
|
|
for _, object in pairs(objects) do
|
|
if object ~= self.object then
|
|
local entity = object:get_luaentity()
|
|
if entity and entity.name == "meshnode:ctrl" then
|
|
minetest.log("warning", "meshnode: duplicate object removed")
|
|
self.object:remove()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
self.object:set_armor_groups({immortal=1})
|
|
if staticdata then
|
|
local data = minetest.deserialize(staticdata) or {}
|
|
self.mesh_id = data[1]
|
|
self.activated = data[2]
|
|
self.nodes = data[3] or {}
|
|
end
|
|
if self.activated then
|
|
self.mesh_id = meshnode.new_id()
|
|
else
|
|
local node = minetest.get_node(pos)
|
|
if node.name ~= "meshnode:controller" then
|
|
minetest.log("warning", "meshnode: stray object removed")
|
|
self.object:remove()
|
|
return
|
|
end
|
|
end
|
|
for _, ref in pairs(self.nodes) do
|
|
ref.id = meshnode.new_id()
|
|
meshnode.add_entity(ref, self)
|
|
end
|
|
self:set_activated(self.activated)
|
|
end,
|
|
on_punch = function(self, puncher)
|
|
--self.object:remove()
|
|
end,
|
|
on_rightclick = function(self, clicker)
|
|
if not self.mesh_id then
|
|
return
|
|
end
|
|
if self.activated then
|
|
local name = clicker:get_player_name()
|
|
local buttons = {}
|
|
local formspec = "size[4,3.5]"
|
|
if self.player then
|
|
table.insert(buttons, {"detach", "Detach"})
|
|
local btn = {"animation_sit", "Sit"}
|
|
if self.animation == "sit" then
|
|
btn = {"animation_stand", "Stand"}
|
|
end
|
|
table.insert(buttons, btn)
|
|
else
|
|
table.insert(buttons, {"attach", "Attach"})
|
|
table.insert(buttons, {"restore", "Restore"})
|
|
end
|
|
table.insert(buttons, {"align", "Align"})
|
|
local y = 0.5
|
|
for _, btn in pairs(buttons) do
|
|
formspec = formspec.."button_exit[0.5,"..y..
|
|
";3,0.75;"..btn[1]..";"..S(btn[2]).."]"
|
|
y = y + 1
|
|
end
|
|
local formname = "meshnode_"..self.mesh_id
|
|
minetest.show_formspec(name, formname, formspec)
|
|
end
|
|
end,
|
|
on_step = function(self, dtime)
|
|
if self.player then
|
|
local velocity = self.object:getvelocity()
|
|
local yaw = self.object:getyaw()
|
|
local speed = self.speed
|
|
local lift = self.lift
|
|
local ctrl = self.player:get_player_control()
|
|
if ctrl.up then
|
|
speed = speed + 0.1
|
|
elseif ctrl.down then
|
|
speed = speed - 0.1
|
|
else
|
|
speed = speed * 0.99
|
|
end
|
|
if speed > meshnode.config.max_speed then
|
|
speed = meshnode.config.max_speed
|
|
elseif speed < 0 - meshnode.config.max_speed then
|
|
speed = 0 - meshnode.config.max_speed
|
|
end
|
|
if ctrl.jump then
|
|
lift = lift + 0.1
|
|
elseif ctrl.sneak then
|
|
lift = lift - 0.1
|
|
else
|
|
lift = lift * 0.99
|
|
end
|
|
if lift > meshnode.config.max_lift then
|
|
lift = meshnode.config.max_lift
|
|
elseif lift < 0 - meshnode.config.max_lift then
|
|
lift = 0 - meshnode.config.max_lift
|
|
end
|
|
if ctrl.left then
|
|
yaw = yaw + meshnode.config.yaw_amount
|
|
elseif ctrl.right then
|
|
yaw = yaw - meshnode.config.yaw_amount
|
|
end
|
|
velocity.x = -math.sin(yaw) * speed
|
|
velocity.y = lift
|
|
velocity.z = math.cos(yaw) * speed
|
|
self.object:setyaw(yaw)
|
|
self.object:setvelocity(velocity)
|
|
self.speed = speed
|
|
self.lift = lift
|
|
else
|
|
self.object:setvelocity({x=0, y=0, z=0})
|
|
self.speed = 0
|
|
self.lift = 0
|
|
end
|
|
end,
|
|
get_staticdata = function(self)
|
|
local data = {self.mesh_id, self.activated, self.nodes}
|
|
return minetest.serialize(data)
|
|
end,
|
|
set_alignment = function(self)
|
|
local pos = self.object:getpos()
|
|
local yaw = self.object:getyaw()
|
|
local deg = math.deg(yaw) + 45
|
|
deg = math.floor(deg / 90) * 90
|
|
self.object:setvelocity({x=0, y=0, z=0})
|
|
self.object:setyaw(math.rad(deg))
|
|
self.object:setpos(vector.round(pos))
|
|
self.speed = 0
|
|
self.lift = 0
|
|
end,
|
|
set_activated = function(self, active)
|
|
local mesh = "meshnode_plant.obj"
|
|
local anim = {x=0, y=0}
|
|
local textures = {"meshnode_trans.png"}
|
|
local collisionbox = {0,0,0, 0,0,0}
|
|
if active then
|
|
mesh = "meshnode_ctrl.b3d"
|
|
anim = {x=20, y=100}
|
|
textures = control_textures
|
|
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5}
|
|
end
|
|
self.object:set_properties({
|
|
mesh = mesh,
|
|
textures = textures,
|
|
collisionbox = collisionbox,
|
|
})
|
|
self.object:set_animation(anim, 15)
|
|
self.activated = active
|
|
end,
|
|
})
|
|
|
|
minetest.register_node("meshnode:controller", {
|
|
description = S("Meshnode Controller"),
|
|
drawtype = "mesh",
|
|
mesh = "meshnode_ctrl.obj",
|
|
paramtype = "light",
|
|
paramtype2 = "facedir",
|
|
tiles = control_textures,
|
|
groups = ctrl_groups,
|
|
-- _mcl_hardness is needed so that the controller is not
|
|
-- diggable without a delay in MineClone2-derived games.
|
|
_mcl_hardness = 1,
|
|
on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local stack = {
|
|
name = "meshnode:glue",
|
|
metadata = minetest.pos_to_string(pos),
|
|
}
|
|
inv:set_size("tool", 1)
|
|
inv:add_item("tool", stack)
|
|
meta:set_string("infotext", S("Meshnode Controller"))
|
|
end,
|
|
on_destruct = function(pos)
|
|
local id = minetest.pos_to_string(pos)
|
|
local entity = meshnode.get_luaentity(id)
|
|
if entity then
|
|
meshnode.restore_all(entity)
|
|
entity.object:remove()
|
|
end
|
|
end,
|
|
after_place_node = function(pos, placer)
|
|
local node = minetest.get_node(pos)
|
|
local id = minetest.pos_to_string(pos)
|
|
local object = minetest.add_entity(pos, "meshnode:ctrl")
|
|
if object then
|
|
local entity = object:get_luaentity()
|
|
if entity then
|
|
local facedir = node.param2 or 0
|
|
local yaw = meshnode.facedir_to_yaw(facedir)
|
|
object:setyaw(yaw)
|
|
entity.mesh_id = id
|
|
end
|
|
end
|
|
end,
|
|
on_rightclick = function(pos, node, clicker, itemstack)
|
|
show_meshnode_formspec(pos, clicker)
|
|
end,
|
|
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
if stack:get_name() ~= "meshnode:glue" then
|
|
return 0
|
|
end
|
|
local meta = stack:get_metadata()
|
|
if meta then
|
|
if vector.equals(minetest.string_to_pos(meta), pos) then
|
|
return 1
|
|
end
|
|
end
|
|
return 0
|
|
end,
|
|
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
show_meshnode_formspec(pos, player)
|
|
end,
|
|
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
show_meshnode_formspec(pos, player)
|
|
end
|
|
})
|
|
|
|
minetest.register_tool("meshnode:glue", {
|
|
description = S("Meshnode Glue"),
|
|
inventory_image = "meshnode_glue.png",
|
|
liquids_pointable = true,
|
|
groups = {not_in_creative_inventory=1},
|
|
on_use = function(itemstack, user, pointed_thing)
|
|
local meta = itemstack:get_metadata()
|
|
local parent = meshnode.get_luaentity(meta)
|
|
if not parent then
|
|
return
|
|
end
|
|
local name = user:get_player_name()
|
|
if not has_privilege(name) then
|
|
return ""
|
|
end
|
|
if pointed_thing and pointed_thing.type == "node" then
|
|
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
|
if pos then
|
|
if minetest.is_protected(pos, name) then
|
|
minetest.chat_send_player(name, S("Protected node").."!")
|
|
return
|
|
end
|
|
local node = minetest.get_node_or_nil(pos)
|
|
if node then
|
|
if string.find(node.name, "meshnode:scaffold") then
|
|
for i, ref in pairs(parent.nodes) do
|
|
local map_pos = meshnode.get_map_pos(ref, parent)
|
|
if vector.equals(map_pos, pos) then
|
|
meshnode.restore(ref, parent)
|
|
parent.nodes[i] = nil
|
|
end
|
|
end
|
|
else
|
|
local dist = vector.distance(parent.object:getpos(), pos)
|
|
if dist > meshnode.config.max_radius then
|
|
minetest.chat_send_player(name, S("Out of range").."!")
|
|
else
|
|
meshnode.create(pos, parent)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
})
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
if formname then
|
|
local id = formname:gsub("meshnode_", "")
|
|
if id == formname then
|
|
return
|
|
end
|
|
local name = player:get_player_name()
|
|
local entity = meshnode.get_luaentity(id)
|
|
if not entity then
|
|
return
|
|
end
|
|
local pos = vector.round(entity.object:getpos())
|
|
if fields.activate then
|
|
local id = meshnode.new_id()
|
|
entity.mesh_id = meshnode.new_id()
|
|
entity:set_activated(true)
|
|
for _, ref in pairs(entity.nodes) do
|
|
local map_pos = meshnode.get_map_pos(ref, entity)
|
|
minetest.remove_node(map_pos)
|
|
ref.parent = id
|
|
end
|
|
minetest.remove_node(pos)
|
|
elseif fields.connect_meta then
|
|
local node = minetest.get_node(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local spos = meta:get_string("meshnode")
|
|
local positions = minetest.deserialize(spos) or {}
|
|
for _, p in pairs(positions) do
|
|
meshnode.create(p, entity)
|
|
end
|
|
elseif fields.connect_we then
|
|
if has_worldedit then
|
|
local p1 = worldedit.pos1[name]
|
|
local p2 = worldedit.pos2[name]
|
|
if p1 and p2 then
|
|
local minp = {
|
|
x = math.min(p1.x, p2.x),
|
|
y = math.min(p1.y, p2.y),
|
|
z = math.min(p1.z, p2.z),
|
|
}
|
|
local maxp = {
|
|
x = math.max(p1.x, p2.x),
|
|
y = math.max(p1.y, p2.y),
|
|
z = math.max(p1.z, p2.z),
|
|
}
|
|
local area = VoxelArea:new{MinEdge=minp, MaxEdge=maxp}
|
|
for i in area:iterp(minp, maxp) do
|
|
local p = area:position(i)
|
|
meshnode.create(p, entity)
|
|
end
|
|
end
|
|
end
|
|
elseif fields.reset then
|
|
meshnode.restore_all(entity)
|
|
entity.nodes = {}
|
|
elseif fields.attach and entity.player == nil then
|
|
-- player position must be right on top of
|
|
-- meshnode controller so that players can
|
|
-- click that controller to unattach later
|
|
player:set_attach(entity.object, "", {x=0, y=5, z=0}, {x=0, y=0, z=0})
|
|
entity.player = player
|
|
entity.object:set_animation({x=0, y=0}, 15)
|
|
player_mod.player_attached[name] = true
|
|
elseif fields.detach and entity.player == player then
|
|
player:set_detach()
|
|
entity.player = nil
|
|
entity.animation = "stand"
|
|
entity.object:set_animation({x=20, y=100}, 15)
|
|
player_mod.player_attached[name] = false
|
|
local p = player:getpos()
|
|
minetest.after(0.1, function()
|
|
player:setpos({x=p.x, y=p.y + 1, z=p.z})
|
|
end)
|
|
elseif fields.animation_sit then
|
|
player_mod.player_set_animation(player, "sit", 30)
|
|
entity.animation = "sit"
|
|
elseif fields.animation_stand then
|
|
player_mod.player_set_animation(player, "stand", 30)
|
|
entity.animation = "stand"
|
|
elseif fields.align then
|
|
entity:set_alignment()
|
|
elseif fields.restore then
|
|
if not has_privilege(name) then
|
|
local msg = S("Requires the meshnode privilege")
|
|
minetest.chat_send_player(name, msg)
|
|
return
|
|
end
|
|
entity:set_alignment()
|
|
local positions = meshnode.restore_all(entity, name)
|
|
if positions then
|
|
local yaw = entity.object:getyaw()
|
|
local node = {
|
|
name = "meshnode:controller",
|
|
param2 = meshnode.yaw_to_facedir(yaw),
|
|
}
|
|
minetest.add_node(pos, node)
|
|
if #positions > 0 then
|
|
local meta = minetest.get_meta(pos)
|
|
local spos = minetest.serialize(positions)
|
|
meta:set_string("meshnode", spos)
|
|
end
|
|
entity.nodes = {}
|
|
entity.mesh_id = minetest.pos_to_string(pos)
|
|
entity:set_activated(false)
|
|
else
|
|
local msg = S("Protected or unloaded area")
|
|
minetest.chat_send_player(name, msg.."!")
|
|
end
|
|
end
|
|
end
|
|
end)
|