2021-05-29 16:12:33 +02:00
local S = minetest.get_translator ( minetest.get_current_modname ( ) )
2019-03-08 00:22:28 +01:00
2022-08-07 11:45:39 +02:00
local minetest = minetest
2020-02-18 19:30:33 +01:00
local mod_doc = minetest.get_modpath ( " doc " )
2020-02-18 20:04:15 +01:00
local mod_screwdriver = minetest.get_modpath ( " screwdriver " )
2020-02-18 19:30:33 +01:00
2020-02-18 22:17:52 +01:00
local equip_armor
if minetest.get_modpath ( " mcl_armor " ) then
2021-04-14 15:46:52 +02:00
equip_armor = mcl_armor.equip_on_use
2020-02-18 22:17:52 +01:00
end
2022-08-07 11:45:39 +02:00
mcl_heads = { }
2022-08-11 11:12:18 +02:00
-- rotations of head nodes within a quadrant (0° ≤ θ ≤ 90°)
mcl_heads.FLOOR_DEGREES = { [ 0 ] = ' ' , ' 22_5 ' , ' 45 ' , ' 67_5 ' , }
2022-08-07 11:45:39 +02:00
-- 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 = {
2024-08-22 00:03:39 +02:00
drawtype = " mesh " ,
mesh = " mcl_heads_floor.obj " ,
selection_box = {
type = " fixed " ,
fixed = mcl_heads.FLOOR_BOX ,
} ,
collision_box = {
2022-08-07 11:45:39 +02:00
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 " ,
2024-08-22 00:03:39 +02:00
paramtype2 = " degrotate " ,
2022-08-07 11:45:39 +02:00
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 ,
}
function mcl_heads . deftemplate_floor . on_rotate ( pos , node , user , mode , new_param2 )
2024-08-22 00:03:39 +02:00
if not ( user and user : is_player ( ) ) then return end
2022-08-07 11:45:39 +02:00
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
2020-02-18 20:04:15 +01:00
end
2017-12-05 14:09:39 +01:00
end
2022-08-07 11:45:39 +02:00
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 )
2022-08-11 11:12:18 +02:00
-- 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 )
2022-08-12 18:13:12 +02:00
2022-08-11 11:12:18 +02:00
-- place floor head node (floor and ceiling)
else
-- determine the head node rotation based on player's yaw (in cw direction from North/Z+)
2024-08-22 00:03:39 +02:00
local rotate_level = math.round ( placer : get_look_horizontal ( ) * 8 / math.pi )
itemstack = minetest.item_place ( placestack , placer , pointed_thing , rotate_level * 15 )
2022-08-11 11:12:18 +02:00
end
-- restore item from angled and wall head nodes
2022-08-07 11:45:39 +02:00
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 )
2024-08-22 00:03:39 +02:00
if not ( user and user : is_player ( ) ) then return end
2022-08-07 11:45:39 +02:00
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
2022-08-11 11:15:39 +02:00
--- @field texture string armor texture for node
2022-08-07 11:45:39 +02:00
--- @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 ,
2024-08-22 00:03:39 +02:00
tiles = { head_def.texture } ,
2022-08-07 11:45:39 +02:00
_mcl_armor_mob_range_mob = head_def.range_mob ,
_mcl_armor_mob_range_factor = head_def.range_factor ,
2022-08-11 11:15:39 +02:00
_mcl_armor_texture = head_def.texture
2022-08-07 11:45:39 +02:00
} ) )
-- register the wall head node
minetest.register_node ( name .. " _wall " , table.update ( table.copy ( mcl_heads.deftemplate_wall ) , {
2020-02-18 19:30:33 +01:00
-- The head textures are based off the textures of an actual mob.
2022-08-11 11:15:39 +02:00
-- Note: -x coords go right per-pixel, -y coords go down per-pixel
2020-02-18 19:30:33 +01:00
tiles = {
2022-08-11 11:15:39 +02:00
{ 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
2022-08-07 11:45:39 +02:00
-- Note: bottom texture is overlaid over top texture to get rid of possible transparency.
-- This is required for skeleton skull and wither skeleton skull.
2022-08-11 11:15:39 +02:00
{ name = " ([combine:16x16:-36,0= " .. head_def.texture .. " )^([combine:16x16:-44,8= " .. head_def.texture .. " ) " , align_style = " node " } , -- bottom
2020-02-18 19:30:33 +01:00
} ,
2022-08-07 11:45:39 +02:00
drop = name ,
} ) )
2015-06-29 19:55:56 +02:00
end
2022-08-07 11:45:39 +02:00
-- initial heads -------------------------------------------------------------------------------------------------------
mcl_heads.register_head {
name = " zombie " ,
2022-08-11 11:15:39 +02:00
texture = " mcl_heads_zombie.png " ,
2022-08-07 11:45:39 +02:00
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 {
2024-05-12 06:21:37 +02:00
name = " stalker " ,
texture = " mcl_heads_stalker.png " ,
description = S ( " Stalker Head " ) ,
longdesc = S ( " A stalker head is a small decorative block which resembles the head of a stalker. It can also be worn as a helmet, which reduces the detection range of stalkers by 50%. " ) ,
range_mob = " mobs_mc:stalker " ,
2022-08-07 11:45:39 +02:00
range_factor = 0.5 ,
}
2017-03-11 18:51:39 +01:00
-- Original Minecraft name: “Head”
2022-08-07 11:45:39 +02:00
mcl_heads.register_head {
name = " steve " ,
2022-08-11 11:15:39 +02:00
texture = " mcl_heads_steve.png " ,
2022-08-07 11:45:39 +02:00
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 " ,
2022-08-11 11:15:39 +02:00
texture = " mcl_heads_skeleton.png " ,
2022-08-07 11:45:39 +02:00
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 " ,
2022-08-11 11:15:39 +02:00
texture = " mcl_heads_wither_skeleton.png " ,
2022-08-07 11:45:39 +02:00
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. " ) ,
}
2024-08-22 00:03:39 +02:00
-- Alias old creeper heads
minetest.register_alias ( " mcl_heads:creeper_wall " , " mcl_heads:stalker_wall " )
minetest.register_alias ( " mcl_heads:creeper " , " mcl_heads:stalker " )
-- Register LBM updates for old floor heads
local old_heads_all = { " mcl_heads:zombie " , " mcl_heads:creeper " , " mcl_heads:stalker " , " mcl_heads:steve " , " mcl_heads:skeleton " , " mcl_heads:wither_skeleton " }
local old_heads_angled = { }
for i = 1 , # old_heads_all do
for j = 1 , # mcl_heads.FLOOR_DEGREES do
old_heads_angled [ # old_heads_angled + 1 ] = old_heads_all [ i ] .. mcl_heads.FLOOR_DEGREES [ j ]
end
end
2024-09-08 04:26:51 +02:00
-- add the angled heads to old_heads_all
for i = 1 , # old_heads_angled do
old_heads_all [ # old_heads_all + 1 ] = old_heads_angled [ i ]
end
2024-08-22 00:03:39 +02:00
local facedir_to_degrotate = { 0 , 180 , 120 , 60 }
minetest.register_lbm ( {
name = " mcl_heads:heads_update_angled " ,
nodenames = old_heads_all ,
action = function ( pos , node )
local degrotate = 0
if node.name : sub ( - 4 ) == " 22_5 " then
node.name = node.name : sub ( 1 , # node.name - 4 )
degrotate = ( ( 4 - node.param2 ) * 90 - 22.5 ) / 1.5
elseif node.name : sub ( - 4 ) == " 67_5 " then
node.name = node.name : sub ( 1 , # node.name - 4 )
degrotate = ( ( 4 - node.param2 ) * 90 - 67.5 ) / 1.5
elseif node.name : sub ( - 2 ) == " 45 " then
node.name = node.name : sub ( 1 , # node.name - 2 )
degrotate = ( ( 4 - node.param2 ) * 90 - 45 ) / 1.5
else
degrotate = facedir_to_degrotate [ node.param2 + 1 ]
end
if not degrotate then
return
end
node.param2 = degrotate
minetest.set_node ( pos , node )
end ,
} )