2022-11-09 02:59:46 +01:00
mcl_mobs = { }
mcl_mobs.mob_class = { }
mcl_mobs.mob_class_meta = { __index = mcl_mobs.mob_class }
2022-11-12 02:16:37 +01:00
mcl_mobs.registered_mobs = { }
2022-11-10 21:10:34 +01:00
local modname = minetest.get_current_modname ( )
local path = minetest.get_modpath ( modname )
local S = minetest.get_translator ( modname )
2022-11-10 22:59:42 +01:00
mcl_mobs.fallback_node = minetest.registered_aliases [ " mapgen_dirt " ] or " mcl_core:dirt "
2024-10-27 19:24:40 +01:00
-- used by the libaries below.
-- get node but use fallback for nil or unknown
local node_ok = function ( pos , fallback )
fallback = fallback or mcl_mobs.fallback_node
local node = minetest.get_node_or_nil ( pos )
if node and minetest.registered_nodes [ node.name ] then
return node
end
return minetest.registered_nodes [ fallback ]
end
mcl_mobs.node_ok = node_ok
2022-11-10 00:52:45 +01:00
--api and helpers
2022-11-10 20:44:31 +01:00
-- effects: sounds and particles mostly
2022-11-09 02:59:46 +01:00
dofile ( path .. " /effects.lua " )
2022-11-10 20:44:31 +01:00
-- physics: involuntary mob movement - particularly falling and death
2022-11-09 02:59:46 +01:00
dofile ( path .. " /physics.lua " )
2022-11-10 20:44:31 +01:00
-- movement: general voluntary mob movement, walking avoiding cliffs etc.
2022-11-10 15:28:51 +01:00
dofile ( path .. " /movement.lua " )
2022-11-10 20:44:31 +01:00
-- items: item management for mobs
2022-11-09 15:35:51 +01:00
dofile ( path .. " /items.lua " )
2022-11-10 20:44:31 +01:00
-- pathfinding: pathfinding to target positions
2022-11-10 00:52:45 +01:00
dofile ( path .. " /pathfinding.lua " )
2022-11-10 20:44:31 +01:00
-- combat: attack logic
2022-11-10 15:28:51 +01:00
dofile ( path .. " /combat.lua " )
2024-10-27 19:24:40 +01:00
-- the entity functions themselves
2022-02-13 21:40:12 +01:00
dofile ( path .. " /api.lua " )
2018-05-31 18:32:26 +02:00
2022-11-10 00:52:45 +01:00
--utility functions
2022-11-09 04:57:48 +01:00
dofile ( path .. " /breeding.lua " )
2022-02-13 21:40:12 +01:00
dofile ( path .. " /spawning.lua " )
dofile ( path .. " /mount.lua " )
2022-11-09 02:59:46 +01:00
dofile ( path .. " /crafts.lua " )
2022-11-09 06:06:59 +01:00
dofile ( path .. " /compat.lua " )
2022-11-10 21:10:34 +01:00
2022-11-10 22:59:42 +01:00
local DEFAULT_FALL_SPEED = - 9.81 * 1.5
local MAX_MOB_NAME_LENGTH = 30
2022-11-10 21:10:34 +01:00
local old_spawn_icons = minetest.settings : get_bool ( " mcl_old_spawn_icons " , false )
2022-11-11 05:14:54 +01:00
local extended_pet_control = minetest.settings : get_bool ( " mcl_extended_pet_control " , true )
2022-11-10 21:10:34 +01:00
local difficulty = tonumber ( minetest.settings : get ( " mob_difficulty " ) ) or 1.0
--#### REGISTER FUNCS
-- Code to execute before custom on_rightclick handling
local on_rightclick_prefix = function ( self , clicker )
2022-11-11 05:14:54 +01:00
if not clicker : is_player ( ) then return end
2022-11-10 21:10:34 +01:00
local item = clicker : get_wielded_item ( )
2022-11-11 05:14:54 +01:00
if extended_pet_control and self.tamed and self.owner == clicker : get_player_name ( ) then
self : toggle_sit ( clicker )
end
2022-11-10 21:10:34 +01:00
-- Name mob with nametag
if not self.ignores_nametag and item : get_name ( ) == " mcl_mobs:nametag " then
local tag = item : get_meta ( ) : get_string ( " name " )
if tag ~= " " then
if string.len ( tag ) > MAX_MOB_NAME_LENGTH then
tag = string.sub ( tag , 1 , MAX_MOB_NAME_LENGTH )
end
self.nametag = tag
self : update_tag ( )
if not minetest.is_creative_enabled ( clicker : get_player_name ( ) ) then
item : take_item ( )
clicker : set_wielded_item ( item )
end
return true
end
end
2022-11-11 05:14:54 +01:00
2022-11-10 21:10:34 +01:00
return false
end
2022-11-11 05:14:54 +01:00
local create_mob_on_rightclick = function ( on_rightclick )
return function ( self , clicker )
local stop = on_rightclick_prefix ( self , clicker )
if ( not stop ) and ( on_rightclick ) then
on_rightclick ( self , clicker )
end
end
end
2022-11-10 21:10:34 +01:00
-- check if within physical map limits (-30911 to 30927)
local function within_limits ( pos , radius )
local wmin , wmax = - 30912 , 30928
if mcl_vars then
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
wmin , wmax = mcl_vars.mapgen_edge_min , mcl_vars.mapgen_edge_max
end
end
if radius then
wmin = wmin - radius
wmax = wmax + radius
end
for _ , v in pairs ( pos ) do
if v < wmin or v > wmax then return false end
end
return true
end
mcl_mobs.spawning_mobs = { }
-- register mob entity
function mcl_mobs . register_mob ( name , def )
mcl_mobs.spawning_mobs [ name ] = true
2022-11-12 02:16:37 +01:00
mcl_mobs.registered_mobs [ name ] = def
2022-11-10 21:10:34 +01:00
2024-10-27 19:24:40 +01:00
local can_despawn = def.can_despawn
if def.can_despawn == nil then can_despawn = def.spawn_class ~= " passive " end
2022-11-10 21:10:34 +01:00
2022-11-12 02:16:37 +01:00
local function scale_difficulty ( value , default , min , special )
if ( not value ) or ( value == default ) or ( value == special ) then
return default
else
return math.max ( min , value * difficulty )
end
end
2022-11-10 21:10:34 +01:00
2022-11-12 02:16:37 +01:00
local collisionbox = def.collisionbox or { - 0.25 , - 0.25 , - 0.25 , 0.25 , 0.25 , 0.25 }
-- Workaround for <https://github.com/minetest/minetest/issues/5966>:
-- Increase upper Y limit to avoid mobs glitching through solid nodes.
-- FIXME: Remove workaround if it's no longer needed.
if collisionbox [ 5 ] < 0.79 then
collisionbox [ 5 ] = 0.79
end
local final_def = {
use_texture_alpha = def.use_texture_alpha ,
head_swivel = def.head_swivel or nil , -- bool to activate this function
2024-11-10 02:41:55 +01:00
head_yaw_offset = math.rad ( def.head_yaw_offset or 0 ) , -- for wonkey model bones
2022-11-12 02:16:37 +01:00
head_pitch_multiplier = def.head_pitch_multiplier or 1 , --for inverted pitch
2024-11-11 17:29:07 +01:00
head_eye_height = def.head_eye_height or 1 , -- how high approximately the mobs eyes are from the ground to tell the mob how high to look up at the player
head_max_yaw = def.head_max_yaw , -- how far the mob may turn the head
head_max_pitch = def.head_max_pitch , -- how far up and down the mob may pitch the head
head_bone_position = def.head_bone_position or { 0 , def.bone_eye_height or 1.4 , def.horizontal_head_height or 0 } ,
2022-11-12 02:16:37 +01:00
curiosity = def.curiosity or 1 , -- how often mob will look at player on idle
head_yaw = def.head_yaw or " y " , -- axis to rotate head on
wears_armor = def.wears_armor , -- a number value used to index texture slot for armor
stepheight = def.stepheight or 0.6 ,
name = name ,
description = def.description ,
type = def.type ,
attack_type = def.attack_type ,
2023-05-08 17:28:21 +02:00
attack_frequency = def.attack_frequency ,
2022-11-12 02:16:37 +01:00
fly = def.fly or false ,
fly_in = def.fly_in or { " air " , " __airlike " } ,
owner = def.owner or " " ,
order = def.order or " " ,
on_die = def.on_die ,
spawn_small_alternative = def.spawn_small_alternative ,
do_custom = def.do_custom ,
detach_child = def.detach_child ,
jump_height = def.jump_height or 4 , -- was 6
rotate = math.rad ( def.rotate or 0 ) , -- 0=front, 90=side, 180=back, 270=side2
lifetimer = def.lifetimer or 57.73 ,
hp_min = scale_difficulty ( def.hp_min , 5 , 1 ) ,
hp_max = scale_difficulty ( def.hp_max , 10 , 1 ) ,
xp_min = def.xp_min or 0 ,
xp_max = def.xp_max or 0 ,
xp_timestamp = 0 ,
2023-11-23 01:32:23 +01:00
invul_timestamp = 0 ,
2022-11-12 02:16:37 +01:00
breath_max = def.breath_max or 15 ,
breathes_in_water = def.breathes_in_water or false ,
physical = true ,
collisionbox = collisionbox ,
selectionbox = def.selectionbox or def.collisionbox ,
visual = def.visual ,
visual_size = def.visual_size or { x = 1 , y = 1 } ,
mesh = def.mesh ,
makes_footstep_sound = def.makes_footstep_sound or false ,
view_range = def.view_range or 16 ,
walk_velocity = def.walk_velocity or 1 ,
run_velocity = def.run_velocity or 2 ,
damage = scale_difficulty ( def.damage , 0 , 0 ) ,
light_damage = def.light_damage or 0 ,
sunlight_damage = def.sunlight_damage or 0 ,
water_damage = def.water_damage or 0 ,
lava_damage = def.lava_damage or 8 ,
fire_damage = def.fire_damage or 1 ,
suffocation = def.suffocation or true ,
fall_damage = def.fall_damage or 1 ,
fall_speed = def.fall_speed or DEFAULT_FALL_SPEED , -- must be lower than -2
drops = def.drops or { } ,
armor = def.armor or 100 ,
on_rightclick = create_mob_on_rightclick ( def.on_rightclick ) ,
arrow = def.arrow ,
shoot_interval = def.shoot_interval ,
sounds = def.sounds or { } ,
animation = def.animation or { } ,
follow = def.follow ,
nofollow = def.nofollow ,
can_open_doors = def.can_open_doors ,
jump = def.jump ~= false ,
automatic_face_movement_max_rotation_per_sec = 300 ,
walk_chance = def.walk_chance or 50 ,
attacks_monsters = def.attacks_monsters or false ,
group_attack = def.group_attack or false ,
passive = def.passive or false ,
knock_back = def.knock_back ~= false ,
shoot_offset = def.shoot_offset or 0 ,
floats = def.floats or 1 , -- floats in water by default
floats_on_lava = def.floats_on_lava or 0 ,
replace_rate = def.replace_rate ,
replace_what = def.replace_what ,
replace_with = def.replace_with ,
replace_offset = def.replace_offset or 0 ,
on_replace = def.on_replace ,
2023-06-04 22:20:29 +02:00
replace_delay = def.replace_delay or 0 ,
2022-11-12 02:16:37 +01:00
timer = 0 ,
env_damage_timer = 0 ,
tamed = false ,
pause_timer = 0 ,
horny = false ,
hornytimer = 0 ,
gotten = false ,
health = 0 ,
frame_speed_multiplier = 1 ,
reach = def.reach or 3 ,
htimer = 0 ,
texture_list = def.textures ,
child_texture = def.child_texture ,
docile_by_day = def.docile_by_day or false ,
time_of_day = 0.5 ,
fear_height = def.fear_height or 0 ,
runaway = def.runaway ,
runaway_timer = 0 ,
pathfinding = def.pathfinding ,
immune_to = def.immune_to or { } ,
explosion_radius = def.explosion_radius , -- LEGACY
explosion_damage_radius = def.explosion_damage_radius , -- LEGACY
explosiontimer_reset_radius = def.explosiontimer_reset_radius ,
explosion_timer = def.explosion_timer or 3 ,
allow_fuse_reset = def.allow_fuse_reset ~= false ,
stop_to_explode = def.stop_to_explode ~= false ,
custom_attack = def.custom_attack ,
double_melee_attack = def.double_melee_attack ,
dogshoot_switch = def.dogshoot_switch ,
dogshoot_count = 0 ,
dogshoot_count_max = def.dogshoot_count_max or 5 ,
dogshoot_count2_max = def.dogshoot_count2_max or ( def.dogshoot_count_max or 5 ) ,
attack_animals = def.attack_animals or false ,
attack_npcs = def.attack_npcs or false ,
specific_attack = def.specific_attack ,
runaway_from = def.runaway_from ,
owner_loyal = def.owner_loyal ,
facing_fence = false ,
is_mob = true ,
pushable = def.pushable or true ,
2022-11-10 21:10:34 +01:00
2022-11-12 02:16:37 +01:00
-- MCL2 extensions
shooter_avoid_enemy = def.shooter_avoid_enemy ,
strafes = def.strafes ,
avoid_distance = def.avoid_distance or 9 ,
do_teleport = def.do_teleport ,
spawn_class = def.spawn_class ,
can_spawn = def.can_spawn ,
ignores_nametag = def.ignores_nametag or false ,
rain_damage = def.rain_damage or 0 ,
glow = def.glow ,
can_despawn = can_despawn ,
child = def.child or false ,
texture_mods = { } ,
shoot_arrow = def.shoot_arrow ,
sounds_child = def.sounds_child ,
_child_animations = def.child_animations ,
pick_up = def.pick_up ,
explosion_strength = def.explosion_strength ,
suffocation_timer = 0 ,
follow_velocity = def.follow_velocity or 2.4 ,
instant_death = def.instant_death or false ,
fire_resistant = def.fire_resistant or false ,
fire_damage_resistant = def.fire_damage_resistant or false ,
ignited_by_sunlight = def.ignited_by_sunlight or false ,
spawn_in_group = def.spawn_in_group ,
spawn_in_group_min = def.spawn_in_group_min ,
noyaw = def.noyaw or false ,
particlespawners = def.particlespawners ,
2023-09-21 06:53:32 +02:00
spawn_check = def.spawn_check ,
2022-11-12 02:16:37 +01:00
-- End of MCL2 extensions
on_spawn = def.on_spawn ,
on_blast = def.on_blast or function ( self , damage )
self.object : punch ( self.object , 1.0 , {
full_punch_interval = 1.0 ,
damage_groups = { fleshy = damage } ,
} , nil )
return false , true , { }
end ,
do_punch = def.do_punch ,
2023-08-11 16:54:58 +02:00
deal_damage = def.deal_damage ,
2022-11-12 02:16:37 +01:00
on_breed = def.on_breed ,
on_grown = def.on_grown ,
on_pick_up = def.on_pick_up ,
on_activate = function ( self , staticdata , dtime )
--this is a temporary hack so mobs stop
--glitching and acting really weird with the
--default built in engine collision detection
self.is_mob = true
self.object : set_properties ( {
collide_with_objects = false ,
} )
2022-11-10 21:10:34 +01:00
2022-11-12 02:16:37 +01:00
return self : mob_activate ( staticdata , def , dtime )
end ,
2024-05-05 21:05:45 +02:00
after_activate = def.after_activate ,
2023-12-10 17:10:33 +01:00
attack_state = def.attack_state , -- custom attack state
on_attack = def.on_attack , -- called after attack, useful with otherwise predefined attack states (not custom)
2022-11-12 02:16:37 +01:00
harmed_by_heal = def.harmed_by_heal ,
2023-08-08 02:53:01 +02:00
is_boss = def.is_boss ,
dealt_effect = def.dealt_effect ,
2023-08-11 16:54:58 +02:00
on_lightning_strike = def.on_lightning_strike ,
2023-08-23 06:12:18 +02:00
extra_hostile = def.extra_hostile ,
attack_exception = def.attack_exception or function ( p ) return false end ,
2023-08-11 16:54:58 +02:00
_spawner = def._spawner ,
2024-04-22 02:12:54 +02:00
_mcl_potions = { } ,
2022-11-12 02:16:37 +01:00
}
2022-11-10 21:10:34 +01:00
2022-11-12 02:16:37 +01:00
if minetest.get_modpath ( " doc_identifier " ) ~= nil then
doc.sub . identifier.register_object ( name , " basics " , " mobs " )
2024-04-21 10:16:15 +02:00
if def.unused ~= true then
doc.add_entry ( " mobs " , name , {
name = def.description or name ,
data = final_def ,
} )
end
2022-11-12 02:16:37 +01:00
end
2022-11-10 21:10:34 +01:00
2024-04-21 10:16:15 +02:00
minetest.register_entity ( name , setmetatable ( final_def , mcl_mobs.mob_class_meta ) )
2022-11-10 21:10:34 +01:00
end -- END mcl_mobs.register_mob function
2024-06-12 13:26:07 +02:00
2024-06-12 13:36:59 +02:00
local STRIP_FIELDS = { " mesh " , " base_size " , " textures " , " base_mesh " , " base_texture " }
2024-06-15 01:53:03 +02:00
function mcl_mobs . strip_staticdata ( unpacked_staticdata )
2024-06-15 01:50:05 +02:00
-- Strip select fields from the staticdata to prevent conversion issues
for i = 1 , # STRIP_FIELDS do
unpacked_staticdata [ STRIP_FIELDS [ i ] ] = nil
end
end
2024-05-30 10:29:12 +02:00
function mcl_mobs . register_conversion ( old_name , new_name )
minetest.register_entity ( old_name , {
on_activate = function ( self , staticdata , dtime )
2024-06-12 13:26:07 +02:00
local unpacked_staticdata = minetest.deserialize ( staticdata )
2024-06-15 01:50:05 +02:00
mcl_mobs.strip_staticdata ( unpacked_staticdata )
2024-06-12 13:31:06 +02:00
staticdata = minetest.serialize ( unpacked_staticdata )
2024-06-12 13:26:07 +02:00
2024-06-09 21:11:04 +02:00
local old_object = self.object
if not old_object then return end
local pos = old_object : get_pos ( )
if not pos then return end
2024-06-09 21:28:44 +02:00
old_object : remove ( )
2024-06-09 21:11:04 +02:00
local new_object = minetest.add_entity ( pos , new_name , staticdata )
if not new_object then return end
2024-06-09 21:13:36 +02:00
local hook = ( new_object : get_luaentity ( ) or { } ) . _on_after_convert
if hook then hook ( new_object ) end
2024-05-30 10:29:12 +02:00
end ,
_convert_to = new_name ,
} )
end
2022-11-10 21:10:34 +01:00
2023-10-26 21:32:50 +02:00
function mcl_mobs . get_arrow_damage_func ( damage , typ )
local typ = mcl_damage.types [ typ ] and typ or " arrow "
return function ( projectile , object )
return mcl_util.deal_damage ( object , damage , { type = typ } )
end
end
2022-11-10 21:10:34 +01:00
-- register arrow for shoot attack
function mcl_mobs . register_arrow ( name , def )
if not name or not def then return end -- errorcheck
2024-11-14 05:02:58 +01:00
local behaviors = {
vl_projectile.has_owner_grace_distance
}
2024-09-10 16:04:36 +02:00
if def.hit_node then
table.insert ( behaviors , vl_projectile.collides_with_solids )
end
if def.hit_player or def.hit_mob or def.hit_object then
table.insert ( behaviors , vl_projectile.collides_with_entities )
end
2022-11-10 21:10:34 +01:00
2024-09-10 16:04:36 +02:00
vl_projectile.register ( name , {
2022-11-10 21:10:34 +01:00
physical = false ,
visual = def.visual ,
visual_size = def.visual_size ,
textures = def.textures ,
velocity = def.velocity ,
hit_player = def.hit_player ,
hit_node = def.hit_node ,
hit_mob = def.hit_mob ,
hit_object = def.hit_object ,
2023-11-15 18:59:46 +01:00
homing = def.homing ,
2022-11-10 21:10:34 +01:00
drop = def.drop or false , -- drops arrow as registered item when true
2024-09-22 02:24:39 +02:00
collisionbox = def.collisionbox or { 0 , 0 , 0 , 0 , 0 , 0 } , -- remove box around arrows
2022-11-10 21:10:34 +01:00
timer = 0 ,
switch = 0 ,
2024-01-25 03:50:00 +01:00
_lifetime = def._lifetime or 7 ,
2022-11-10 21:10:34 +01:00
owner_id = def.owner_id ,
2024-11-14 13:25:30 +01:00
_vl_projectile = table.update ( def._vl_projectile or { } , {
2024-09-10 16:04:36 +02:00
behaviors = behaviors ,
ignore_gravity = true ,
damages_players = true ,
allow_punching = function ( self , entity_def , projectile_def , object )
2024-09-22 14:33:33 +02:00
if def.allow_punching and not def.allow_punching ( self , entity_def , projectile_def , object ) then return false end
2024-09-16 03:00:19 +02:00
if self.timer > 2 then return true end
2024-10-24 13:39:37 +02:00
if self._owner and mcl_util.get_entity_id ( object ) == self._owner then return false end
2024-09-22 14:33:33 +02:00
2024-09-10 16:04:36 +02:00
return true
end ,
on_collide_with_solid = function ( self , pos , node , nodedef )
if nodedef or not nodedef.walkable then return end
self.hit_node ( self , pos , node )
if self.drop == true then
pos.y = pos.y + 1
self.lastpos = self.lastpos or pos
minetest.add_item ( self.lastpos , self.object : get_luaentity ( ) . name )
end
self._removed = true
self.object : remove ( ) ;
end ,
on_collide_with_entity = function ( self , pos , object )
if self.hit_player and object : is_player ( ) then
self.hit_player ( self , object )
self._removed = true
self.object : remove ( )
return
end
local entity = object : get_luaentity ( )
if not entity or entity.name == self.object : get_luaentity ( ) . name then return end
if self.timer <= 2 then return end
if self.hit_mob and entity.is_mob == true then
self.hit_mob ( self , object )
self._removed = true
self.object : remove ( )
return
elseif self.hit_object then
self.hit_object ( self , object )
self._removed = true
self.object : remove ( )
return
end
end
2024-11-14 13:25:30 +01:00
} ) ,
2024-01-25 03:50:00 +01:00
on_punch = def.on_punch or function ( self , puncher , time_from_last_punch , tool_capabilities , dir , damage )
local vel = self.object : get_velocity ( ) : length ( )
2024-05-02 00:18:43 +02:00
self.object : set_velocity ( dir * vel )
2024-10-24 13:39:37 +02:00
self._owner = mcl_util.get_entity_id ( puncher )
2022-11-10 21:10:34 +01:00
end ,
automatic_face_movement_dir = def.rotate
and ( def.rotate - ( math.pi / 180 ) ) or false ,
on_activate = def.on_activate ,
on_step = def.on_step or function ( self , dtime )
2024-09-10 16:04:36 +02:00
-- Projectile behavior processing
vl_projectile.update_projectile ( self , dtime )
2022-11-10 21:10:34 +01:00
local pos = self.object : get_pos ( )
2024-09-10 16:04:36 +02:00
if not pos then return end
2022-11-10 21:10:34 +01:00
2024-09-10 16:04:36 +02:00
if self.switch == 0 or self.timer > self._lifetime or not within_limits ( pos , 0 ) then
2022-11-10 21:10:34 +01:00
mcl_burning.extinguish ( self.object )
2024-09-10 16:04:36 +02:00
self._removed = true
2022-11-10 21:10:34 +01:00
self.object : remove ( ) ;
return
end
-- does arrow have a tail (fireball)
2024-09-15 02:04:39 +02:00
if def.tail == 1 and def.tail_texture then
2022-11-10 21:10:34 +01:00
minetest.add_particle ( {
pos = pos ,
velocity = { x = 0 , y = 0 , z = 0 } ,
acceleration = { x = 0 , y = 0 , z = 0 } ,
expirationtime = def.expire or 0.25 ,
collisiondetection = false ,
texture = def.tail_texture ,
size = def.tail_size or 5 ,
glow = def.glow or 0 ,
} )
end
2023-11-15 18:59:46 +01:00
if self.homing and self._target then
local p = self._target : get_pos ( )
if p then
if minetest.line_of_sight ( self.object : get_pos ( ) , p ) then
self.object : set_velocity ( vector.direction ( self.object : get_pos ( ) , p ) * self.velocity )
end
else
2023-12-14 16:50:23 +01:00
self._target = nil
2023-11-15 18:59:46 +01:00
end
end
2022-11-10 21:10:34 +01:00
self.lastpos = pos
end
2022-11-10 22:59:42 +01:00
} )
2022-11-10 21:10:34 +01:00
end
-- Register spawn eggs
-- Note: This also introduces the “spawn_egg” group:
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
2024-09-14 04:31:04 +02:00
function mcl_mobs . register_egg ( mob_id , desc , background_color , overlay_color , addegg , no_creative )
2022-11-10 21:10:34 +01:00
local grp = { spawn_egg = 1 }
-- do NOT add this egg to creative inventory (e.g. dungeon master)
if no_creative == true then
grp.not_in_creative_inventory = 1
end
local invimg = " (spawn_egg.png^[multiply: " .. background_color .. " )^(spawn_egg_overlay.png^[multiply: " .. overlay_color .. " ) "
if old_spawn_icons then
2024-09-14 04:31:04 +02:00
local mobname = mob_id : gsub ( " mobs_mc: " , " " )
2022-11-10 21:10:34 +01:00
local fn = " mobs_mc_spawn_icon_ " .. mobname .. " .png "
if mcl_util.file_exists ( minetest.get_modpath ( " mobs_mc " ) .. " /textures/ " .. fn ) then
invimg = fn
end
end
if addegg == 1 then
invimg = " mobs_chicken_egg.png^( " .. invimg ..
" ^[mask:mobs_chicken_egg_overlay.png) "
end
-- register old stackable mob egg
2024-09-14 04:31:04 +02:00
minetest.register_craftitem ( mob_id , {
2022-11-10 21:10:34 +01:00
description = desc ,
inventory_image = invimg ,
groups = grp ,
_doc_items_longdesc = S ( " This allows you to place a single mob. " ) ,
_doc_items_usagehelp = S ( " Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns. " ) ,
on_place = function ( itemstack , placer , pointed_thing )
local pos = pointed_thing.above
2023-06-04 22:20:29 +02:00
2022-11-10 21:10:34 +01:00
-- am I clicking on something with existing on_rightclick function?
local under = minetest.get_node ( pointed_thing.under )
local def = minetest.registered_nodes [ under.name ]
if def and def.on_rightclick then
return def.on_rightclick ( pointed_thing.under , under , placer , itemstack )
end
2024-09-14 04:31:04 +02:00
local mob_name = itemstack : get_name ( )
2023-03-04 02:38:51 +01:00
if pos and within_limits ( pos , 0 ) and not minetest.is_protected ( pos , placer : get_player_name ( ) ) then
2022-11-10 21:10:34 +01:00
local name = placer : get_player_name ( )
local privs = minetest.get_player_privs ( name )
2023-03-04 02:38:51 +01:00
2022-11-10 21:10:34 +01:00
if under.name == " mcl_mobspawners:spawner " then
if minetest.is_protected ( pointed_thing.under , name ) then
minetest.record_protection_violation ( pointed_thing.under , name )
return itemstack
end
if not privs.maphack then
minetest.chat_send_player ( name , S ( " You need the “maphack” privilege to change the mob spawner. " ) )
return itemstack
end
2023-03-04 02:38:51 +01:00
local dim = mcl_worlds.pos_to_dimension ( placer : get_pos ( ) )
local mob_light_lvl = { mcl_mobs : mob_light_lvl ( itemstack : get_name ( ) , dim ) }
--minetest.log("min light: " .. mob_light_lvl[1])
--minetest.log("max light: " .. mob_light_lvl[2])
2024-06-15 02:49:45 +02:00
-- Handle egg conversion
local convert_to = ( minetest.registered_entities [ mob_name ] or { } ) . _convert_to
if convert_to then mob_name = convert_to end
mcl_mobspawners.setup_spawner ( pointed_thing.under , mob_name , mob_light_lvl [ 1 ] , mob_light_lvl [ 2 ] )
2022-11-10 21:10:34 +01:00
if not minetest.is_creative_enabled ( name ) then
itemstack : take_item ( )
end
return itemstack
end
2024-09-14 04:31:04 +02:00
if not minetest.registered_entities [ mob_name ] then
2022-11-10 21:10:34 +01:00
return itemstack
end
if minetest.settings : get_bool ( " only_peaceful_mobs " , false )
2024-09-14 04:31:04 +02:00
and minetest.registered_entities [ mob_name ] . type == " monster " then
2022-11-10 21:10:34 +01:00
minetest.chat_send_player ( name , S ( " Only peaceful mobs allowed! " ) )
return itemstack
end
2024-09-14 04:31:04 +02:00
pos.y = pos.y - 1
local mob = mcl_mobs.spawn ( pos , mob_name )
2024-09-30 19:21:40 +02:00
if not mob then
2024-09-14 04:31:04 +02:00
pos.y = pos.y + 1
mob = mcl_mobs.spawn ( pos , mob_name )
if not mob then return end
end
2022-11-10 21:10:34 +01:00
local entityname = itemstack : get_name ( )
minetest.log ( " action " , " Player " .. name .. " spawned " .. entityname .. " at " .. minetest.pos_to_string ( pos ) )
local ent = mob : get_luaentity ( )
-- don't set owner if monster or sneak pressed
if ent.type ~= " monster "
and not placer : get_player_control ( ) . sneak then
ent.owner = placer : get_player_name ( )
ent.tamed = true
end
-- set nametag
local nametag = itemstack : get_meta ( ) : get_string ( " name " )
if nametag ~= " " then
if string.len ( nametag ) > MAX_MOB_NAME_LENGTH then
nametag = string.sub ( nametag , 1 , MAX_MOB_NAME_LENGTH )
end
ent.nametag = nametag
2023-04-27 22:43:33 +02:00
ent : update_tag ( )
2022-11-10 21:10:34 +01:00
end
-- if not in creative then take item
if not minetest.is_creative_enabled ( placer : get_player_name ( ) ) then
itemstack : take_item ( )
end
end
return itemstack
end ,
} )
end