From 531253008a13559cdab63f420e9d35c78b382c95 Mon Sep 17 00:00:00 2001 From: jordan4ibanez Date: Fri, 23 Apr 2021 23:56:59 -0400 Subject: [PATCH] Complete mob breeding, make cows breedable --- mods/ENTITIES/mcl_mobs/api/api.lua | 16 +- .../mcl_mobs/api/mob_functions/ai.lua | 97 +++++++++++- .../mcl_mobs/api/mob_functions/animation.lua | 21 +++ .../mcl_mobs/api/mob_functions/breeding.lua | 146 +++++++++++++++++- .../api/mob_functions/mob_effects.lua | 30 ++++ .../mcl_mobs/api/mob_functions/set_up.lua | 32 ++-- mods/ENTITIES/mobs_mc/cow+mooshroom.lua | 20 ++- 7 files changed, 333 insertions(+), 29 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api/api.lua b/mods/ENTITIES/mcl_mobs/api/api.lua index af2ce1d7c..ac771164e 100644 --- a/mods/ENTITIES/mcl_mobs/api/api.lua +++ b/mods/ENTITIES/mcl_mobs/api/api.lua @@ -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 diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua index cb755b03e..b5af5f533 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua @@ -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 diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/animation.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/animation.lua index 809eacef5..c26d33089 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/animation.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/animation.lua @@ -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 \ No newline at end of file diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/breeding.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/breeding.lua index 975495f04..f71018aba 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/breeding.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/breeding.lua @@ -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 \ No newline at end of file diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/mob_effects.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/mob_effects.lua index b01920f18..847315ff1 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/mob_effects.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/mob_effects.lua @@ -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 \ No newline at end of file diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/set_up.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/set_up.lua index 864b888e8..41f3932f7 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/set_up.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/set_up.lua @@ -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 diff --git a/mods/ENTITIES/mobs_mc/cow+mooshroom.lua b/mods/ENTITIES/mobs_mc/cow+mooshroom.lua index 6dbb594f0..87e6f2552 100644 --- a/mods/ENTITIES/mobs_mc/cow+mooshroom.lua +++ b/mods/ENTITIES/mobs_mc/cow+mooshroom.lua @@ -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,