local S = minetest.get_translator(minetest.get_current_modname()) local minetest = minetest local mod_doc = minetest.get_modpath("doc") local mod_screwdriver = minetest.get_modpath("screwdriver") local equip_armor if minetest.get_modpath("mcl_armor") then equip_armor = mcl_armor.equip_on_use end mcl_heads = {} -- rotations of head nodes within a quadrant (0° ≤ θ ≤ 90°) mcl_heads.FLOOR_DEGREES = { [0]='', '22_5', '45', '67_5', } -- box of head nodes mcl_heads.FLOOR_BOX = { -0.25, -0.5, -0.25, 0.25, 0.0, 0.25, } -- floor head node nodedef template ------------------------------------------------------------------------------------ --- node definition template for floor mod heads mcl_heads.deftemplate_floor = { drawtype = "nodebox", node_box = { type = "fixed", fixed = mcl_heads.FLOOR_BOX, }, groups = { handy = 1, armor = 1, armor_head = 1, non_combat_armor = 1, non_combat_armor_head = 1, head = 1, deco_block = 1, dig_by_piston = 1, }, use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false, paramtype = "light", paramtype2 = "facedir", stack_max = 64, sunlight_propagates = true, sounds = mcl_sounds.node_sound_defaults{ footstep = {name="default_hard_footstep", gain=0.3}, }, is_ground_content = false, _mcl_armor_element = "head", _mcl_blast_resistance = 1, _mcl_hardness = 1, on_secondary_use = equip_armor, } mcl_heads.deftemplate_floor_angled = { drawtype = "mesh", selection_box = { type = "fixed", fixed = mcl_heads.FLOOR_BOX, }, collision_box = { type = "fixed", fixed = mcl_heads.FLOOR_BOX, }, groups = { handy = 1, head = 1, deco_block = 1, dig_by_piston = 1, not_in_creative_inventory = 1, }, use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false, paramtype = "light", paramtype2 = "facedir", stack_max = 64, sunlight_propagates = true, sounds = mcl_sounds.node_sound_defaults{ footstep = {name="default_hard_footstep", gain=0.3}, }, is_ground_content = false, _doc_items_create_entry = false, _mcl_blast_resistance = 1, _mcl_hardness = 1, } function mcl_heads.deftemplate_floor.on_rotate(pos, node, user, mode, new_param2) if mode == screwdriver.ROTATE_AXIS then node.name = node.name .. "_wall" node.param2 = minetest.dir_to_wallmounted(minetest.facedir_to_dir(node.param2)) minetest.set_node(pos, node) return true end end function mcl_heads.deftemplate_floor.on_place(itemstack, placer, pointed_thing) if pointed_thing.type ~= "node" then return itemstack end local under = pointed_thing.under local node = minetest.get_node(under) local def = minetest.registered_nodes[node.name] if not def then return itemstack end -- Allow pointed node's on_rightclick callback to override place. if placer and not placer:get_player_control().sneak then if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then return minetest.registered_nodes[node.name].on_rightclick(under, node, placer, itemstack) or itemstack end end local above = pointed_thing.above local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z} local wdir = minetest.dir_to_wallmounted(dir) local itemstring = itemstack:get_name() local placestack = ItemStack(itemstack) -- place wall head node (elsewhere) if wdir ~= 0 and wdir ~= 1 then placestack:set_name(itemstring .."_wall") itemstack = minetest.item_place(placestack, placer, pointed_thing, wdir) -- place floor head node (floor and ceiling) else local fdir = minetest.dir_to_facedir(dir) -- determine the head node rotation based on player's yaw (in cw direction from North/Z+) local yaw = placer:get_look_horizontal() yaw = wdir == 1 and math.pi*2 - yaw or yaw local rotation_level = math.min(math.max(math.round((yaw / (math.pi*2)) * 16), 0), 15) placestack:set_name(itemstring ..mcl_heads.FLOOR_DEGREES[rotation_level % 4]) -- determine the head node face direction based on rotation level fdir = math.floor(rotation_level / 4) + (wdir == 1 and 0 or 20) itemstack = minetest.item_place(placestack, placer, pointed_thing, fdir) end -- restore item from angled and wall head nodes itemstack:set_name(itemstring) return itemstack end -- wall head node nodedef template ------------------------------------------------------------------------------------- --- node definition template for wall mod heads mcl_heads.deftemplate_wall = { drawtype = "nodebox", node_box = { type = "wallmounted", wall_bottom = { -0.25, -0.5, -0.25, 0.25, 0.0, 0.25, }, wall_top = { -0.25, 0.0, -0.25, 0.25, 0.5, 0.25, }, wall_side = { -0.5, -0.25, -0.25, 0.0, 0.25, 0.25, }, }, groups = { handy = 1, head = 1, deco_block = 1, dig_by_piston = 1, not_in_creative_inventory = 1, }, use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false, paramtype = "light", paramtype2 = "wallmounted", stack_max = 64, sunlight_propagates = true, sounds = mcl_sounds.node_sound_defaults{ footstep = {name="default_hard_footstep", gain=0.3}, }, is_ground_content = false, _doc_items_create_entry = false, _mcl_blast_resistance = 1, _mcl_hardness = 1, } function mcl_heads.deftemplate_wall.on_rotate(pos, node, user, mode, new_param2) if mode == screwdriver.ROTATE_AXIS then node.name = string.sub(node.name, 1, string.len(node.name)-5) node.param2 = minetest.dir_to_facedir(minetest.wallmounted_to_dir(node.param2)) minetest.set_node(pos, node) return true end end -- API functions ------------------------------------------------------------------------------------------------------- --- @class HeadDef --- @field name string identifier for node --- @field texture string armor texture for node --- @field description string translated description --- @field longdesc string translated doc description --- @field range_mob string name of mob affected by range reduction --- @field range_factor number factor of range reduction --- registers a head --- @param head_def HeadDef head node definition function mcl_heads.register_head(head_def) local name = "mcl_heads:" ..head_def.name -- register the floor head node minetest.register_node(name, table.update(table.copy(mcl_heads.deftemplate_floor), { description = head_def.description, _doc_items_longdesc = head_def.longdesc, -- The head textures are based off the textures of an actual mob. tiles = { -- Note: bottom texture is overlaid over top texture to get rid of possible transparency. -- This is required for skeleton skull and wither skeleton skull. -- Note: -x coords go right per-pixel, -y coords go down per-pixel "[combine:16x16:-36,4=" ..head_def.texture, -- top "([combine:16x16:-36,4=" ..head_def.texture..")^([combine:16x16:-44,4="..head_def.texture..")", -- bottom "[combine:16x16:-28,0=" ..head_def.texture, -- left "[combine:16x16:-44,0=" ..head_def.texture, -- right "[combine:16x16:-52,0=" ..head_def.texture, -- back "[combine:16x16:-36,0=" ..head_def.texture, -- front }, _mcl_armor_mob_range_mob = head_def.range_mob, _mcl_armor_mob_range_factor = head_def.range_factor, _mcl_armor_texture = head_def.texture })) -- register the angled floor head nodes for i, d in ipairs(mcl_heads.FLOOR_DEGREES) do minetest.register_node(name ..d, table.update(table.copy(mcl_heads.deftemplate_floor_angled), { mesh = "mcl_heads_floor" ..d ..".obj", tiles = { head_def.texture }, drop = name, })) end -- register the wall head node minetest.register_node(name .."_wall", table.update(table.copy(mcl_heads.deftemplate_wall), { -- The head textures are based off the textures of an actual mob. -- Note: -x coords go right per-pixel, -y coords go down per-pixel tiles = { { name = "[combine:16x16:-36,-4=" ..head_def.texture, align_style = "world" }, -- front { name = "[combine:16x16:-52,-4="..head_def.texture, align_style = "world" }, -- back { name = "[combine:16x16:-40,-4=" ..head_def.texture, align_style = "world" }, -- right { name = "[combine:16x16:-32,-4=" ..head_def.texture, align_style = "world" }, -- left { name = "([combine:16x16:-36,0=" ..head_def.texture ..")^[transformR180", align_style = "node" }, -- top -- Note: bottom texture is overlaid over top texture to get rid of possible transparency. -- This is required for skeleton skull and wither skeleton skull. { name = "([combine:16x16:-36,0=" ..head_def.texture ..")^([combine:16x16:-44,8=" ..head_def.texture..")", align_style = "node" }, -- bottom }, drop = name, })) end -- initial heads ------------------------------------------------------------------------------------------------------- mcl_heads.register_head{ name = "zombie", texture = "mcl_heads_zombie.png", description = S("Zombie Head"), longdesc = S("A zombie head is a small decorative block which resembles the head of a zombie. It can also be worn as a helmet, which reduces the detection range of zombies by 50%."), range_mob = "mobs_mc:zombie", range_factor = 0.5, } mcl_heads.register_head{ name = "creeper", texture = "mcl_heads_creeper.png", description = S("Creeper Head"), longdesc = S("A creeper head is a small decorative block which resembles the head of a creeper. It can also be worn as a helmet, which reduces the detection range of creepers by 50%."), range_mob = "mobs_mc:creeper", range_factor = 0.5, } -- Original Minecraft name: “Head” mcl_heads.register_head{ name = "steve", texture = "mcl_heads_steve.png", description = S("Human Head"), longdesc = S("A human head is a small decorative block which resembles the head of a human (i.e. a player character). It can also be worn as a helmet for fun, but does not offer any protection."), } mcl_heads.register_head{ name = "skeleton", texture = "mcl_heads_skeleton.png", description = S("Skeleton Skull"), longdesc = S("A skeleton skull is a small decorative block which resembles the skull of a skeleton. It can also be worn as a helmet, which reduces the detection range of skeletons by 50%."), range_mob = "mobs_mc:skeleton", range_factor = 0.5, } mcl_heads.register_head{ name = "wither_skeleton", texture = "mcl_heads_wither_skeleton.png", description = S("Wither Skeleton Skull"), longdesc = S("A wither skeleton skull is a small decorative block which resembles the skull of a wither skeleton. It can also be worn as a helmet for fun, but does not offer any protection."), }