2017-07-05 03:15:46 +02:00
--MCmobs v0.4
--maikerumine
--made for MC like Survival game
--License for code WTFPL and otherwise stated in readmes
2020-06-13 21:51:27 +02:00
-- ENDERMAN BEHAVIOUR (OLD):
2018-12-22 14:29:45 +01:00
-- In this game, endermen attack the player on sight, like other monsters do.
-- However, they have a reduced viewing range to make them less dangerous.
-- This differs from MC, in which endermen only become hostile when provoked,
-- and they are provoked by looking directly at them.
2020-06-08 07:51:48 +02:00
-- Rootyjr
-----------------------------
-- implemented ability to detect when seen / break eye contact and aggressive response
-- implemented teleport to avoid arrows.
-- implemented teleport to avoid rain.
-- implemented teleport to chase.
-- added enderman particles.
-- drew mcl_portal_particle1.png
-- drew mcl_portal_particle2.png
-- drew mcl_portal_particle3.png
-- drew mcl_portal_particle4.png
-- drew mcl_portal_particle5.png
-- added rain damage.
-- fixed the grass_with_dirt issue.
2022-10-05 20:14:01 +02:00
minetest.register_entity ( " mobs_mc:ender_eyes " , {
visual = " mesh " ,
mesh = " mobs_mc_spider.b3d " ,
visual_size = { x = 1.01 , y = 1.01 } ,
textures = {
" mobs_mc_enderman_eyes.png " ,
} ,
on_activate = function ( self )
minetest.after ( 0.1 , function ( )
if not self.object : get_attach ( ) then
self.object : remove ( )
end
end )
end ,
glow = 50 ,
} )
2022-02-13 21:40:12 +01:00
local S = minetest.get_translator ( " mobs_mc " )
2017-07-05 03:15:46 +02:00
2022-02-13 21:40:12 +01:00
local telesound = function ( pos , is_source )
2020-12-10 16:35:48 +01:00
local snd
if is_source then
snd = " mobs_mc_enderman_teleport_src "
else
snd = " mobs_mc_enderman_teleport_dst "
end
minetest.sound_play ( snd , { pos = pos , max_hear_distance = 16 } , true )
end
2017-07-05 03:15:46 +02:00
--###################
--################### ENDERMAN
--###################
local pr = PseudoRandom ( os.time ( ) * ( - 334 ) )
2017-09-13 23:13:47 +02:00
-- How freqeuntly to take and place blocks, in seconds
2021-02-01 22:45:22 +01:00
local take_frequency_min = 235
local take_frequency_max = 245
local place_frequency_min = 235
local place_frequency_max = 245
2017-07-05 03:15:46 +02:00
2022-05-25 14:57:59 +02:00
-- Texuture overrides for enderman block. Required for cactus because it's original is a nodebox
-- and the textures have tranparent pixels.
local block_texture_overrides
do
local cbackground = " mobs_mc_enderman_cactus_background.png "
local ctiles = minetest.registered_nodes [ " mcl_core:cactus " ] . tiles
local ctable = { }
local last
for i = 1 , 6 do
if ctiles [ i ] then
last = ctiles [ i ]
end
table.insert ( ctable , cbackground .. " ^ " .. last )
end
block_texture_overrides = {
[ " mcl_core:cactus " ] = ctable ,
-- FIXME: replace colorize colors with colors from palette
[ " mcl_core:dirt_with_grass " ] =
{
" mcl_core_grass_block_top.png^[colorize:green:90 " ,
" default_dirt.png " ,
" default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90) " ,
" default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90) " ,
" default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90) " ,
" default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90) " }
}
end
2017-08-23 03:50:22 +02:00
-- Create the textures table for the enderman, depending on which kind of block
-- the enderman holds (if any).
local create_enderman_textures = function ( block_type , itemstring )
local base = " mobs_mc_enderman.png^mobs_mc_enderman_eyes.png "
--[[ Order of the textures in the texture table:
Flower , 90 degrees
Flower , 45 degrees
Held block , backside
Held block , bottom
Held block , front
Held block , left
Held block , right
Held block , top
Enderman texture ( base )
] ]
-- Regular cube
if block_type == " cube " then
local tiles = minetest.registered_nodes [ itemstring ] . tiles
local textures = { }
local last
2022-05-25 14:57:59 +02:00
if block_texture_overrides [ itemstring ] then
2017-08-23 04:34:06 +02:00
-- Texture override available? Use these instead!
2022-05-25 14:57:59 +02:00
textures = block_texture_overrides [ itemstring ]
2017-08-23 04:34:06 +02:00
else
-- Extract the texture names
for i = 1 , 6 do
if type ( tiles [ i ] ) == " string " then
last = tiles [ i ]
elseif type ( tiles [ i ] ) == " table " then
if tiles [ i ] . name then
last = tiles [ i ] . name
end
2017-08-23 03:50:22 +02:00
end
2017-08-23 04:34:06 +02:00
table.insert ( textures , last )
2017-08-23 03:50:22 +02:00
end
end
return {
" blank.png " ,
" blank.png " ,
textures [ 5 ] ,
textures [ 2 ] ,
textures [ 6 ] ,
textures [ 3 ] ,
textures [ 4 ] ,
textures [ 1 ] ,
base , -- Enderman texture
}
-- Node of plantlike drawtype, 45° (recommended)
elseif block_type == " plantlike45 " then
local textures = minetest.registered_nodes [ itemstring ] . tiles
return {
" blank.png " ,
textures [ 1 ] ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
base ,
}
-- Node of plantlike drawtype, 90°
elseif block_type == " plantlike90 " then
local textures = minetest.registered_nodes [ itemstring ] . tiles
return {
textures [ 1 ] ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
base ,
}
elseif block_type == " unknown " then
return {
" blank.png " ,
" blank.png " ,
" unknown_node.png " ,
" unknown_node.png " ,
" unknown_node.png " ,
" unknown_node.png " ,
" unknown_node.png " ,
" unknown_node.png " ,
base , -- Enderman texture
}
-- No block held (for initial texture)
elseif block_type == " nothing " or block_type == nil then
return {
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
" blank.png " ,
base , -- Enderman texture
}
end
end
-- Select a new animation definition.
local select_enderman_animation = function ( animation_type )
-- Enderman holds a block
if animation_type == " block " then
return {
walk_speed = 25 ,
run_speed = 50 ,
stand_speed = 25 ,
stand_start = 200 ,
stand_end = 200 ,
walk_start = 161 ,
walk_end = 200 ,
run_start = 161 ,
run_end = 200 ,
punch_start = 121 ,
punch_end = 160 ,
}
-- Enderman doesn't hold a block
elseif animation_type == " normal " or animation_type == nil then
return {
walk_speed = 25 ,
run_speed = 50 ,
stand_speed = 25 ,
stand_start = 40 ,
stand_end = 80 ,
walk_start = 0 ,
walk_end = 40 ,
run_start = 0 ,
run_end = 40 ,
punch_start = 81 ,
punch_end = 120 ,
}
end
end
2020-07-20 00:32:33 +02:00
local mobs_griefing = minetest.settings : get_bool ( " mobs_griefing " ) ~= false
2022-08-26 04:03:20 +02:00
local spawners = { }
2018-03-25 22:27:06 +02:00
2022-05-25 14:44:49 +02:00
mcl_mobs : register_mob ( " mobs_mc:enderman " , {
2021-04-25 17:30:15 +02:00
description = S ( " Enderman " ) ,
2018-12-22 14:29:45 +01:00
type = " monster " ,
2020-04-11 02:46:03 +02:00
spawn_class = " passive " ,
2022-02-13 21:40:12 +01:00
passive = true ,
pathfinding = 1 ,
2017-07-05 03:15:46 +02:00
hp_min = 40 ,
hp_max = 40 ,
2020-12-06 15:46:42 +01:00
xp_min = 5 ,
xp_max = 5 ,
2017-07-05 03:15:46 +02:00
collisionbox = { - 0.3 , - 0.01 , - 0.3 , 0.3 , 2.89 , 0.3 } ,
visual = " mesh " ,
mesh = " mobs_mc_enderman.b3d " ,
2017-08-23 03:50:22 +02:00
textures = create_enderman_textures ( ) ,
2017-07-05 03:15:46 +02:00
visual_size = { x = 3 , y = 3 } ,
makes_footstep_sound = true ,
2022-10-05 20:14:01 +02:00
on_spawn = function ( self )
local spider_eyes = false
for n = 1 , # self.object : get_children ( ) do
local obj = self.object : get_children ( ) [ n ]
if obj : get_luaentity ( ) and self.object : get_luaentity ( ) . name == " mobs_mc:ender_eyes " then
spider_eyes = true
end
end
if not spider_eyes then
minetest.add_entity ( self.object : get_pos ( ) , " mobs_mc:ender_eyes " ) : set_attach ( self.object , " head.low " , vector.new ( 0 , 3.25 , - 1.98 ) , vector.new ( 90 , 0 , 180 ) )
minetest.add_entity ( self.object : get_pos ( ) , " mobs_mc:ender_eyes " ) : set_attach ( self.object , " head.low " , vector.new ( 1 , 3.25 , - 1.98 ) , vector.new ( 90 , 0 , 180 ) )
end
end ,
2017-07-05 03:15:46 +02:00
sounds = {
2020-12-10 17:52:14 +01:00
-- TODO: Custom war cry sound
2017-07-05 03:15:46 +02:00
war_cry = " mobs_sandmonster " ,
2020-12-10 17:52:14 +01:00
death = { name = " mobs_mc_enderman_death " , gain = 0.7 } ,
damage = { name = " mobs_mc_enderman_hurt " , gain = 0.5 } ,
random = { name = " mobs_mc_enderman_random " , gain = 0.5 } ,
2017-07-05 03:15:46 +02:00
distance = 16 ,
} ,
2022-02-13 21:40:12 +01:00
walk_velocity = 0.2 ,
run_velocity = 3.4 ,
2017-07-05 03:15:46 +02:00
damage = 7 ,
2017-07-26 00:26:18 +02:00
reach = 2 ,
2017-07-05 03:15:46 +02:00
drops = {
2022-05-25 23:25:15 +02:00
{ name = " mcl_throwing:ender_pearl " ,
2017-07-05 03:15:46 +02:00
chance = 1 ,
min = 0 ,
2020-12-23 17:41:42 +01:00
max = 1 ,
looting = " common " } ,
2017-07-05 03:15:46 +02:00
} ,
2017-08-23 03:50:22 +02:00
animation = select_enderman_animation ( " normal " ) ,
2017-07-05 03:15:46 +02:00
_taken_node = " " ,
2022-07-21 03:07:03 +02:00
can_spawn = function ( pos )
return # minetest.find_nodes_in_area ( vector.offset ( pos , 0 , 1 , 0 ) , vector.offset ( pos , 0 , 3 , 0 ) , { " air " } ) > 2
end ,
2017-07-05 03:15:46 +02:00
do_custom = function ( self , dtime )
2020-06-08 07:51:48 +02:00
-- PARTICLE BEHAVIOUR HERE.
local enderpos = self.object : get_pos ( )
2022-08-26 04:03:20 +02:00
if self._particle_timer and self._particle_timer >= 1 then
for _ , player in pairs ( minetest.get_connected_players ( ) ) do
if not spawners [ player ] then spawners [ player ] = { } end
local dst = vector.distance ( player : get_pos ( ) , enderpos )
if dst < 128 and not spawners [ player ] [ self.object ] then
self._particle_timer = 0
spawners [ player ] [ self.object ] = minetest.add_particlespawner ( {
amount = 5 ,
minpos = vector.new ( - 0.6 , 0 , - 0.6 ) ,
maxpos = vector.new ( 0.6 , 3 , 0.6 ) ,
minvel = vector.new ( - 0.25 , - 0.25 , - 0.25 ) ,
maxvel = vector.new ( 0.25 , 0.25 , 0.25 ) ,
minacc = vector.new ( - 0.5 , - 0.5 , - 0.5 ) ,
maxacc = vector.new ( 0.5 , 0.5 , 0.5 ) ,
minexptime = 0.2 ,
maxexptime = 3 ,
minsize = 0.2 ,
maxsize = 1.2 ,
collisiondetection = true ,
vertical = false ,
time = 0 ,
texture = " mcl_portals_particle " .. math.random ( 1 , 5 ) .. " .png " ,
attached = self.object ,
playername = player : get_player_name ( ) ,
} )
elseif dst > 128 and spawners [ player ] [ self.object ] then
minetest.delete_particlespawner ( spawners [ player ] [ self.object ] )
spawners [ player ] [ self.object ] = nil
end
end
elseif not self._particle_timer then
self._particle_timer = 0
2020-06-08 07:51:48 +02:00
end
2022-08-26 04:03:20 +02:00
self._particle_timer = self._particle_timer + dtime
2020-06-08 07:51:48 +02:00
-- RAIN DAMAGE / EVASIVE WARP BEHAVIOUR HERE.
2022-08-26 04:03:20 +02:00
enderpos = self.object : get_pos ( )
2021-02-26 04:48:37 +01:00
local dim = mcl_worlds.pos_to_dimension ( enderpos )
if dim == " overworld " then
if mcl_weather.state == " rain " or mcl_weather.state == " lightning " then
local damage = true
local enderpos = self.object : get_pos ( )
enderpos.y = enderpos.y + 2.89
local height = { x = enderpos.x , y = enderpos.y + 512 , z = enderpos.z }
local ray = minetest.raycast ( enderpos , height , true )
-- Check for blocks above enderman.
for pointed_thing in ray do
if pointed_thing.type == " node " then
local nn = minetest.get_node ( minetest.get_pointed_thing_position ( pointed_thing ) ) . name
local def = minetest.registered_nodes [ nn ]
if ( not def ) or def.walkable then
-- There's a node in the way. Delete arrow without damage
damage = false
break
end
2020-06-08 07:51:48 +02:00
end
end
2020-07-20 00:32:33 +02:00
2021-02-26 04:48:37 +01:00
if damage == true then
self.state = " "
--rain hurts enderman
self.object : punch ( self.object , 1.0 , {
full_punch_interval = 1.0 ,
damage_groups = { fleshy = self._damage } ,
} , nil )
--randomly teleport hopefully under something.
self : teleport ( nil )
end
2020-06-08 07:51:48 +02:00
end
2021-02-26 04:48:37 +01:00
else return end
2020-06-08 07:51:48 +02:00
-- AGRESSIVELY WARP/CHASE PLAYER BEHAVIOUR HERE.
if self.state == " attack " then
2022-08-13 23:16:58 +02:00
if self.attack then
local target = self.attack
local pos = target : get_pos ( )
if pos ~= nil then
if vector.distance ( self.object : get_pos ( ) , target : get_pos ( ) ) > 10 then
self : teleport ( target )
2020-06-13 21:51:27 +02:00
end
end
2022-08-13 23:16:58 +02:00
end
else --if not attacking try to tp to the dark
2022-08-31 11:56:35 +02:00
local light = minetest.get_node_light ( enderpos )
if light and light > minetest.LIGHT_MAX then
2022-08-13 23:16:58 +02:00
self : teleport ( nil )
end
2020-06-08 07:51:48 +02:00
end
2020-06-13 21:51:27 +02:00
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
-- Check for arrows and people nearby.
2022-08-13 23:16:58 +02:00
enderpos = self.object : get_pos ( )
2021-04-07 04:22:05 +02:00
enderpos.y = enderpos.y + 1.5
local objs = minetest.get_objects_inside_radius ( enderpos , 2 )
2020-06-08 07:51:48 +02:00
for n = 1 , # objs do
2020-07-11 11:53:58 +02:00
local obj = objs [ n ]
2020-06-08 07:51:48 +02:00
if obj then
2022-02-13 21:40:12 +01:00
if minetest.is_player ( obj ) then
2020-06-13 21:51:27 +02:00
-- Warp from players during day.
2021-01-05 20:12:50 +01:00
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
-- self:teleport(nil)
--end
2022-02-13 21:40:12 +01:00
else
2020-07-11 11:53:58 +02:00
local lua = obj : get_luaentity ( )
2020-06-13 21:51:27 +02:00
if lua then
2021-04-07 04:22:05 +02:00
if lua.name == " mcl_bows:arrow_entity " or lua.name == " mcl_throwing:snowball_entity " then
2020-06-13 21:51:27 +02:00
self : teleport ( nil )
end
2020-06-08 07:51:48 +02:00
end
end
end
end
-- PROVOKED BEHAVIOUR HERE.
local enderpos = self.object : get_pos ( )
if self.provoked == " broke_contact " then
self.provoked = " false "
2021-01-05 20:12:50 +01:00
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
-- self:teleport(nil)
-- self.state = ""
--else
2022-02-13 21:40:12 +01:00
if self.attack ~= nil and not minetest.settings : get_bool ( " creative_mode " ) then
self.state = ' attack '
2020-06-13 21:51:27 +02:00
end
2021-01-05 20:12:50 +01:00
--end
2020-06-08 07:51:48 +02:00
end
-- Check to see if people are near by enough to look at us.
2021-04-07 02:24:46 +02:00
for _ , obj in pairs ( minetest.get_connected_players ( ) ) do
2021-04-25 17:30:15 +02:00
2021-04-07 03:13:20 +02:00
--check if they are within radius
2021-04-07 02:24:46 +02:00
local player_pos = obj : get_pos ( )
2021-04-07 03:13:20 +02:00
if player_pos then -- prevent crashing in 1 in a million scenario
2021-04-07 02:37:48 +02:00
2021-04-07 03:13:20 +02:00
local ender_distance = vector.distance ( enderpos , player_pos )
if ender_distance <= 64 then
-- Check if they are looking at us.
local look_dir_not_normalized = obj : get_look_dir ( )
local look_dir = vector.normalize ( look_dir_not_normalized )
local player_eye_height = obj : get_properties ( ) . eye_height
--skip player if they have no data - log it
if not player_eye_height then
minetest.log ( " error " , " Enderman at location: " .. dump ( enderpos ) .. " has indexed a null player! " )
2021-04-10 23:59:13 +02:00
else
2021-04-07 03:13:20 +02:00
2021-04-10 23:59:13 +02:00
--calculate very quickly the exact location the player is looking
--within the distance between the two "heads" (player and enderman)
local look_pos = vector.new ( player_pos.x , player_pos.y + player_eye_height , player_pos.z )
local look_pos_base = look_pos
local ender_eye_pos = vector.new ( enderpos.x , enderpos.y + 2.75 , enderpos.z )
local eye_distance_from_player = vector.distance ( ender_eye_pos , look_pos )
look_pos = vector.add ( look_pos , vector.multiply ( look_dir , eye_distance_from_player ) )
2021-04-25 17:30:15 +02:00
2021-04-10 23:59:13 +02:00
--if looking in general head position, turn hostile
if minetest.line_of_sight ( ender_eye_pos , look_pos_base ) and vector.distance ( look_pos , ender_eye_pos ) <= 0.4 then
self.provoked = " staring "
2022-02-13 21:40:12 +01:00
self.attack = minetest.get_player_by_name ( obj : get_player_name ( ) )
2021-04-10 23:59:13 +02:00
break
2022-02-13 21:40:12 +01:00
else -- I'm not sure what this part does, but I don't want to break anything - jordan4ibanez
2021-04-10 23:59:13 +02:00
if self.provoked == " staring " then
self.provoked = " broke_contact "
2021-04-25 17:30:15 +02:00
end
2021-04-10 23:59:13 +02:00
end
2021-04-07 03:13:20 +02:00
2021-04-10 23:59:13 +02:00
end
2020-06-08 07:51:48 +02:00
end
end
2020-07-20 00:32:33 +02:00
end
2020-06-08 07:51:48 +02:00
-- TAKE AND PLACE STUFF BEHAVIOUR BELOW.
2018-03-25 22:27:06 +02:00
if not mobs_griefing then
return
end
2017-07-05 03:15:46 +02:00
-- Take and put nodes
2017-09-13 23:13:47 +02:00
if not self._take_place_timer or not self._next_take_place_time then
2017-07-05 03:15:46 +02:00
self._take_place_timer = 0
2017-09-13 23:13:47 +02:00
self._next_take_place_time = math.random ( take_frequency_min , take_frequency_max )
2017-07-05 03:15:46 +02:00
return
end
self._take_place_timer = self._take_place_timer + dtime
2017-09-13 23:13:47 +02:00
if ( self._taken_node == nil or self._taken_node == " " ) and self._take_place_timer >= s elf._next_take_place_time then
2017-07-05 03:15:46 +02:00
-- Take random node
self._take_place_timer = 0
2017-09-13 23:13:47 +02:00
self._next_take_place_time = math.random ( place_frequency_min , place_frequency_max )
2019-02-01 06:33:07 +01:00
local pos = self.object : get_pos ( )
2022-05-25 14:57:59 +02:00
local takable_nodes = minetest.find_nodes_in_area_under_air ( { x = pos.x - 2 , y = pos.y - 1 , z = pos.z - 2 } , { x = pos.x + 2 , y = pos.y + 1 , z = pos.z + 2 } , " group:enderman_takable " )
2017-07-05 03:15:46 +02:00
if # takable_nodes >= 1 then
local r = pr : next ( 1 , # takable_nodes )
local take_pos = takable_nodes [ r ]
local node = minetest.get_node ( take_pos )
2020-06-12 08:22:01 +02:00
-- Don't destroy protected stuff.
if not minetest.is_protected ( take_pos , " " ) then
2022-04-22 01:02:20 +02:00
minetest.remove_node ( take_pos )
local dug = minetest.get_node_or_nil ( take_pos )
if dug and dug.name == " air " then
2022-05-25 14:57:59 +02:00
self._taken_node = node.name
2020-06-12 08:22:01 +02:00
local def = minetest.registered_nodes [ self._taken_node ]
-- Update animation and texture accordingly (adds visibly carried block)
local block_type
-- Cube-shaped
if def.drawtype == " normal " or
def.drawtype == " nodebox " or
def.drawtype == " liquid " or
def.drawtype == " flowingliquid " or
def.drawtype == " glasslike " or
def.drawtype == " glasslike_framed " or
def.drawtype == " glasslike_framed_optional " or
def.drawtype == " allfaces " or
def.drawtype == " allfaces_optional " or
def.drawtype == nil then
block_type = " cube "
elseif def.drawtype == " plantlike " then
-- Flowers and stuff
block_type = " plantlike45 "
elseif def.drawtype == " airlike " then
-- Just air
block_type = nil
else
-- Fallback for complex drawtypes
block_type = " unknown "
end
self.base_texture = create_enderman_textures ( block_type , self._taken_node )
self.object : set_properties ( { textures = self.base_texture } )
self.animation = select_enderman_animation ( " block " )
2022-05-25 14:44:49 +02:00
mcl_mobs : set_animation ( self , self.animation . current )
2020-06-12 08:22:01 +02:00
if def.sounds and def.sounds . dug then
minetest.sound_play ( def.sounds . dug , { pos = take_pos , max_hear_distance = 16 } , true )
end
2017-07-05 03:15:46 +02:00
end
end
end
2022-02-13 21:40:12 +01:00
elseif self._taken_node ~= nil and self._taken_node ~= " " and self._take_place_timer >= self._next_take_place_time then
2017-07-05 03:15:46 +02:00
-- Place taken node
self._take_place_timer = 0
2017-09-13 23:13:47 +02:00
self._next_take_place_time = math.random ( take_frequency_min , take_frequency_max )
2019-02-01 06:33:07 +01:00
local pos = self.object : get_pos ( )
2017-07-05 03:15:46 +02:00
local yaw = self.object : get_yaw ( )
-- Place node at looking direction
local place_pos = vector.subtract ( pos , minetest.facedir_to_dir ( minetest.dir_to_facedir ( minetest.yaw_to_dir ( yaw ) ) ) )
2020-06-12 08:22:01 +02:00
-- Also check to see if protected.
if minetest.get_node ( place_pos ) . name == " air " and not minetest.is_protected ( place_pos , " " ) then
2017-07-05 03:15:46 +02:00
-- ... but only if there's a free space
2017-08-23 04:53:36 +02:00
local success = minetest.place_node ( place_pos , { name = self._taken_node } )
if success then
local def = minetest.registered_nodes [ self._taken_node ]
-- Update animation accordingly (removes visible block)
self.animation = select_enderman_animation ( " normal " )
2022-05-25 14:44:49 +02:00
mcl_mobs : set_animation ( self , self.animation . current )
2017-08-23 04:53:36 +02:00
if def.sounds and def.sounds . place then
2020-04-07 00:55:45 +02:00
minetest.sound_play ( def.sounds . place , { pos = place_pos , max_hear_distance = 16 } , true )
2017-08-23 04:53:36 +02:00
end
self._taken_node = " "
2017-07-05 03:15:46 +02:00
end
end
end
end ,
2020-06-08 07:51:48 +02:00
do_teleport = function ( self , target )
2022-02-13 21:40:12 +01:00
if target ~= nil then
2020-06-08 07:51:48 +02:00
local target_pos = target : get_pos ( )
-- Find all solid nodes below air in a 10× 10× 10 cuboid centered on the target
local nodes = minetest.find_nodes_in_area_under_air ( vector.subtract ( target_pos , 5 ) , vector.add ( target_pos , 5 ) , { " group:solid " , " group:cracky " , " group:crumbly " } )
local telepos
2022-02-13 21:40:12 +01:00
if nodes ~= nil then
2020-06-17 06:59:16 +02:00
if # nodes > 0 then
-- Up to 64 attempts to teleport
for n = 1 , math.min ( 64 , # nodes ) do
local r = pr : next ( 1 , # nodes )
local nodepos = nodes [ r ]
local node_ok = true
-- Selected node needs to have 3 nodes of free space above
for u = 1 , 3 do
local node = minetest.get_node ( { x = nodepos.x , y = nodepos.y + u , z = nodepos.z } )
2022-03-09 02:05:38 +01:00
local ndef = minetest.registered_nodes [ node.name ]
if ndef and ndef.walkable then
2020-06-17 06:59:16 +02:00
node_ok = false
break
end
end
if node_ok then
telepos = { x = nodepos.x , y = nodepos.y + 1 , z = nodepos.z }
2020-06-08 07:51:48 +02:00
end
end
2020-06-17 06:59:16 +02:00
if telepos then
2020-12-10 16:35:48 +01:00
telesound ( self.object : get_pos ( ) , false )
2020-06-17 06:59:16 +02:00
self.object : set_pos ( telepos )
2020-12-10 16:35:48 +01:00
telesound ( telepos , true )
2017-07-05 03:15:46 +02:00
end
end
end
2020-06-08 07:51:48 +02:00
else
-- Attempt to randomly teleport enderman
local pos = self.object : get_pos ( )
2020-06-19 01:16:00 +02:00
-- Up to 8 top-level attempts to teleport
for n = 1 , 8 do
local node_ok = false
-- We need to add (or subtract) different random numbers to each vector component, so it couldn't be done with a nice single vector.add() or .subtract():
local randomCube = vector.new ( pos.x + 8 * ( pr : next ( 0 , 16 ) - 8 ) , pos.y + 8 * ( pr : next ( 0 , 16 ) - 8 ) , pos.z + 8 * ( pr : next ( 0 , 16 ) - 8 ) )
local nodes = minetest.find_nodes_in_area_under_air ( vector.subtract ( randomCube , 4 ) , vector.add ( randomCube , 4 ) , { " group:solid " , " group:cracky " , " group:crumbly " } )
2022-02-13 21:40:12 +01:00
if nodes ~= nil then
2020-06-19 01:16:00 +02:00
if # nodes > 0 then
-- Up to 8 low-level (in total up to 8*8 = 64) attempts to teleport
for n = 1 , math.min ( 8 , # nodes ) do
local r = pr : next ( 1 , # nodes )
local nodepos = nodes [ r ]
node_ok = true
for u = 1 , 3 do
local node = minetest.get_node ( { x = nodepos.x , y = nodepos.y + u , z = nodepos.z } )
2022-03-09 02:05:38 +01:00
local ndef = minetest.registered_nodes [ node.name ]
if ndef and ndef.walkable then
2020-06-19 01:16:00 +02:00
node_ok = false
break
end
end
if node_ok then
2020-12-10 16:35:48 +01:00
telesound ( self.object : get_pos ( ) , false )
local telepos = { x = nodepos.x , y = nodepos.y + 1 , z = nodepos.z }
self.object : set_pos ( telepos )
telesound ( telepos , true )
2020-06-17 06:59:16 +02:00
break
end
end
2020-06-08 07:51:48 +02:00
end
end
2020-06-19 01:16:00 +02:00
if node_ok then
break
end
2017-07-05 03:15:46 +02:00
end
end
end ,
on_die = function ( self , pos )
-- Drop carried node on death
2022-02-13 21:40:12 +01:00
if self._taken_node ~= nil and self._taken_node ~= " " then
2017-07-05 03:15:46 +02:00
minetest.add_item ( pos , self._taken_node )
end
end ,
2020-06-08 07:51:48 +02:00
do_punch = function ( self , hitter , tflp , tool_caps , dir )
-- damage from rain caused by itself so we don't want it to attack itself.
2022-02-13 21:40:12 +01:00
if hitter ~= self.object and hitter ~= nil then
2021-01-05 20:12:50 +01:00
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
-- self:teleport(nil)
--else
2021-04-02 15:04:21 +02:00
if pr : next ( 1 , 8 ) == 8 then --FIXME: real mc rate
2020-06-13 21:51:27 +02:00
self : teleport ( hitter )
2021-04-02 15:04:21 +02:00
end
self.attack = hitter
self.state = " attack "
2021-01-05 20:12:50 +01:00
--end
2020-06-08 07:51:48 +02:00
end
end ,
2020-08-05 18:33:53 +02:00
armor = { fleshy = 100 , water_vulnerable = 100 } ,
2017-07-05 03:15:46 +02:00
water_damage = 8 ,
2020-06-08 07:51:48 +02:00
view_range = 64 ,
2017-07-05 03:15:46 +02:00
fear_height = 4 ,
2022-02-13 21:40:12 +01:00
attack_type = " dogfight " ,
2017-07-05 03:15:46 +02:00
} )
2022-08-26 04:03:20 +02:00
minetest.register_on_leaveplayer ( function ( player )
2022-09-01 11:40:23 +02:00
if not spawners [ player ] then return end
2022-08-26 04:03:20 +02:00
for _ , s in pairs ( spawners [ player ] ) do
minetest.delete_particlespawner ( s )
end
spawners [ player ] = nil
end )
2017-07-05 03:15:46 +02:00
2017-08-16 22:08:17 +02:00
-- End spawn
2022-05-25 14:44:49 +02:00
mcl_mobs : spawn_specific (
2021-04-25 17:30:15 +02:00
" mobs_mc:enderman " ,
" end " ,
2021-04-08 13:39:18 +02:00
" ground " ,
{
2022-09-11 19:55:13 +02:00
" End " ,
" EndIsland " ,
" EndMidlands " ,
" EndBarrens " ,
" EndBorder " ,
" EndSmallIslands "
2021-04-08 13:39:18 +02:00
} ,
2021-04-25 17:30:15 +02:00
0 ,
minetest.LIGHT_MAX + 1 ,
30 ,
3000 ,
12 ,
2022-05-25 23:25:15 +02:00
mcl_vars.mg_end_min ,
mcl_vars.mg_end_max )
2017-08-16 22:08:17 +02:00
-- Overworld spawn
2022-05-25 14:44:49 +02:00
mcl_mobs : spawn_specific (
2021-04-25 17:30:15 +02:00
" mobs_mc:enderman " ,
" overworld " ,
2021-04-08 13:39:18 +02:00
" ground " ,
{
" Mesa " ,
" FlowerForest " ,
" Swampland " ,
" Taiga " ,
" ExtremeHills " ,
" Jungle " ,
" Savanna " ,
" BirchForest " ,
" MegaSpruceTaiga " ,
" MegaTaiga " ,
" ExtremeHills+ " ,
" Forest " ,
" Plains " ,
" Desert " ,
" ColdTaiga " ,
" IcePlainsSpikes " ,
" SunflowerPlains " ,
" IcePlains " ,
" RoofedForest " ,
" ExtremeHills+_snowtop " ,
" MesaPlateauFM_grasstop " ,
" JungleEdgeM " ,
" ExtremeHillsM " ,
" JungleM " ,
" BirchForestM " ,
" MesaPlateauF " ,
" MesaPlateauFM " ,
" MesaPlateauF_grasstop " ,
" MesaBryce " ,
" JungleEdge " ,
" SavannaM " ,
" FlowerForest_beach " ,
" Forest_beach " ,
" StoneBeach " ,
" ColdTaiga_beach_water " ,
" Taiga_beach " ,
" Savanna_beach " ,
" Plains_beach " ,
" ExtremeHills_beach " ,
" ColdTaiga_beach " ,
" Swampland_shore " ,
" JungleM_shore " ,
" Jungle_shore " ,
" MesaPlateauFM_sandlevel " ,
" MesaPlateauF_sandlevel " ,
" MesaBryce_sandlevel " ,
" Mesa_sandlevel " ,
" RoofedForest_ocean " ,
" JungleEdgeM_ocean " ,
" BirchForestM_ocean " ,
" BirchForest_ocean " ,
" IcePlains_deep_ocean " ,
" Jungle_deep_ocean " ,
" Savanna_ocean " ,
" MesaPlateauF_ocean " ,
" ExtremeHillsM_deep_ocean " ,
" Savanna_deep_ocean " ,
" SunflowerPlains_ocean " ,
" Swampland_deep_ocean " ,
" Swampland_ocean " ,
" MegaSpruceTaiga_deep_ocean " ,
" ExtremeHillsM_ocean " ,
" JungleEdgeM_deep_ocean " ,
" SunflowerPlains_deep_ocean " ,
" BirchForest_deep_ocean " ,
" IcePlainsSpikes_ocean " ,
" Mesa_ocean " ,
" StoneBeach_ocean " ,
" Plains_deep_ocean " ,
" JungleEdge_deep_ocean " ,
" SavannaM_deep_ocean " ,
" Desert_deep_ocean " ,
" Mesa_deep_ocean " ,
" ColdTaiga_deep_ocean " ,
" Plains_ocean " ,
" MesaPlateauFM_ocean " ,
" Forest_deep_ocean " ,
" JungleM_deep_ocean " ,
" FlowerForest_deep_ocean " ,
" MegaTaiga_ocean " ,
" StoneBeach_deep_ocean " ,
" IcePlainsSpikes_deep_ocean " ,
" ColdTaiga_ocean " ,
" SavannaM_ocean " ,
" MesaPlateauF_deep_ocean " ,
" MesaBryce_deep_ocean " ,
" ExtremeHills+_deep_ocean " ,
" ExtremeHills_ocean " ,
" Forest_ocean " ,
" MegaTaiga_deep_ocean " ,
" JungleEdge_ocean " ,
" MesaBryce_ocean " ,
" MegaSpruceTaiga_ocean " ,
" ExtremeHills+_ocean " ,
" Jungle_ocean " ,
" RoofedForest_deep_ocean " ,
" IcePlains_ocean " ,
" FlowerForest_ocean " ,
" ExtremeHills_deep_ocean " ,
" MesaPlateauFM_deep_ocean " ,
" Desert_ocean " ,
" Taiga_ocean " ,
" BirchForestM_deep_ocean " ,
" Taiga_deep_ocean " ,
" JungleM_ocean " ,
" FlowerForest_underground " ,
" JungleEdge_underground " ,
" StoneBeach_underground " ,
" MesaBryce_underground " ,
" Mesa_underground " ,
" RoofedForest_underground " ,
" Jungle_underground " ,
" Swampland_underground " ,
" BirchForest_underground " ,
" Plains_underground " ,
" MesaPlateauF_underground " ,
" ExtremeHills_underground " ,
" MegaSpruceTaiga_underground " ,
" BirchForestM_underground " ,
" SavannaM_underground " ,
" MesaPlateauFM_underground " ,
" Desert_underground " ,
" Savanna_underground " ,
" Forest_underground " ,
" SunflowerPlains_underground " ,
" ColdTaiga_underground " ,
" IcePlains_underground " ,
" IcePlainsSpikes_underground " ,
" MegaTaiga_underground " ,
" Taiga_underground " ,
" ExtremeHills+_underground " ,
" JungleM_underground " ,
" ExtremeHillsM_underground " ,
" JungleEdgeM_underground " ,
} ,
2021-04-25 17:30:15 +02:00
0 ,
7 ,
30 ,
19000 ,
2 ,
2022-05-25 23:25:15 +02:00
mcl_vars.mg_overworld_min ,
mcl_vars.mg_overworld_max )
2021-04-08 13:39:18 +02:00
2017-08-16 22:08:17 +02:00
-- Nether spawn (rare)
2022-05-25 14:44:49 +02:00
mcl_mobs : spawn_specific (
2021-04-25 17:30:15 +02:00
" mobs_mc:enderman " ,
" nether " ,
2021-04-08 13:39:18 +02:00
" ground " ,
{
2022-06-18 16:28:03 +02:00
" Nether " ,
2022-09-11 19:55:13 +02:00
" SoulsandValley " ,
2021-04-08 13:39:18 +02:00
} ,
2021-04-25 17:30:15 +02:00
0 ,
2022-07-17 15:33:06 +02:00
11 ,
2021-04-25 17:30:15 +02:00
30 ,
27500 ,
4 ,
2022-05-25 23:25:15 +02:00
mcl_vars.mg_nether_min ,
mcl_vars.mg_nether_max )
2017-07-05 03:15:46 +02:00
2022-06-18 16:28:03 +02:00
-- Warped Forest spawn (common)
mcl_mobs : spawn_specific (
" mobs_mc:enderman " ,
" nether " ,
" ground " ,
{
" WarpedForest "
} ,
0 ,
2022-07-17 15:33:06 +02:00
11 ,
2022-06-18 16:28:03 +02:00
30 ,
5000 ,
4 ,
mcl_vars.mg_nether_min ,
mcl_vars.mg_nether_max )
2017-07-05 03:15:46 +02:00
-- spawn eggs
2022-10-03 19:52:22 +02:00
mcl_mobs : register_egg ( " mobs_mc:enderman " , S ( " Enderman " ) , " #252525 " , " #151515 " , 0 )