Complete mob breeding, make cows breedable
parent
79cb6ddc49
commit
531253008a
|
@ -245,7 +245,6 @@ function mobs:register_mob(name, def)
|
|||
shoot_interval = def.shoot_interval,
|
||||
sounds = def.sounds or {},
|
||||
animation = def.animation,
|
||||
follow = def.follow,
|
||||
jump = def.jump ~= false,
|
||||
walk_chance = def.walk_chance or 50,
|
||||
attacks_monsters = def.attacks_monsters or false,
|
||||
|
@ -329,11 +328,24 @@ function mobs:register_mob(name, def)
|
|||
lifetimer_reset = 30, --30 seconds
|
||||
lifetimer = 30, --30 seconds
|
||||
|
||||
--breeding stuff
|
||||
breedable = def.breedable,
|
||||
breed_timer = 0,
|
||||
breed_cooloff_timer = 5*60, -- 5 minutes
|
||||
breed_lookout_timer = 0,
|
||||
breed_distance = def.breed_distance or 1.5, --how far away mobs have to be to begin actual breeding
|
||||
breed_lookout_timer_goal = 30, --30 seconds (this timer is for how long the mob looks for a mate)
|
||||
breed_timer_cooloff = 5*60, -- 5 minutes (this timer is for how long the mob has to wait before being bred again)
|
||||
bred = false,
|
||||
follow = def.follow, --this item is also used for the breeding mechanism
|
||||
follow_distance = def.follow_distance or 2,
|
||||
baby_size = def.baby_size or 0.5,
|
||||
baby = false,
|
||||
grow_up_timer = 0,
|
||||
grow_up_goal = 20*60, --in 20 minutes the mob grows up
|
||||
|
||||
backup_visual_size = def.visual_size,
|
||||
backup_collisionbox = collisionbox,
|
||||
backup_selectionbox = def.selectionbox or def.collisionbox,
|
||||
--end j4i stuff
|
||||
|
||||
-- MCL2 extensions
|
||||
|
|
|
@ -81,8 +81,20 @@ local land_state_switch = function(self, dtime)
|
|||
--continue
|
||||
end
|
||||
|
||||
--ignore everything else if breeding
|
||||
if self.breed_lookout_timer and self.breed_lookout_timer > 0 then
|
||||
self.state = "breed"
|
||||
return
|
||||
--reset the state timer to get the mob out of
|
||||
--the breed state
|
||||
elseif self.state == "breed" then
|
||||
self.state_timer = 0
|
||||
end
|
||||
|
||||
--ignore everything else if following
|
||||
if mobs.check_following(self) then
|
||||
if mobs.check_following(self) and
|
||||
(not self.breed_lookout_timer or (self.breed_lookout_timer and self.breed_lookout_timer == 0)) and
|
||||
(not self.breed_timer or (self.breed_timer and self.breed_timer == 0)) then
|
||||
self.state = "follow"
|
||||
return
|
||||
--reset the state timer to get the mob out of
|
||||
|
@ -109,17 +121,37 @@ end
|
|||
-- states are executed here
|
||||
local land_state_execution = function(self,dtime)
|
||||
|
||||
--[[ -- this is a debug which shows the timer and makes mobs breed 100 times faster
|
||||
print(self.breed_timer)
|
||||
if self.breed_timer > 0 then
|
||||
self.breed_timer = self.breed_timer - (dtime * 100)
|
||||
if self.breed_timer <= 0 then
|
||||
self.breed_timer = 0
|
||||
end
|
||||
end
|
||||
]]--
|
||||
|
||||
--no collisionbox exception
|
||||
if not self.object:get_properties() then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
--timer to time out looking for mate
|
||||
if self.breed_lookout_timer and self.breed_lookout_timer > 0 then
|
||||
self.breed_lookout_timer = self.breed_lookout_timer - dtime
|
||||
--looking for mate failed
|
||||
if self.breed_lookout_timer <= 0 then
|
||||
self.breed_lookout_timer = 0
|
||||
end
|
||||
end
|
||||
|
||||
--cool off after breeding
|
||||
if self.breed_timer and self.breed_timer > 0 then
|
||||
self.breed_timer = self.breed_timer - dtime
|
||||
--do this to skip the first check, using as switch
|
||||
if self.breed_timer <= 0 then
|
||||
self.breed_timer = nil
|
||||
self.breed_timer = 0
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -165,7 +197,6 @@ local land_state_execution = function(self,dtime)
|
|||
end
|
||||
|
||||
mobs.lock_yaw(self)
|
||||
|
||||
elseif self.state == "follow" then
|
||||
|
||||
--always look at players
|
||||
|
@ -288,6 +319,48 @@ local land_state_execution = function(self,dtime)
|
|||
mobs.projectile_attack_walk(self,dtime)
|
||||
|
||||
end
|
||||
elseif self.state == "breed" then
|
||||
|
||||
mobs.breeding_effect(self)
|
||||
|
||||
local mate = mobs.look_for_mate(self)
|
||||
|
||||
--found a mate
|
||||
if mate then
|
||||
mobs.set_yaw_while_breeding(self,mate)
|
||||
mobs.set_velocity(self, self.walk_velocity)
|
||||
|
||||
--smoosh together basically
|
||||
if vector_distance(self.object:get_pos(), mate:get_pos()) <= self.breed_distance then
|
||||
mobs.set_mob_animation(self, "stand")
|
||||
if self.special_breed_timer == 0 then
|
||||
self.special_breed_timer = 2 --breeding takes 2 seconds
|
||||
end
|
||||
|
||||
self.special_breed_timer = self.special_breed_timer - dtime
|
||||
if self.special_breed_timer <= 0 then
|
||||
|
||||
--pop a baby out, it's a miracle!
|
||||
local baby_pos = vector.divide(vector.add(self.object:get_pos(), mate:get_pos()), 2)
|
||||
local baby_mob = minetest.add_entity(pos, self.name, minetest.serialize({baby = true, grow_up_timer = self.grow_up_goal, bred = true}))
|
||||
|
||||
self.special_breed_timer = 0
|
||||
self.breed_lookout_timer = 0
|
||||
self.breed_timer = self.breed_timer_cooloff
|
||||
|
||||
mate:get_luaentity().special_breed_timer = 0
|
||||
mate:get_luaentity().breed_lookout_timer = 0
|
||||
mate:get_luaentity().breed_timer = self.breed_timer_cooloff -- can reuse because it's the same mob
|
||||
end
|
||||
else
|
||||
mobs.set_mob_animation(self, "walk")
|
||||
end
|
||||
--couldn't find a mate, just stand there until the player pushes it towards one
|
||||
--or the timer runs out
|
||||
else
|
||||
mobs.set_mob_animation(self, "stand")
|
||||
mobs.set_velocity(self,0)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
@ -777,6 +850,24 @@ mobs.mob_step = function(self, dtime)
|
|||
return
|
||||
end
|
||||
|
||||
--baby grows up
|
||||
if self.baby then
|
||||
--print(self.grow_up_timer)
|
||||
--catch missing timer
|
||||
if not self.grow_up_timer then
|
||||
self.grow_up_timer = self.grow_up_goal
|
||||
end
|
||||
|
||||
self.grow_up_timer = self.grow_up_timer - dtime
|
||||
|
||||
--baby grows up!
|
||||
if self.grow_up_timer <= 0 then
|
||||
self.grow_up_timer = 0
|
||||
mobs.baby_grow_up(self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--do custom mob instructions
|
||||
if self.do_custom then
|
||||
|
|
|
@ -233,6 +233,27 @@ mobs.set_yaw_while_following = function(self)
|
|||
local new_direction = vector_direction(pos1,pos2)
|
||||
local new_yaw = minetest_dir_to_yaw(new_direction)
|
||||
|
||||
self.object:set_yaw(new_yaw)
|
||||
self.yaw = new_yaw
|
||||
end
|
||||
|
||||
--this is used for when mobs breed
|
||||
mobs.set_yaw_while_breeding = function(self, mate)
|
||||
|
||||
if self.object:get_properties().automatic_face_movement_dir then
|
||||
self.object:set_properties{automatic_face_movement_dir = false}
|
||||
end
|
||||
|
||||
--turn positions into pseudo 2d vectors
|
||||
local pos1 = self.object:get_pos()
|
||||
pos1.y = 0
|
||||
|
||||
local pos2 = mate:get_pos()
|
||||
pos2.y = 0
|
||||
|
||||
local new_direction = vector_direction(pos1,pos2)
|
||||
local new_yaw = minetest_dir_to_yaw(new_direction)
|
||||
|
||||
self.object:set_yaw(new_yaw)
|
||||
self.yaw = new_yaw
|
||||
end
|
|
@ -1,3 +1,7 @@
|
|||
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||
|
||||
local vector_distance = vector.distance
|
||||
|
||||
--check to see if someone nearby has some tasty food
|
||||
mobs.check_following = function(self) -- returns true or false
|
||||
|
||||
|
@ -18,8 +22,8 @@ mobs.check_following = function(self) -- returns true or false
|
|||
self.following_person = nil
|
||||
return(false)
|
||||
end
|
||||
local item_name = stack:get_name()
|
||||
|
||||
local item_name = stack:get_name()
|
||||
--all checks have passed, that guy has some good looking food
|
||||
if item_name == self.follow then
|
||||
self.following_person = follower
|
||||
|
@ -30,4 +34,144 @@ mobs.check_following = function(self) -- returns true or false
|
|||
--everything failed
|
||||
self.following_person = nil
|
||||
return(false)
|
||||
end
|
||||
|
||||
--a function which attempts to make mobs enter
|
||||
--the breeding state
|
||||
mobs.enter_breed_state = function(self,clicker)
|
||||
|
||||
--do not breed if baby
|
||||
if self.baby then
|
||||
return(false)
|
||||
end
|
||||
|
||||
--do not do anything if looking for mate or
|
||||
--if cooling off from breeding
|
||||
if self.breed_lookout_timer > 0 or self.breed_timer > 0 then
|
||||
return(false)
|
||||
end
|
||||
|
||||
--if this is caught, that means something has gone
|
||||
--seriously wrong
|
||||
if not clicker or not clicker:is_player() then
|
||||
return(false)
|
||||
end
|
||||
|
||||
local stack = clicker:get_wielded_item()
|
||||
--safety check
|
||||
if not stack then
|
||||
return(false)
|
||||
end
|
||||
|
||||
local item_name = stack:get_name()
|
||||
--all checks have passed, that guy has some good looking food
|
||||
if item_name == self.follow then
|
||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||
stack:take_item()
|
||||
clicker:set_wielded_item(stack)
|
||||
end
|
||||
self.breed_lookout_timer = self.breed_lookout_timer_goal
|
||||
self.bred = true
|
||||
return(true)
|
||||
end
|
||||
|
||||
--everything failed
|
||||
return(false)
|
||||
end
|
||||
|
||||
|
||||
--find the closest mate in the area
|
||||
mobs.look_for_mate = function(self)
|
||||
|
||||
local pos1 = self.object:get_pos()
|
||||
pos1.y = pos1.y + self.eye_height
|
||||
|
||||
local mates_in_area = {}
|
||||
local winner_mate = nil
|
||||
local mates_detected = 0
|
||||
local radius = self.view_range
|
||||
|
||||
--get mates in radius
|
||||
for _,mate in pairs(minetest_get_objects_inside_radius(pos1, radius)) do
|
||||
|
||||
--look for a breeding mate
|
||||
if mate and mate:get_luaentity()
|
||||
and mate:get_luaentity()._cmi_is_mob
|
||||
and mate:get_luaentity().name == self.name
|
||||
and mate:get_luaentity().breed_lookout_timer > 0
|
||||
and mate:get_luaentity() ~= self then
|
||||
|
||||
local pos2 = mate:get_pos()
|
||||
|
||||
local distance = vector_distance(pos1,pos2)
|
||||
|
||||
if distance <= radius then
|
||||
if line_of_sight then
|
||||
--must add eye height or stuff breaks randomly because of
|
||||
--seethrough nodes being a blocker (like grass)
|
||||
if minetest_line_of_sight(
|
||||
vector_new(pos1.x, pos1.y, pos1.z),
|
||||
vector_new(pos2.x, pos2.y + mate:get_properties().eye_height, pos2.z)
|
||||
) then
|
||||
mates_detected = mates_detected + 1
|
||||
mates_in_area[mate] = distance
|
||||
end
|
||||
else
|
||||
mates_detected = mates_detected + 1
|
||||
mates_in_area[mate] = distance
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--return if there's no one near by
|
||||
if mates_detected <= 0 then --handle negative numbers for some crazy error that could possibly happen
|
||||
return nil
|
||||
end
|
||||
|
||||
--do a default radius max
|
||||
local shortest_distance = radius + 1
|
||||
|
||||
--sort through mates and find the closest mate
|
||||
for mate,distance in pairs(mates_in_area) do
|
||||
if distance < shortest_distance then
|
||||
shortest_distance = distance
|
||||
winner_mate = mate
|
||||
end
|
||||
end
|
||||
|
||||
return(winner_mate)
|
||||
|
||||
end
|
||||
|
||||
--make the baby grow up
|
||||
mobs.baby_grow_up = function(self)
|
||||
self.baby = nil
|
||||
self.visual_size = self.backup_visual_size
|
||||
self.collisionbox = self.backup_collisionbox
|
||||
self.selectionbox = self.backup_selectionbox
|
||||
self.object:set_properties(self)
|
||||
end
|
||||
|
||||
--makes the baby grow up faster with diminishing returns
|
||||
mobs.make_baby_grow_faster = function(self,clicker)
|
||||
if clicker and clicker:is_player() then
|
||||
local stack = clicker:get_wielded_item()
|
||||
--safety check
|
||||
if not stack then
|
||||
return
|
||||
end
|
||||
|
||||
local item_name = stack:get_name()
|
||||
--all checks have passed, that guy has some good looking food
|
||||
if item_name == self.follow then
|
||||
self.grow_up_timer = self.grow_up_timer - (self.grow_up_timer * 0.10) --take 10 percent off - diminishing returns
|
||||
|
||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||
stack:take_item()
|
||||
clicker:set_wielded_item(stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -119,4 +119,34 @@ mobs.tamed_effect = function(self)
|
|||
vertical = false,
|
||||
texture = "heart.png",
|
||||
})
|
||||
end
|
||||
|
||||
--hearts when breeding
|
||||
mobs.breeding_effect = function(self)
|
||||
local pos = self.object:get_pos()
|
||||
local yaw = self.object:get_yaw()
|
||||
local collisionbox = self.object:get_properties().collisionbox
|
||||
|
||||
local min, max
|
||||
|
||||
if collisionbox then
|
||||
min = {x=collisionbox[1], y=collisionbox[2], z=collisionbox[3]}
|
||||
max = {x=collisionbox[4], y=collisionbox[5], z=collisionbox[6]}
|
||||
end
|
||||
|
||||
minetest_add_particlespawner({
|
||||
amount = 2,
|
||||
time = 0.0001,
|
||||
minpos = vector.add(pos, min),
|
||||
maxpos = vector.add(pos, max),
|
||||
minvel = vector.new(-1,1,-1),
|
||||
maxvel = vector.new(1,3,1),
|
||||
minexptime = 0.7,
|
||||
maxexptime = 1,
|
||||
minsize = 1,
|
||||
maxsize = 2,
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
texture = "heart.png",
|
||||
})
|
||||
end
|
|
@ -107,12 +107,12 @@ mobs.mob_activate = function(self, staticdata, def, dtime)
|
|||
mesh = def.gotten_mesh
|
||||
end
|
||||
|
||||
-- set child objects to half size
|
||||
if self.child == true then
|
||||
-- set baby mobs to half size
|
||||
if self.baby == true then
|
||||
|
||||
vis_size = {
|
||||
x = self.base_size.x * .5,
|
||||
y = self.base_size.y * .5,
|
||||
x = self.base_size.x * self.baby_size,
|
||||
y = self.base_size.y * self.baby_size,
|
||||
}
|
||||
|
||||
if def.child_texture then
|
||||
|
@ -120,20 +120,20 @@ mobs.mob_activate = function(self, staticdata, def, dtime)
|
|||
end
|
||||
|
||||
colbox = {
|
||||
self.base_colbox[1] * .5,
|
||||
self.base_colbox[2] * .5,
|
||||
self.base_colbox[3] * .5,
|
||||
self.base_colbox[4] * .5,
|
||||
self.base_colbox[5] * .5,
|
||||
self.base_colbox[6] * .5
|
||||
self.base_colbox[1] * self.baby_size,
|
||||
self.base_colbox[2] * self.baby_size,
|
||||
self.base_colbox[3] * self.baby_size,
|
||||
self.base_colbox[4] * self.baby_size,
|
||||
self.base_colbox[5] * self.baby_size,
|
||||
self.base_colbox[6] * self.baby_size
|
||||
}
|
||||
selbox = {
|
||||
self.base_selbox[1] * .5,
|
||||
self.base_selbox[2] * .5,
|
||||
self.base_selbox[3] * .5,
|
||||
self.base_selbox[4] * .5,
|
||||
self.base_selbox[5] * .5,
|
||||
self.base_selbox[6] * .5
|
||||
self.base_selbox[1] * self.baby_size,
|
||||
self.base_selbox[2] * self.baby_size,
|
||||
self.base_selbox[3] * self.baby_size,
|
||||
self.base_selbox[4] * self.baby_size,
|
||||
self.base_selbox[5] * self.baby_size,
|
||||
self.base_selbox[6] * self.baby_size
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -48,15 +48,19 @@ local cow_def = {
|
|||
walk_end = 40, run_start = 0,
|
||||
run_end = 40,
|
||||
},
|
||||
follow = mobs_mc.follow.cow,
|
||||
--follow = mobs_mc.follow.cow,
|
||||
on_rightclick = function(self, clicker)
|
||||
--if mobs:feed_tame(self, clicker, 1, true, true) then
|
||||
--return
|
||||
--end
|
||||
|
||||
--if self.child then
|
||||
-- return
|
||||
--end
|
||||
--attempt to enter breed state
|
||||
if mobs.enter_breed_state(self,clicker) then
|
||||
return
|
||||
end
|
||||
|
||||
if self.baby then
|
||||
mobs.make_baby_grow_faster(self,clicker)
|
||||
--do child timer thing %10
|
||||
return
|
||||
end
|
||||
|
||||
local item = clicker:get_wielded_item()
|
||||
if item:get_name() == mobs_mc.items.bucket and clicker:get_inventory() then
|
||||
|
@ -75,6 +79,8 @@ local cow_def = {
|
|||
end
|
||||
end,
|
||||
breedable = true,
|
||||
breed_distance = 1.5,
|
||||
baby_size = 0.5,
|
||||
follow_distance = 2,
|
||||
follow = mobs_mc.items.wheat,
|
||||
view_range = 10,
|
||||
|
|
Loading…
Reference in New Issue