diff --git a/api/api.lua b/api/api.lua index de9b443..0558ba8 100644 --- a/api/api.lua +++ b/api/api.lua @@ -3,30 +3,117 @@ ------------- -- Ver 0.1 -- +local hitbox = mob_core.get_hitbox + +local find_string = mob_core.find_val + +---------- +-- Math -- +---------- + local random = math.random +local min = math.min local pi = math.pi local abs = math.abs local ceil = math.ceil +local floor = math.floor +local function diff(a, b) -- Get difference between 2 angles + return math.atan2(math.sin(b - a), math.cos(b - a)) +end +local function round(x) -- Round to nearest multiple of 0.5 + return x + 0.5 - (x + 0.5) % 1 +end +local function clamp(num, min, max) + if num < min then + num = min + elseif num > max then + num = max + end + + return num +end + +local yaw2dir = minetest.yaw_to_dir +local dir2yaw = minetest.dir_to_yaw local vec_dist = vector.distance +local vec_dir = vector.direction -local abr = minetest.get_mapgen_setting('active_block_range') +local function dist_2d(pos1, pos2) + local a = vector.new(pos1.x, 0, pos1.z) + local b = vector.new(pos2.x, 0, pos2.z) + return vec_dist(a, b) +end -local function hitbox(self) return self.object:get_properties().collisionbox end +local function get_average_pos(vectors) + local sum = {x = 0, y = 0, z = 0} + for _, vec in pairs(vectors) do sum = vector.add(sum, vec) end + local avg = vector.divide(sum, #vectors) + avg.x = math.floor(avg.x) + 0.5 + avg.y = math.floor(avg.y) + 0.5 + avg.z = math.floor(avg.z) + 0.5 + return avg +end -local walkable_nodes = {} +local function pos_to_neighbor(self, pos2) + local pos = self.object:get_pos() + local dir = vector.direction(pos, pos2) + local neighbor = self._neighbors[mobkit.dir2neighbor(dir)] + local vec = { + x = neighbor.x, + y = 0, + z = neighbor.z + } + return vector.add(pos, vec) +end -minetest.register_on_mods_loaded(function() - for name in pairs(minetest.registered_nodes) do - if name ~= "air" and name ~= "ignore" then - if minetest.registered_nodes[name].walkable then - table.insert(walkable_nodes,name) +local function get_random_offset(pos, range) + if not pos then return nil end + range = (range * 0.5) or 3 + local pos_x = pos.x + random(-range, range) + local pos_z = pos.z + random(-range, range) + local node = nil + for i = -1, 1 do + i_pos = vector.new(pos_x, pos.y + i, pos_z) + if minetest.registered_nodes[minetest.get_node(vector.new(i_pos.x, i_pos.y - 1, i_pos.z)).name].walkable then + node = i_pos + break + end + end + if not node then return nil end + return node +end + +local is_movable = mob_core.is_moveable + +------------------- +-- API Functions -- +------------------- + +function animalia.is_prey(name1, name2) + if name1 == name2 then return 0 end + local def1 = minetest.registered_entities[name1] + local def2 = minetest.registered_entities[name2] + if find_string(paleotest.mobkit_mobs, name2) + and def1.follow + and def2.drops then + for _, drop in ipairs(def2.drops) do + if drop.name + and find_string(def1.follow, drop.name) + and def1.prey_params["height"] >= get_height(name2) + and def1.prey_params["health"] >= def2.max_hp then + -- Mob is prey + return 2 end end + -- Mob can be attacked + return 1 end -end) + -- Not a mobkit mob + return 0 +end -function better_fauna.particle_spawner(pos, texture, type, min_pos, max_pos) +function animalia.particle_spawner(pos, texture, type, min_pos, max_pos) type = type or "float" min_pos = min_pos or { x = pos.x - 2, @@ -71,157 +158,43 @@ function better_fauna.particle_spawner(pos, texture, type, min_pos, max_pos) end end ---------------------------- --- Mob Control Functions -- ---------------------------- - -local function can_fit(pos, width) - local pos1 = vector.new(pos.x - width, pos.y, pos.z - width) - local pos2 = vector.new(pos.x + width, pos.y, pos.z + width) - for x = pos1.x, pos2.x do - for y = pos1.y, pos2.y do - for z = pos1.z, pos2.z do - local p2 = vector.new(x, y, z) - local node = minetest.get_node(p2) - if minetest.registered_nodes[node.name].walkable then - local p3 = vector.new(p2.x, p2.y + 1, p2.z) - local node2 = minetest.get_node(p3) - if minetest.registered_nodes[node2.name].walkable then - return false - end - end - end +function animalia.can_reach(self, pos2) + if pos2 then + local pos = mobkit.get_stand_pos(self) + local box = hitbox(self) + local path_data = mob_core.find_path(pos, pos2, box[4] - 0.1, self.height, 100) + if path_data and #path_data > 2 then + return true, path_data end end - return true + return false end -local function move_from_wall(pos, width) - local pos1 = vector.new(pos.x - width, pos.y, pos.z - width) - local pos2 = vector.new(pos.x + width, pos.y, pos.z + width) - for x = pos1.x, pos2.x do - for y = pos1.y, pos2.y do - for z = pos1.z, pos2.z do - local p2 = vector.new(x, y, z) - if can_fit(p2, width) then - return p2 - end - end +function animalia.find_collision(self, dir) + local pos = mobkit.get_stand_pos(self) + local pos2 = vector.add(pos, vector.multiply(dir, 16)) + local ray = minetest.raycast(pos, pos2, false, false) + for pointed_thing in ray do + if pointed_thing.type == "node" then + return pointed_thing.under end end - return pos + return nil end -function better_fauna.find_path(pos, tpos, width) - - local endpos = tpos - - if not minetest.registered_nodes[minetest.get_node( - vector.new(endpos.x, endpos.y - 1, endpos.z)) - .name].walkable then - local min = vector.subtract(endpos, 1) - local max = vector.add(endpos, 1) - - local index_table = minetest.find_nodes_in_area_under_air( min, max, better_fauna.walkable_nodes) - for _, i_pos in pairs(index_table) do - if minetest.registered_nodes[minetest.get_node(i_pos) - .name].walkable then - endpos = vector.new(i_pos.x, i_pos.y + 1, i_pos.z) - break - end +function animalia.get_nearby_mate(self, name) + for _, obj in ipairs(self.nearby_objects) do + if mobkit.is_alive(obj) + and not obj:is_player() + and obj:get_luaentity().name == name + and obj:get_luaentity().gender ~= self.gender + and obj:get_luaentity().breeding then + return obj end - end - - local path = minetest.find_path(pos, endpos, 32, 1, 1, "A*_noprefetch") - - if not path - or #path < 2 then return end - - table.remove(path, 1) - table.remove(path, #path) - - for i = #path, 1, -1 do - if not path then return end - if not can_fit(path[i], width + 1) then - local clear = move_from_wall(path[i], width + 1) - if clear and can_fit(clear, width) then - path[i] = clear - end - end - if #path > 3 then - local pos1 = path[i - 2] - local pos2 = path[i] - -- Handle Diagonals - if pos1 - and pos2 - and pos1.x ~= pos2.x - and pos1.z ~= pos2.z then - if minetest.line_of_sight(pos1, pos2) then - local pos3 = vector.divide(vector.add(pos1, pos2), 2) - if can_fit(pos, width) then - table.remove(path, i - 1) - end - end - end - -- Reduce Straight Lines - if pos1 - and pos2 - and pos1.x == pos2.x - and pos1.z ~= pos2.z - and pos1.y == pos2.y then - if minetest.line_of_sight(pos1, pos2) then - local pos3 = vector.divide(vector.add(pos1, pos2), 2) - if can_fit(pos, width) then - table.remove(path, i - 1) - end - end - elseif pos1 - and pos2 - and pos1.x ~= pos2.x - and pos1.z == pos2.z - and pos1.y == pos2.y then - if minetest.line_of_sight(pos1, pos2) then - local pos3 = vector.divide(vector.add(pos1, pos2), 2) - if can_fit(pos, width) then - table.remove(path, i - 1) - end - end - end - end - end - - if #path > 2 then - if vector.distance(pos, path[2]) <= width + 1 then - for i = 3, #path do - path[i - 1] = path[i] - end - end - end - - return path + end end -function better_fauna.path_to_next_waypoint(self, tpos, speed_factor) - speed_factor = speed_factor or 1 - local pos = self.object:get_pos() - local path_data = better_fauna.find_path(pos, tpos, 1) - if not path_data - or #path_data < 2 then - return true - end - local pos2 = path_data[2] - if pos2 then - local yaw = self.object:get_yaw() - local tyaw = minetest.dir_to_yaw(vector.direction(pos, pos2)) - if abs(tyaw - yaw) > 0.1 then - mobkit.lq_turn2pos(self, pos2) - end - mobkit.lq_dumbwalk(self, pos2, speed_factor) - return true - end -end - -function better_fauna.feed_tame(self, clicker, feed_count, tame, breed) +function animalia.feed_tame(self, clicker, feed_count, tame, breed) local item = clicker:get_wielded_item() local pos = self.object:get_pos() local mob_name = mob_core.get_name_proper(self.name) @@ -266,20 +239,20 @@ function better_fauna.feed_tame(self, clicker, feed_count, tame, breed) }) if self.food >= feed_count then self.food = mobkit.remember(self, "food", 0) - if tame and not self.tamed then + if tame + and not self.tamed + and self.follow[1] == item:get_name() then mob_core.set_owner(self, clicker:get_player_name()) minetest.chat_send_player(clicker:get_player_name(), mob_name.." has been tamed!") mobkit.clear_queue_high(self) - paleotest.particle_spawner(pos, "mob_core_green_particle.png", "float", min, max) + animalia.particle_spawner(pos, "mob_core_green_particle.png", "float", minppos, maxppos) end if breed then if self.child then return false end if self.breeding then return false end if self.breeding_cooldown <= 0 then self.breeding = true - local min = vector.subtract(pos, 0.5) - local max = vector.add(pos, 0.5) - better_fauna.particle_spawner(pos, "heart.png", "float", min, max) + animalia.particle_spawner(pos, "heart.png", "float", minppos, maxppos) end end end @@ -287,250 +260,485 @@ function better_fauna.feed_tame(self, clicker, feed_count, tame, breed) return false end ------------------- --- HQ Functions -- ------------------- - -function better_fauna.hq_sporadic_flee(self, prty, player) - local tyaw = 0 - local init = true - local timer = 1 - if not player then return true end - local func = function(self) - if not mobkit.is_alive(player) then return true end - if not mobkit.is_alive(self) then return true end - local pos = mobkit.get_stand_pos(self) - local yaw = self.object:get_yaw() - local tpos = vector.add(pos, vector.multiply(minetest.yaw_to_dir(yaw), 4)) - - if init then - tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos)) - end - if self._anim ~= "run" then - mobkit.animate(self, "run") - end - - if mobkit.is_queue_empty_low(self) then - timer = timer - self.dtime - if timer < 0 then - tyaw = yaw - random(1.6, 3.2) - timer = 1 - end - if abs(tyaw - yaw) > 0.1 then - mobkit.turn2yaw(self, tyaw) - end - mobkit.go_forward_horizontal(self, self.max_speed) - if timer <= 0 then return true end - end - end - mobkit.queue_high(self, func, prty) +local function is_within_reach(self, target) + local dist = vec_dist(mobkit.get_stand_pos(self), mobkit.get_stand_pos(target)) - (hitbox(self)[4] + hitbox(target)[4]) + if dist <= self.reach then + return true + end + return false end -function better_fauna.hq_follow_player(self, prty, player) -- Follow Player - if not player then return end - if not mob_core.follow_holding(self, player) then return end - local func = function(self) - if not mobkit.is_alive(player) then - mobkit.clear_queue_high(self) +local function is_on_ground(object) + if object then + local pos = object:get_pos() + local sub = 1 + if not object:is_player() then + sub = math.abs(hitbox(object)[2]) + 1 + end + pos.y = pos.y - sub + if minetest.registered_nodes[minetest.get_node(pos).name].walkable then return true - end - if mobkit.is_queue_empty_low(self) then - local pos = mobkit.get_stand_pos(self) - local tpos = player:get_pos() - if mob_core.follow_holding(self, player) then - self.status = mobkit.remember(self, "status", "following") - local dist = vec_dist(pos, tpos) - local yaw = self.object:get_yaw() - local tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos)) - if dist > self.view_range then - self.status = mobkit.remember(self, "status", "") - return true - end - better_fauna.path_to_next_waypoint(self, tpos, 0.85) - if vec_dist(pos, tpos) < hitbox(self)[4] + 2 then - mobkit.lq_idle(self, 0.1, "stand") - end - else - self.status = mobkit.remember(self, "status", "") - mobkit.lq_idle(self, 0.1, "stand") - return true - end - end - end - mobkit.queue_high(self, func, prty) -end - -function better_fauna.get_nearby_mate(self, name) - for _,obj in ipairs(self.nearby_objects) do - if mobkit.is_alive(obj) - and not obj:is_player() - and obj:get_luaentity().name == name - and obj:get_luaentity().gender ~= self.gender - and obj:get_luaentity().breeding then - return obj end - end - return -end - -function better_fauna.hq_breed(self, prty) - local mate = better_fauna.get_nearby_mate(self, self.name) - if not mate then return end - local speed_factor = 0.5 - local func = function(self) - if mobkit.is_queue_empty_low(self) then - local pos = mobkit.get_stand_pos(self) - local tpos = mate:get_pos() - local dist = vec_dist(pos, tpos) - math.abs(hitbox(self)[4]) - if dist > 1.5 then - speed_factor = 0.5 - else - speed_factor = 0.1 - end - mob_core.goto_next_waypoint(self, tpos, speed_factor) - if dist < 1.75 then - self.breeding_time = self.breeding_time + 1 - end - if self.breeding_time >= 2 - or mate:get_luaentity().breeding_time >= 2 then - if self.gender == "female" then - mob_core.spawn_child(pos, self.name) - end - self.breeding = false - self.breeding_time = 0 - self.breeding_cooldown = 300 - mobkit.remember(self, "breeding", self.breeding) - mobkit.remember(self, "breeding_time", self.breeding_time) - mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown) - return true - end + pos.y = pos.y - 1 + if minetest.registered_nodes[minetest.get_node(pos).name].walkable then + return true end end - mobkit.queue_high(self, func, prty) + return false end -function better_fauna.hq_fowl_breed(self, prty) - local mate = better_fauna.get_nearby_mate(self, self.name) - if not mate then return end - local speed_factor = 0.5 - local func = function(self) - if mobkit.is_queue_empty_low(self) then - local pos = mobkit.get_stand_pos(self) - local tpos = mate:get_pos() - local dist = vec_dist(pos, tpos) - math.abs(hitbox(self)[4]) - if dist > 1.5 then - speed_factor = 0.5 - else - speed_factor = 0.1 - end - mob_core.goto_next_waypoint(self, tpos, speed_factor) - if dist < 1.75 then - self.breeding_time = self.breeding_time + 1 - end - if self.breeding_time >= 2 - or mate:get_luaentity().breeding_time >= 2 then - if self.gender == "female" then - minetest.add_particlespawner({ - amount = 6, - time = 0.25, - minpos = {x = pos.x - 7/16, y = pos.y - 5/16, z = pos.z - 7/16}, - maxpos = {x = pos.x + 7/16, y = pos.y - 5/16, z = pos.z + 7/16}, - minvel = vector.new(-1, 2, -1), - maxvel = vector.new(1, 5, 1), - minacc = vector.new(0, -9.81, 0), - maxacc = vector.new(0, -9.81, 0), - collisiondetection = true, - texture = "better_fauna_egg_fragment.png", - }) - mob_core.spawn_child(pos, self.name) - end - self.breeding = false - self.breeding_time = 0 - self.breeding_cooldown = 300 - mobkit.remember(self, "breeding", self.breeding) - mobkit.remember(self, "breeding_time", self.breeding_time) - mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown) - return true - end - end - end - mobkit.queue_high(self, func, prty) +local function get_height(name) + local def = minetest.registered_entities[name] + return abs(def.collisionbox[2]) + abs(def.collisionbox[5]) end -function better_fauna.hq_eat(self, prty) - local func = function(self) +------------------ +-- LQ Functions -- +------------------ + +function animalia.lq_follow_path(self, path_data, speed_factor, anim) + speed_factor = speed_factor or 1 + anim = anim or "walk" + local dest = nil + local timer = #path_data -- failsafe + local width = hitbox(self)[4] + local init = false + local func = function(self) local pos = mobkit.get_stand_pos(self) - local under = vector.new(pos.x, pos.y - 1, pos.z) - for _, node in ipairs(self.consumable_nodes) do - if node.name == minetest.get_node(under).name then - minetest.set_node(under, {name = node.replacement}) - local def = minetest.registered_nodes[node.name] - local texture = def.tiles[1] - texture = texture .. "^[resize:8x8" - minetest.add_particlespawner({ - amount = 6, - time = 0.1, - minpos = vector.new( - pos.x - 0.5, - pos.y + 0.1, - pos.z - 0.5 - ), - maxpos = vector.new( - pos.x + 0.5, - pos.y + 0.1, - pos.z + 0.5 - ), - minvel = {x=-1, y=1, z=-1}, - maxvel = {x=1, y=2, z=1}, - minacc = {x=0, y=-5, z=0}, - maxacc = {x=0, y=-9, z=0}, - minexptime = 1, - maxexptime = 1, - minsize = 1, - maxsize = 2, - collisiondetection = true, - vertical = false, - texture = texture, - }) - self.gotten = false - mobkit.remember(self, "gotten", self.gotten) - return true + local yaw = self.object:get_yaw() + local s_fctr = speed_factor + if path_data and #path_data > 1 then + if #path_data >= math.ceil(width) then + dest = path_data[1] else return true end + else + return true + end + + if not self.isonground then + table.remove(path_data, 1) + timer = timer - 1 + s_fctr = 0.25 + end + + timer = timer - self.dtime + if timer < 0 then return true end + + local y = self.object:get_velocity().y + + local tyaw = minetest.dir_to_yaw(vector.direction(pos, dest)) + + mobkit.turn2yaw(self, tyaw, self.turn_rate or 4) + + if vec_dist(pos, path_data[#path_data]) < math.ceil(width) then + if not self.isonground and not self.isinliquid and + mob_core.fall_check(self, pos, self.max_fall or self.jump_height) then + self.object:set_velocity({x = 0, y = y, z = 0}) + end + return true + end + + if vec_dist(pos, path_data[1]) < 2.5 + and diff(yaw, tyaw) < 1.5 then + table.remove(path_data, 1) + timer = timer - 1 + end + + if mob_core.fall_check(self, pos, self.max_fall or self.jump_height) then + self.object:set_velocity({x = 0, y = y, z = 0}) + return true + end + + if self.isonground or self.isinliquid then + local forward_dir = vector.normalize(minetest.yaw_to_dir(yaw)) + forward_dir = vector.multiply(forward_dir, + self.max_speed * s_fctr) + forward_dir.y = y + self.object:set_velocity(forward_dir) + if not init then + mobkit.animate(self, anim) + init = true + end + end + end + mobkit.queue_low(self, func) +end + +function animalia.lq_dumb_follow_path(self, path_data, speed_factor, anim) + speed_factor = speed_factor or 1 + anim = anim or "walk" + local dest = nil + local timer = 3 -- failsafe + local width = hitbox(self)[4] + local stop_thresh = 1 + local init = false + local func = function(self) + local pos = mobkit.get_stand_pos(self) + local yaw = self.object:get_yaw() + if path_data and #path_data > 1 then + dest = path_data[1] + else + return true + end + + if not self.isonground then table.remove(path_data, 1) end + + timer = timer - self.dtime + if timer < 0 then return true end + + local y = self.object:get_velocity().y + + local tyaw = minetest.dir_to_yaw(vector.direction(pos, dest)) + + if #path_data > 2 + and ((dist_2d(pos, path_data[1]) < 1 + or abs(tyaw - yaw) < 3) + or dist_2d(pos, path_data[1]) >= dist_2d(pos, path_data[2])) then + table.remove(path_data, 1) + end + + if abs(yaw - tyaw) > 0.5 then + mobkit.turn2yaw(self, tyaw, 8) + end + + if dist_2d(pos, path_data[#path_data]) < 0.6 then + if not self.isonground and not self.isinliquid and + mob_core.fall_check(self, pos, self.max_fall or self.jump_height) then + self.object:set_velocity({x = 0, y = y, z = 0}) + end + mobkit.animate(self, "stand") + return true + end + + if dist_2d(pos, path_data[#path_data]) < 0.6 then + mobkit.animate(self, "stand") + return true + end + + if mob_core.fall_check(self, pos, self.max_fall or self.jump_height) then + self.object:set_velocity({x = 0, y = y, z = 0}) + return true + end + + if self.isonground or self.isinliquid then + local forward_dir = vector.normalize(minetest.yaw_to_dir(yaw)) + forward_dir = vector.multiply(forward_dir, + self.max_speed * speed_factor) + forward_dir.y = y + self.object:set_velocity(forward_dir) + if not init then + mobkit.animate(self, anim) + init = true + end + end + end + mobkit.queue_low(self, func) +end + +function animalia.lq_idle(self, duration, anim) + anim = anim or 'stand' + local random_yaw = nil + local init = true + local func = function(self) + if init then + mobkit.animate(self, anim) + init = false + end + duration = duration - self.dtime + if random(6) < 2 + and not random_yaw then + random_yaw = self.object:get_yaw() + random(-2, 2) + elseif random_yaw + and abs(self.object:get_yaw() - random_yaw) > 0.1 then + mobkit.turn2yaw(self, random_yaw, 3) + self._tyaw = random_yaw + end + if duration <= 0 then return true end + end + mobkit.queue_low(self, func) +end + +--------------------------- +-- Mob Control Functions -- +--------------------------- + +function animalia.go_to_pos(self, tpos, speed_factor, anim) + speed_factor = speed_factor or 1 + local pos = self.object:get_pos() + local dist = vec_dist(pos, tpos) + if dist < 5 + and minetest.line_of_sight(pos, tpos) then + local _, pos2 = mob_core.get_next_waypoint(self, tpos) + if pos2 then + mob_core.lq_dumbwalk(self, pos2, speed_factor, anim) + return + end + else + local box = hitbox(self) + local path_data = mob_core.find_path(mobkit.get_stand_pos(self), tpos, box[4] - 0.1, self.height, 100) + if path_data then + mob_core.lq_follow_path(self, path_data, speed_factor, anim) + return + end + end + mob_core.lq_dumbwalk(self, tpos, speed_factor, anim) +end + +function animalia.go_to_pos_lite(self, tpos, speed_factor) + speed_factor = speed_factor or 1 + if mobkit.is_queue_empty_low(self) then + local _, pos2 = mob_core.get_next_waypoint(self, tpos) + if pos2 then + mob_core.lq_dumbwalk(self, tpos, speed_factor) + return true + else + local box = hitbox(self) + local path_data = mob_core.find_path(mobkit.get_stand_pos(self), tpos, box[4] - 0.1, self.height, 100) + if path_data and #path_data > 2 then + mob_core.lq_follow_path(self, path_data, speed_factor, anim) + return true + end end end - mobkit.queue_high(self, func, prty) + return false end --------------------------------- -- Entity Definition Functions -- --------------------------------- -function better_fauna.on_step(self, dtime, moveresult) +local function sensors() + local timer = 2 + local pulse = 1 + return function(self) + timer = timer - self.dtime + if timer < 0 then + pulse = pulse + 1 + local range = self.view_range + if pulse > 2 then + pulse = 1 + else + range = self.view_range * 0.5 + end + + local pos = self.object:get_pos() + self.group = {} + self.nearby_objects = minetest.get_objects_inside_radius(pos, range) + for i, obj in ipairs(self.nearby_objects) do + if obj ~= self.object + and obj:get_luaentity() + and obj:get_luaentity().name == self.name then + table.insert(self.group, obj) + elseif obj == self.object then + table.remove(self.nearby_objects, i) + break + end + end + timer = 2 + end + end +end + +function animalia.on_activate(self, staticdata, dtime_s) + mob_core.on_activate(self, staticdata, dtime_s) + self.sensefunc = sensors() + self.order = mobkit.recall(self, "order") or "wander" + self.gotten = mobkit.recall(self, "gotten") or false + self.attention_span = mobkit.recall(self, "attention_span") or 0 + self.breeding = mobkit.recall(self, "breeding") or false + self.breeding_time = mobkit.recall(self, "breeding_time") or 0 + self.breeding_cooldown = mobkit.recall(self, "breeding_cooldown") or 0 + self.lasso_pos = mobkit.recall(self, "lasso_pos") or nil + self.liquid_recovery_cooldown = 0 + self.target_blacklist = {} + if self.lasso_pos then + self.caught_with_lasso = true + if minetest.get_item_group(minetest.get_node(self.lasso_pos).name, "fence") > 0 then + local object = minetest.add_entity(self.lasso_pos, "animalia:lasso_fence_ent") + object:get_luaentity().parent = self.object + end + end + for name in pairs(minetest.registered_entities) do + if self.targets + and self.prey_params then + if animalia.is_prey(self.name, name) == 2 then + table.insert(self.targets, name) + end + end + end +end + +local function lasso_effect(self, pos2) + local pos = mobkit.get_stand_pos(self) + pos.y = pos.y + (self.height * 0.5) + local object = minetest.add_entity(pos2, "animalia:lasso_visual") + local ent = object:get_luaentity() + ent.parent = self.object + ent.anchor_pos = pos2 + return object +end + +local function is_under_solid(pos) + local pos2 = vector.new(pos.x, pos.y + 1, pos.z) + local def = minetest.registered_nodes[minetest.get_node(pos2).name] + return (def.walkable or mobkit.get_node_height(pos2) < 1.5) +end + +local function vec_center(vec) + for _, v in pairs(vec) do + v = floor(v + 0.5) + end + return vec +end + +local function do_step(self, moveresult) + local pos = mobkit.get_stand_pos(self) + local width = hitbox(self)[4] - 0.1 + if not self._step then + for _, data in ipairs(moveresult.collisions) do + if data.type == "node" + and data.node_pos.y + 0.5 > pos.y + and not is_under_solid(data.node_pos) + and not vector.equals(vec_center(pos), vec_center(data.node_pos)) then + local vel_yaw = self.object:get_yaw() + local dir_yaw = minetest.dir_to_yaw(vector.direction(pos, data.node_pos)) + if diff(vel_yaw, dir_yaw) < 1.6 then + self._step = data.node_pos + break + end + end + end + else + local vel = self.object:get_velocity() + self.object:set_velocity(vector.new(vel.x, 4, vel.z)) + if self._step.y < pos.y - 0.5 then + self.object:set_velocity(vector.new(vel.x, 0.5, vel.z)) + self._step = nil + end + end +end + +function animalia.on_step(self, dtime, moveresult) mob_core.on_step(self, dtime, moveresult) + mob_core.vitals(self) if mobkit.timer(self, 1) then if self.breeding_cooldown > 0 then self.breeding_cooldown = self.breeding_cooldown - 1 end mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown) end -end - -function better_fauna.on_activate(self, staticdata, dtime_s) - mob_core.on_activate(self, staticdata, dtime_s) - self.gotten = mobkit.recall(self, "gotten") or false - self.attention_span = mobkit.recall(self, "attention_span") or 0 - self.breeding = mobkit.recall(self, "breeding") or false - self.breeding_time = mobkit.recall(self, "breeding_time") or 0 - self.breeding_cooldown = mobkit.recall(self, "breeding_cooldown") or 0 + if mobkit.timer(self, 4) + and #self.target_blacklist > 0 then + table.remove(self.target_blacklist, 1) + end + if self.caught_with_lasso then + if self.lasso_player + and mobkit.is_alive(self) then + local player = self.lasso_player + local pos = mobkit.get_stand_pos(self) + pos.y = pos.y + (self.height * 0.5) + local ppos = player:get_pos() + ppos.y = ppos.y + 1 + if not self.lasso_visual + or not self.lasso_visual:get_luaentity() then + self.lasso_visual = lasso_effect(self, ppos) + else + self.lasso_visual:get_luaentity().anchor_pos = ppos + end + local dist = vector.distance(pos, ppos) + local dist = vector.distance(pos, ppos) + if dist_2d(pos, ppos) > 6 + or abs(ppos.y - pos.y) > 8 then + local p_target = vector.add(pos, vector.multiply(vector.direction(pos, ppos), dist * 0.8)) + local g = -0.18 + local v = vector.new(0, 0, 0) + v.x = (1.0 + (0.005 * dist)) * (p_target.x - pos.x) / dist + v.y = -((1.0 + (0.03 * dist)) * ((ppos.y - 4) - pos.y) / (dist * (g * dist))) + v.z = (1.0 + (0.005 * dist)) * (p_target.z - pos.z) / dist + self.object:add_velocity(v) + end + if player:get_wielded_item():get_name() ~= "animalia:lasso" + or vector.distance(pos, ppos) > 20 then + self.caught_with_lasso = nil + self.lasso_player = nil + if self.lasso_visual then + self.lasso_visual:remove() + self.lasso_visual = nil + end + end + elseif self.lasso_pos + and mobkit.is_alive(self) then + mobkit.remember(self, "lasso_pos", self.lasso_pos) + local pos = mobkit.get_stand_pos(self) + pos.y = pos.y + (self.height * 0.5) + local ppos = self.lasso_pos + if not self.lasso_visual + or not self.lasso_visual:get_luaentity() then + self.lasso_visual = lasso_effect(self, ppos) + else + self.lasso_visual:get_luaentity().anchor_pos = ppos + end + local dist = vector.distance(pos, ppos) + if dist_2d(pos, ppos) > 6 + or abs(ppos.y - pos.y) > 8 then + local p_target = vector.add(pos, vector.multiply(vector.direction(pos, ppos), dist * 0.8)) + local g = -0.18 + local v = vector.new(0, 0, 0) + v.x = (1.0 + (0.005 * dist)) * (p_target.x - pos.x) / dist + v.y = -((1.0 + (0.03 * dist)) * ((ppos.y - 4) - pos.y) / (dist * (g * dist))) + v.z = (1.0 + (0.005 * dist)) * (p_target.z - pos.z) / dist + self.object:add_velocity(v) + end + local objects = minetest.get_objects_inside_radius(ppos, 1) + local is_lasso_attached = false + for _, object in ipairs(objects) do + if object + and object:get_luaentity() + and object:get_luaentity().name == "animalia:lasso_fence_ent" then + is_lasso_attached = true + end + end + if not is_lasso_attached then + + self.caught_with_lasso = nil + self.lasso_pos = nil + if self.lasso_visual then + self.lasso_visual:remove() + self.lasso_visual = nil + end + end + else + if self.lasso_pos then + local objects = minetest.get_objects_inside_radius(self.lasso_pos, 0.4) + for _, object in ipairs(objects) do + if object + and object:get_luaentity() + and object:get_luaentity().name == "animalia:lasso_fence_ent" then + minetest.add_item(object:get_pos(), "animalia:lasso") + object:remove() + end + end + end + self.caught_with_lasso = nil + self.lasso_pos = nil + if self.lasso_visual then + self.lasso_visual:remove() + self.lasso_visual = nil + end + end + end + if mobkit.is_alive(self) then + do_step(self, moveresult) + end end ------------- -- Physics -- ------------- -function better_fauna.lightweight_physics(self) +function animalia.lightweight_physics(self) local vel = self.object:get_velocity() if self.isonground and not self.isinliquid then self.object:set_velocity({x= vel.x> 0.2 and vel.x*mobkit.friction or 0, @@ -581,3 +789,709 @@ function better_fauna.lightweight_physics(self) self.object:set_acceleration({x=0,y=-2.8,z=0}) end end + +------------------ +-- HQ Functions -- +------------------ + +function animalia.hq_eat(self, prty) + local func = function(self) + local pos = mobkit.get_stand_pos(self) + local under = vector.new(pos.x, pos.y - 1, pos.z) + for _, node in ipairs(self.consumable_nodes) do + if node.name == minetest.get_node(under).name then + minetest.set_node(under, {name = node.replacement}) + local def = minetest.registered_nodes[node.name] + local texture = def.tiles[1] + texture = texture .. "^[resize:8x8" + minetest.add_particlespawner({ + amount = 6, + time = 0.1, + minpos = vector.new( + pos.x - 0.5, + pos.y + 0.1, + pos.z - 0.5 + ), + maxpos = vector.new( + pos.x + 0.5, + pos.y + 0.1, + pos.z + 0.5 + ), + minvel = {x=-1, y=1, z=-1}, + maxvel = {x=1, y=2, z=1}, + minacc = {x=0, y=-5, z=0}, + maxacc = {x=0, y=-9, z=0}, + minexptime = 1, + maxexptime = 1, + minsize = 1, + maxsize = 2, + collisiondetection = true, + vertical = false, + texture = texture, + }) + self.gotten = false + mobkit.remember(self, "gotten", self.gotten) + return true + else + return true + end + end + end + mobkit.queue_high(self, func, prty) +end + +-- Wandering -- + +function animalia.hq_go_to_land(self, prty) + local init = false + local tpos = nil + local func = function(self) + if self.liquid_recovery_cooldown > 0 then + self.liquid_recovery_cooldown = self.liquid_recovery_cooldown - 1 + return true + end + if not init then + for i = 1, 359, 15 do + local yaw = math.rad(i) + local dir = minetest.yaw_to_dir(yaw) + tpos = animalia.find_collision(self, dir) + if tpos then + local node = minetest.get_node({x = tpos.x, y = tpos.y + 1, z = tpos.z}) + if node.name == "air" then + break + else + tpos = nil + end + end + end + init = true + end + if tpos then + local pos = mobkit.get_stand_pos(self) + local yaw = self.object:get_yaw() + local tyaw = minetest.dir_to_yaw(vec_dir(pos, tpos)) + if abs(tyaw - yaw) > 0.1 then + mobkit.turn2yaw(self, tyaw) + else + mobkit.go_forward_horizontal(self, self.max_speed * 0.66) + mobkit.animate(self, "walk") + end + if dist_2d(pos, tpos) < 1 + or (not self.isinliquid + and self.isonground) then + return true + end + else + self.liquid_recovery_cooldown = 5 + return true + end + end + mobkit.queue_high(self, func, prty) +end + +function animalia.hq_wander_ranged(self, prty) + local idle_time = 3 + local move_probability = 3 + local func = function(self) + if mobkit.is_queue_empty_low(self) then + local pos = self.object:get_pos() + local random_goal = vector.new( + pos.x + random(-1, 1), + pos.y, + pos.z + random(-1, 1) + ) + local node = minetest.get_node(random_goal) + if minetest.registered_nodes[node.name].drawtype == "liquid" + or minetest.registered_nodes[node.name].walkable then + random_goal = nil + end + if self.lasso_pos + and vec_dist(pos, self.lasso_pos) > 10 then + random_goal = self.lasso_pos + end + if random(move_probability) < 2 + and random_goal then + local _, pos2 = mobkit.get_next_waypoint(self, random_goal) + if pos2 then + random_goal = pos2 + end + mob_core.lq_dumbwalk(self, random_goal, 0.5) + else + animalia.lq_idle(self, idle_time) + end + end + end + mobkit.queue_high(self, func, prty) +end + +function animalia.hq_wander_group(self, prty, group_range) + local idle_time = 3 + local move_probability = 3 + local group_tick = 0 + local func = function(self) + if mobkit.is_queue_empty_low(self) then + group_tick = group_tick - 1 + local pos = self.object:get_pos() + local group_positions = {} + local random_goal = vector.new( + pos.x + random(-1, 1), + pos.y, + pos.z + random(-1, 1) + ) + if group_tick <= 0 + and self.group + and #self.group > 0 then + for _, obj in ipairs(self.group) do + if obj + and mobkit.is_alive(obj) + and #group_positions < 4 then + table.insert(group_positions, obj:get_pos()) + end + end + if #group_positions > 2 then + group_range = group_range + #group_positions + local center = get_average_pos(group_positions) + if center + and ((vec_dist(random_goal, center) > group_range) + or vec_dist(pos, center) > group_range) then + random_goal = pos_to_neighbor(self, center) + end + end + group_tick = 3 + end + local node = minetest.get_node(random_goal) + if minetest.registered_nodes[node.name].drawtype == "liquid" + or minetest.registered_nodes[node.name].walkable then + random_goal = nil + end + if self.lasso_pos + and vec_dist(pos, self.lasso_pos) > 10 then + random_goal = self.lasso_pos + end + if random(move_probability) < 2 + and random_goal then + local _, pos2 = mobkit.get_next_waypoint(self, random_goal) + if pos2 then + random_goal = pos2 + end + mob_core.lq_dumbwalk(self, random_goal, 0.5) + else + animalia.lq_idle(self, idle_time) + end + end + end + mobkit.queue_high(self, func, prty) +end + +-- Breeding -- + +function animalia.hq_breed(self, prty) + local mate = animalia.get_nearby_mate(self, self.name) + if not mate then return end + local func = function(self) + if not mobkit.is_alive(mate) then + return true + end + local pos = mobkit.get_stand_pos(self) + local tpos = mate:get_pos() + local dist = vec_dist(pos, tpos) - math.abs(hitbox(self)[4]) + local speed_factor = clamp(dist, 0.1, 0.65) + if dist < 1.75 then + self.breeding_time = self.breeding_time + 1 + end + if self.breeding_time >= 2 + or mate:get_luaentity().breeding_time >= 2 then + if self.gender == "female" then + mob_core.spawn_child(pos, self.name) + end + self.breeding = false + self.breeding_time = 0 + self.breeding_cooldown = 300 + mobkit.remember(self, "breeding", self.breeding) + mobkit.remember(self, "breeding_time", self.breeding_time) + mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown) + return true + end + if mobkit.is_queue_empty_low(self) then + animalia.go_to_pos(self, tpos, speed_factor) + end + end + mobkit.queue_high(self, func, prty) +end + +function animalia.hq_fowl_breed(self, prty) + local mate = animalia.get_nearby_mate(self, self.name) + if not mate then return end + local speed_factor = 0.5 + local func = function(self) + if mobkit.is_queue_empty_low(self) then + local pos = mobkit.get_stand_pos(self) + local tpos = mate:get_pos() + local dist = vec_dist(pos, tpos) - math.abs(hitbox(self)[4]) + if dist > 1.5 then + speed_factor = 0.5 + else + speed_factor = 0.1 + end + mob_core.goto_next_waypoint(self, tpos, speed_factor) + if dist < 1.75 then + self.breeding_time = self.breeding_time + 1 + end + if self.breeding_time >= 2 + or mate:get_luaentity().breeding_time >= 2 then + if self.gender == "female" then + minetest.add_particlespawner({ + amount = 6, + time = 0.25, + minpos = {x = pos.x - 7/16, y = pos.y - 5/16, z = pos.z - 7/16}, + maxpos = {x = pos.x + 7/16, y = pos.y - 5/16, z = pos.z + 7/16}, + minvel = vector.new(-1, 2, -1), + maxvel = vector.new(1, 5, 1), + minacc = vector.new(0, -9.81, 0), + maxacc = vector.new(0, -9.81, 0), + collisiondetection = true, + texture = "animalia_egg_fragment.png", + }) + mob_core.spawn_child(pos, self.name) + end + self.breeding = false + self.breeding_time = 0 + self.breeding_cooldown = 300 + mobkit.remember(self, "breeding", self.breeding) + mobkit.remember(self, "breeding_time", self.breeding_time) + mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown) + return true + end + end + end + mobkit.queue_high(self, func, prty) +end + +-- Player Interaction -- + + +function animalia.hq_sporadic_flee(self, prty) + local timer = 12 + local func = function(self) + if mobkit.is_queue_empty_low(self) then + local pos = self.object:get_pos() + local random_goal = vector.new( + pos.x + random(-6, 6), + pos.y, + pos.z + random(-6, 6) + ) + local node = minetest.get_node({x = random_goal.x, y = random_goal.y + 1, z = random_goal.z}) + if minetest.registered_nodes[node.name].drawtype == "liquid" then + random_goal = nil + end + if random_goal then + local anim = "walk" + if self.animation["run"] then + anim = "run" + end + mob_core.lq_dumbwalk(self, random_goal, 1, anim) + else + animalia.lq_idle(self, 0.1) + end + end + timer = timer - self.dtime + if timer <= 0 then + return true + end + end + mobkit.queue_high(self, func, prty) +end + +function animalia.hq_attack(self, prty, target) + local func = function(self) + if not mobkit.is_alive(target) then + return true + end + mob_core.punch_timer(self) + local pos = mobkit.get_stand_pos(self) + local tpos = target:get_pos() + if not is_on_ground(target) then + table.insert(self.target_blacklist, target) + return true + end + local can_punch = is_within_reach(self, target) + if mobkit.is_queue_empty_low(self) then + animalia.go_to_pos(self, tpos, 1, "run") + end + if self.punch_timer <= 0 + and can_punch then + target:punch(self.object, 1.0, { + full_punch_interval = 0.1, + damage_groups = {fleshy = self.damage} + }, nil) + mob_core.knockback(self, target) + mob_core.punch_timer(self, self.punch_cooldown or 1) + return true + end + end + mobkit.queue_high(self, func, prty) +end + +function animalia.hq_follow_player(self, prty, player, force) -- Follow Player + if not player then return end + if not force + and not mob_core.follow_holding(self, player) then return end + local func = function(self) + if not mobkit.is_alive(player) then + return true + end + local pos = mobkit.get_stand_pos(self) + local tpos = player:get_pos() + if mob_core.follow_holding(self, player) + or force then + self.status = mobkit.remember(self, "status", "following") + local dist = vec_dist(pos, tpos) + local yaw = self.object:get_yaw() + local tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos)) + if dist > self.view_range then + self.status = mobkit.remember(self, "status", "") + return true + end + if mobkit.is_queue_empty_low(self) then + if vec_dist(pos, tpos) > hitbox(self)[4] + 2 then + animalia.go_to_pos(self, tpos, 0.6) + else + mobkit.lq_idle(self, 0.1, "stand") + end + end + elseif mobkit.is_queue_empty_low(self) then + self.status = mobkit.remember(self, "status", "") + mobkit.lq_idle(self, 0.1, "stand") + return true + end + end + mobkit.queue_high(self, func, prty) +end + +------------------------------- +-- Mob Specific HQ Functions -- +------------------------------- + +-- Cat -- + +function animalia.hq_find_and_break_glass(self, prty) + local timer = 6 + local moving = false + local pos2 = nil + mobkit.clear_queue_low(self) + local func = function(self) + local pos = mobkit.get_stand_pos(self) + if not pos2 then + local nodes = minetest.find_nodes_in_area( + vector.subtract(pos, 8), + vector.add(pos, 8), + {"vessels:glass_bottle", "vessels:drinking_glass"} + ) + if #nodes > 0 then + pos2 = nodes[1] + end + end + if not pos2 then return true end + timer = timer - self.dtime + if mobkit.is_queue_empty_low(self) then + if dist_2d(pos, pos2) > 0.5 then + animalia.go_to_pos(self, pos2, 0.35) + end + end + if dist_2d(pos, pos2) <= 0.5 then + mobkit.lq_idle(self, 0.7, "smack") + minetest.remove_node(pos2) + minetest.add_item(pos2, "vessels:glass_fragments") + if minetest.get_node(pos2).name == "air" then + return true + end + end + if timer < 0 then return true end + end + mobkit.queue_high(self, func, prty) +end + +function animalia.hq_walk_in_front_of_player(self, prty, player) + if not player then return end + local can_reach = false + local path_data = nil + local timer = 8 + local func = function(self) + if not mobkit.is_alive(player) then + return true + end + local pos = mobkit.get_stand_pos(self) + local tpos = player:get_pos() + local dir = player:get_look_dir() + tpos.x = tpos.x + dir.x + tpos.z = tpos.z + dir.z + self.status = mobkit.remember(self, "status", "following") + local dist = vec_dist(pos, tpos) + local yaw = self.object:get_yaw() + local tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos)) + if dist > self.view_range then + self.status = mobkit.remember(self, "status", "") + return true + end + if mobkit.is_queue_empty_low(self) then + if vec_dist(pos, tpos) > hitbox(self)[4] + 0.5 then + if not can_reach then + can_reach, path_data = animalia.can_reach(self, tpos) + else + animalia.lq_dumb_follow_path(self, path_data, 1, "run") + end + else + can_reach = false + path_data = nil + mobkit.lq_idle(self, 0.1, "stand") + end + else + can_reach = false + path_data = nil + end + timer = timer - self.dtime + if timer < 0 then return true end + end + mobkit.queue_high(self, func, prty) +end + +-- Horse -- + +function animalia.hq_mount_logic(self, prty) + local tvel = 0 + local rearing = false + local jumping = false + local anim = "stand" + local func = function(self) + if not self.driver then return true end + -- if horse is rearing, stop moving + if rearing then + if mobkit.timer(self, 1.5) then + rearing = false + end + return + end + -- Controls + local vel = self.object:get_velocity() + local ctrl = self.driver:get_player_control() + if ctrl.up then + tvel = self.speed + if ctrl.aux1 then + tvel = self.speed * 2 + end + elseif tvel < 0.25 or tvel == 0 then + tvel = 0 + self.object:set_velocity({ + x = 0, + y = vel.y, + z = 0 + }) + anim = "stand" + end + if self.isonground then + if ctrl.jump then + jumping = true + vel.y = self.jump_power + 4.405 + else + jumping = false + end + end + -- Physics and Animation + if not ctrl.up + and self.isonground then + tvel = tvel * 0.75 + elseif not self.isonground then + if self.isinliquid then + tvel = tvel * 0.4 + vel.y = vel.y * 0.4 + elseif jumping then + tvel = tvel * 0.4 + end + end + if tvel > 0 then + if jumping then + anim = "rear_constant" + else + if ctrl.aux1 then + anim = "run" + else + anim = "walk" + end + end + end + if random(1024) < 2 then + tvel = 0 + anim = "rear" + rearing = true + end + mobkit.animate(self, anim) + local tyaw = self.driver:get_look_horizontal() or 0 + self._tyaw = tyaw + self.object:set_yaw(tyaw) + local nvel = vector.multiply(minetest.yaw_to_dir(self.object:get_yaw()), tvel) + self.object:set_velocity({ + x = nvel.x, + y = vel.y, + z = nvel.z + }) + if ctrl.sneak then + mob_core.detach(self.driver, {x = 1, y = 0, z = 1}) + return true + end + end + mobkit.queue_high(self, func, prty) +end + +function animalia.hq_horse_breed(self, prty) + local mate = animalia.get_nearby_mate(self, self.name) + if not mate then return end + local speed_factor = 0.5 + local func = function(self) + if mobkit.is_queue_empty_low(self) then + local pos = mobkit.get_stand_pos(self) + local tpos = mate:get_pos() + local dist = vec_dist(pos, tpos) - math.abs(hitbox(self)[4]) + if dist > 1.5 then + speed_factor = 0.5 + else + speed_factor = 0.1 + end + mob_core.goto_next_waypoint(self, tpos, speed_factor) + if dist < 1.75 then + self.breeding_time = self.breeding_time + 1 + end + if self.breeding_time >= 2 + or mate:get_luaentity().breeding_time >= 2 then + if self.gender == "female" then + local obj = mob_core.spawn_child(pos, self.name) + local ent = obj:get_luaentity() + local tex_no = self.texture_no + if random(2) < 2 then + tex_no = mate:get_luaentity().texture_no + end + mobkit.remember(ent, "texture_no", self.texture_no) + mobkit.remember(ent, "speed", random(mate:get_luaentity().speed, self.speed)) + mobkit.remember(ent, "jump_power", random(mate:get_luaentity().jump_power, self.jump_power)) + mobkit.remember(ent, "max_hp", random(mate:get_luaentity().max_hp, self.max_hp)) + ent.speed = mobkit.recall(ent, "speed") + ent.jump_power = mobkit.recall(ent, "jump_power") + ent.max_hp = mobkit.recall(ent, "max_hp") + ent.object:set_properties({ + texture = ent.textures[ent.texture_no] .. "^" .. mobkit.recall(ent, "pattern") + }) + end + self.breeding = false + self.breeding_time = 0 + self.breeding_cooldown = 300 + mobkit.remember(self, "breeding", self.breeding) + mobkit.remember(self, "breeding_time", self.breeding_time) + mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown) + return true + end + end + end + mobkit.queue_high(self, func, prty) +end + +----------------------- +-- Dynamic Animation -- +----------------------- + +local function clamp_bone_rot(n) -- Fixes issues with bones jittering when yaw clamps + if n < -180 then + n = n + 360 + elseif n > 180 then + n = n - 360 + end + if n < -60 then + n = -60 + elseif n > 60 then + n = 60 + end + return n +end + +local function interp(a, b, w) -- Smoothens bone movement + local pi = math.pi + if math.abs(a - b) > math.deg(pi) then + if a < b then + return ((a + (b - a) * w) + (math.deg(pi) * 2)) + elseif a > b then + return ((a + (b - a) * w) - (math.deg(pi) * 2)) + end + end + return a + (b - a) * w +end + +local function move_head(self, tyaw, pitch) + local data = self.head_data + local _, rot = self.object:get_bone_position(data.bone or "Head.CTRL") + local yaw = self.object:get_yaw() + local look_yaw = clamp_bone_rot(math.deg(yaw - tyaw)) + local look_pitch = 0 + if pitch then + look_pitch = clamp_bone_rot(math.deg(pitch)) + end + if tyaw ~= yaw then + look_yaw = look_yaw * 0.66 + end + local yaw = interp(rot.z, look_yaw, 0.1) + local ptch = interp(rot.x, look_pitch + data.pitch_correction, 0.1) + self.object:set_bone_position(data.bone or "Head.CTRL", data.offset, {x = ptch, y = yaw, z = yaw}) +end + +function animalia.head_tracking(self) + if not self.head_data then return end + local yaw = self.object:get_yaw() + local pos = mobkit.get_stand_pos(self) + local v = vector.add(pos, vector.multiply(yaw2dir(yaw), self.head_data.pivot_h)) + pos.x = v.x + pos.y = pos.y + self.head_data.pivot_v + pos.z = v.z + --[[minetest.add_particle({ + pos = pos, + velocity = {x=0, y=0, z=0}, + acceleration = {x=0, y=0, z=0}, + expirationtime = 0.1, + size = 8, + collisiondetection = false, + vertical = false, + texture = "mob_core_green_particle.png", + playername = "singleplayer" + })]] + if not self.head_tracking then + local objects = minetest.get_objects_inside_radius(pos, 6) + for _, object in ipairs(objects) do + if object:is_player() then + local dir_2_plyr = vector.direction(pos, object:get_pos()) + local yaw_2_plyr = dir2yaw(dir_2_plyr) + if abs(yaw - yaw_2_plyr) < 1 + or abs(yaw - yaw_2_plyr) > 5.3 then + self.head_tracking = object + end + break + end + end + if self._anim == "stand" then + move_head(self, yaw) + else + move_head(self, self._tyaw) + end + else + if not mobkit.exists(self.head_tracking) then + self.head_tracking = nil + return + end + local ppos = self.head_tracking:get_pos() + ppos.y = ppos.y + 1.4 + local dir = vector.direction(pos, ppos) + local tyaw = minetest.dir_to_yaw(dir) + if abs(yaw - tyaw) > 1 + and abs(yaw - tyaw) < 5.3 then + self.head_tracking = nil + dir.y = 0 + return + end + move_head(self, tyaw, dir.y) + end +end \ No newline at end of file diff --git a/api/legacy_convert.lua b/api/legacy_convert.lua new file mode 100644 index 0000000..410bb1b --- /dev/null +++ b/api/legacy_convert.lua @@ -0,0 +1,81 @@ +-------------------------------------- +-- Convert Better Fauna to Animalia -- +-------------------------------------- + +for i = 1, #animalia.mobs do + local new_mob = animalia.mobs[i] + local old_mob = "better_fauna:" .. new_mob:split(":")[2] + minetest.register_entity(":" .. old_mob, { + on_activate = mob_core.on_activate + }) + minetest.register_alias_force("better_fauna:spawn_" .. new_mob:split(":")[2], "animalia:spawn_" .. new_mob:split(":")[2]) +end + +minetest.register_globalstep(function(dtime) + local mobs = minetest.luaentities + for _, mob in pairs(mobs) do + if mob + and mob.name:match("better_fauna:") then + if mob.name:find("lasso_fence_ent") then + local pos = mob.object:get_pos() + if pos then + minetest.add_entity(pos, "animalia:lasso_fence_ent") + end + mob.object:remove() + elseif mob.name:find("lasso_visual") then + if pos then + minetest.add_entity(pos, "animalia:lasso_visual") + end + mob.object:remove() + end + for i = 1, #animalia.mobs do + local ent = animalia.mobs[i] + local new_name = ent:split(":")[2] + local old_name = mob.name:split(":")[2] + if new_name == old_name then + local pos = mob.object:get_pos() + if pos then + local new_mob = minetest.add_entity(pos, ent) + local mem = nil + if mob.memory then + mem = mob.memory + end + minetest.after(0.1, function() + if mem then + new_mob:get_luaentity().memory = mem + new_mob:get_luaentity():on_activate(new_mob, nil, dtime) + end + end) + end + mob.object:remove() + end + end + end + end +end) + + +-- Tools + +minetest.register_alias_force("better_fauna:net", "animalia:net") +minetest.register_alias_force("better_fauna:lasso", "animalia:lasso") +minetest.register_alias_force("better_fauna:cat_toy", "animalia:cat_toy") +minetest.register_alias_force("better_fauna:saddle", "animalia:saddle") +minetest.register_alias_force("better_fauna:shears", "animalia:shears") + +-- Drops + +minetest.register_alias_force("better_fauna:beef_raw", "animalia:beef_raw") +minetest.register_alias_force("better_fauna:beef_cooked", "animalia:beef_cooked") +minetest.register_alias_force("better_fauna:bucket_milk", "animalia:bucket_milk") +minetest.register_alias_force("better_fauna:leather", "animalia:leather") +minetest.register_alias_force("better_fauna:chicken_egg", "animalia:chicken_egg") +minetest.register_alias_force("better_fauna:chicken_raw", "animalia:chicken_raw") +minetest.register_alias_force("better_fauna:chicken_cooked", "animalia:chicken_cooked") +minetest.register_alias_force("better_fauna:feather", "animalia:feather") +minetest.register_alias_force("better_fauna:mutton_raw", "animalia:mutton_raw") +minetest.register_alias_force("better_fauna:mutton_cooked", "animalia:mutton_cooked") +minetest.register_alias_force("better_fauna:porkchop_raw", "animalia:porkchop_raw") +minetest.register_alias_force("better_fauna:porkchop_cooked", "animalia:porkchop_cooked") +minetest.register_alias_force("better_fauna:turkey_raw", "animalia:turkey_raw") +minetest.register_alias_force("better_fauna:turkey_cooked", "animalia:turkey_cooked") \ No newline at end of file diff --git a/craftitems.lua b/craftitems.lua index ed78d24..680b21a 100644 --- a/craftitems.lua +++ b/craftitems.lua @@ -3,67 +3,344 @@ ---------------- ---- Ver 0.1 --- +---------------- +-- Animal Net -- Used to capture and store animals +---------------- -minetest.register_craftitem("better_fauna:net", { +minetest.register_craftitem("animalia:net", { description = "Animal Net", - inventory_image = "better_fauna_net.png", + inventory_image = "animalia_net.png", + stack_max = 1, on_secondary_use = function(itemstack, placer, pointed_thing) - if pointed_thing.type == "object" then - if pointed_thing.ref:is_player() then + if pointed_thing.type == "object" then + if pointed_thing.ref:is_player() then return end + local ent = pointed_thing.ref:get_luaentity() + if not ent.name:match("^animalia:") or not ent.catch_with_net then return end - local ent = pointed_thing.ref:get_luaentity() - if not ent.name:match("^better_fauna:") - or not ent.catch_with_net then - return - end local ent_name = mob_core.get_name_proper(ent.name) + local ent_gender = mob_core.get_name_proper(ent.gender) local meta = itemstack:get_meta() - if not meta:get_string("mob") - or meta:get_string("mob") == "" then + if not meta:get_string("mob") or meta:get_string("mob") == "" then + if placer:get_wielded_item():get_count() > 1 then + if placer:get_inventory():room_for_item("main", {name = "animalia:net"}) then + itemstack:take_item(1) + placer:get_inventory():add_item("main", "animalia:net") + return itemstack + else + return + end + end meta:set_string("mob", ent.name) meta:set_string("staticdata", ent:get_staticdata()) - local desc = "Animal Net \n" .. minetest.colorize("#a9a9a9", ent_name) + local desc = "Animal Net \n" .. minetest.colorize("#a9a9a9", ent_name) .. "\n" .. minetest.colorize("#a9a9a9", ent_gender) + if ent.name == "animalia:cat" + and ent.trust + and ent.trust[placer:get_player_name()] then + desc = desc .. "\n" .. minetest.colorize("#a9a9a9", ent.trust[placer:get_player_name()]) + end meta:set_string("description", desc) placer:set_wielded_item(itemstack) ent.object:remove() return itemstack else - minetest.chat_send_player(placer:get_player_name(), "This Net already contains a "..ent_name) + minetest.chat_send_player(placer:get_player_name(), + "This Net already contains a " .. + mob_core.get_name_proper( + meta:get_string("mob"))) return end end - end, - on_place = function(itemstack, placer, pointed_thing) - local pos = pointed_thing.above - if pos then - local under = minetest.get_node(pointed_thing.under) - local node = minetest.registered_nodes[under.name] - if node and node.on_rightclick then - return node.on_rightclick(pointed_thing.under, under, placer, itemstack) - end - if pos - and not minetest.is_protected(pos, placer:get_player_name()) then - local mob = itemstack:get_meta():get_string("mob") - local staticdata = itemstack:get_meta():get_string("staticdata") - if mob ~= "" then - pos.y = pos.y + math.abs(minetest.registered_entities[mob].collisionbox[2]) - minetest.add_entity(pos, mob, staticdata) - itemstack:get_meta():set_string("mob", nil) - itemstack:get_meta():set_string("staticdata", nil) - itemstack:get_meta():set_string("description", "Animal Net") - end - end - end - return itemstack - end + end, + on_place = function(itemstack, placer, pointed_thing) + local pos = pointed_thing.above + if pos then + local under = minetest.get_node(pointed_thing.under) + local node = minetest.registered_nodes[under.name] + if node and node.on_rightclick then + return node.on_rightclick(pointed_thing.under, under, placer, + itemstack) + end + if pos and not minetest.is_protected(pos, placer:get_player_name()) then + local mob = itemstack:get_meta():get_string("mob") + local staticdata = itemstack:get_meta():get_string("staticdata") + if mob ~= "" then + pos.y = pos.y + + math.abs( + minetest.registered_entities[mob] + .collisionbox[2]) + minetest.add_entity(pos, mob, staticdata) + itemstack:get_meta():set_string("mob", nil) + itemstack:get_meta():set_string("staticdata", nil) + itemstack:get_meta():set_string("description", "Animal Net") + end + end + end + return itemstack + end }) minetest.register_craft({ - output = "better_fauna:net", + output = "animalia:net", + recipe = { + {"farming:string", "", "farming:string"}, + {"farming:string", "", "farming:string"}, + {"group:stick", "farming:string", ""} + } +}) + +----------- +-- Lasso -- Used to pull animals around, and can be attached to fences +----------- + +local function is_lasso_in_use(player) + for _, ent in pairs(minetest.luaentities) do + if ent.name + and ent.name:match("^animalia:") then + if ent.lasso_player + and ent.lasso_player == player then + return true + end + end + end + return false +end + + +minetest.register_entity("animalia:lasso_visual", { + hp_max = 1, + armor_groups = {immortal = 1}, + physical = false, + collisionbox = {0, 0, 0, 0, 0, 0}, + visual = "mesh", + mesh = "animalia_lasso.b3d", + visual_size = {x = 2, y = 2}, + textures = {"animalia_lasso_cube.png"}, + is_visible = true, + makes_footstep_sound = false, + glow = 1, + on_step = function(self, dtime) + self.object:set_armor_groups({immortal = 1}) + if not self.parent + or not self.anchor_pos + or (self.parent + and (not mobkit.is_alive(self.parent) + or not self.parent:get_luaentity().caught_with_lasso)) then + self.object:remove() + return + end + local pos = mobkit.get_stand_pos(self.parent) + pos.y = pos.y + (self.parent:get_luaentity().height * 0.5) + self.object:set_pos(self.anchor_pos) + local rot = vector.dir_to_rotation(vector.direction(self.anchor_pos, pos)) + self.object:set_rotation(rot) + self.object:set_properties({ + visual_size = {x = 6, z = 10 * vector.distance(self.anchor_pos, pos), y = 6} + }) + end +}) + +minetest.register_entity("animalia:lasso_fence_ent", { + hp_max = 1, + physical = false, + collisionbox = {-0.25,-0.25,-0.25, 0.25,0.25,0.25}, + visual = "cube", + visual_size = {x = 0.3, y = 0.3}, + mesh = "model", + textures = { + "animalia_lasso_cube.png", + "animalia_lasso_cube.png", + "animalia_lasso_cube.png", + "animalia_lasso_cube.png", + "animalia_lasso_cube.png", + "animalia_lasso_cube.png", + }, + makes_footstep_sound = false, + on_step = function(self) + if not self.parent + or not self.parent:get_luaentity() + or not self.parent:get_luaentity().lasso_pos then + self.object:remove() + return + end + local pos = self.object:get_pos() + local node = minetest.get_node(pos) + if not minetest.registered_nodes[node.name].walkable + or minetest.get_item_group(node.name, "fence") < 1 then + local ent = self.parent:get_luaentity() + ent.lasso_pos = mobkit.remember(ent, "lasso_pos", nil) + ent.caught_with_lasso = nil + if ent.lasso_visual then + ent.lasso_visual:remove() + ent.lasso_visual = nil + end + minetest.add_item(self.object:get_pos(), "animalia:lasso") + self.object:remove() + return + end + end, + on_rightclick = function(self) + if self.parent then + local ent = self.parent:get_luaentity() + ent.lasso_pos = mobkit.remember(ent, "lasso_pos", nil) + ent.caught_with_lasso = nil + if ent.lasso_visual then + ent.lasso_visual:remove() + ent.lasso_visual = nil + end + end + local dirs = { + vector.new(1, 0, 0), + vector.new(-1, 0, 0), + vector.new(0, 1, 0), + vector.new(0, -1, 0), + vector.new(0, 0, 1), + vector.new(0, 0, -1), + } + for i = 1, 6 do + local pos = vector.add(self.object:get_pos(), dirs[i]) + local name = minetest.get_node(pos).name + if not minetest.registered_nodes[name].walkable then + minetest.add_item(pos, "animalia:lasso") + break + end + end + self.object:remove() + end, + on_punch = function(self) + if self.parent then + local ent = self.parent:get_luaentity() + ent.lasso_pos = mobkit.remember(ent, "lasso_pos", nil) + ent.caught_with_lasso = nil + if ent.lasso_visual then + ent.lasso_visual:remove() + ent.lasso_visual = nil + end + end + local dirs = { + vector.new(1, 0, 0), + vector.new(-1, 0, 0), + vector.new(0, 1, 0), + vector.new(0, -1, 0), + vector.new(0, 0, 1), + vector.new(0, 0, -1), + } + for i = 1, 6 do + local pos = vector.add(self.object:get_pos(), dirs[i]) + local name = minetest.get_node(pos).name + if not minetest.registered_nodes[name].walkable then + minetest.add_item(pos, "animalia:lasso") + break + end + end + self.object:remove() + end +}) + +minetest.register_craftitem("animalia:lasso", { + description = "Lasso", + inventory_image = "animalia_lasso.png", + on_secondary_use = function(itemstack, placer, pointed_thing) + if pointed_thing.type == "object" then + if pointed_thing.ref:is_player() then return end + local ent = pointed_thing.ref:get_luaentity() + if not ent.name:match("^animalia:") or not ent.catch_with_net then + return + end + if not ent.caught_with_lasso + and not is_lasso_in_use(placer) then + ent.caught_with_lasso = true + ent.lasso_player = placer + elseif ent.lasso_player + and ent.lasso_player == placer then + ent.caught_with_lasso = nil + ent.lasso_player = nil + end + end + end, + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type == "node" then + local pos = minetest.get_pointed_thing_position(pointed_thing) + if minetest.get_item_group(minetest.get_node(pos).name, "fence") > 0 then + local objects = minetest.get_objects_inside_radius(placer:get_pos(), 21) + for _, obj in ipairs(objects) do + if obj:get_luaentity() + and obj:get_luaentity().lasso_player + and obj:get_luaentity().lasso_player == placer then + obj:get_luaentity().lasso_pos = pos + obj:get_luaentity().lasso_player = nil + local object = minetest.add_entity(pos, "animalia:lasso_fence_ent") + object:get_luaentity().parent = obj + itemstack:take_item(1) + break + end + end + end + end + return itemstack + end +}) + +minetest.register_craft({ + output = "animalia:lasso", + recipe = { + {"", "farming:string", "farming:string"}, + {"", "animalia:leather", "farming:string"}, + {"farming:string", "", ""} + } +}) + +------------- +-- Cat Toy -- Used to quickly increase trust with Cats +------------- + +minetest.register_craftitem("animalia:cat_toy", { + description = "Cat Toy", + inventory_image = "animalia_cat_toy.png", + wield_image = "animalia_cat_toy.png^[transformFYR90", +}) + +minetest.register_craft({ + output = "animalia:cat_toy", + recipe = { + {"", "", "farming:string"}, + {"", "group:stick", "farming:string"}, + {"group:stick", "", "group:feather"} + } +}) + +------------ +-- Saddle -- Can be attached to a tamed Horse to make it ridable +------------ + +minetest.register_craftitem("animalia:saddle", { + description = "Saddle", + inventory_image = "animalia_saddle.png", +}) + +minetest.register_craft({ + output = "animalia:saddle", + recipe = { + {"animalia:leather", "animalia:leather", "animalia:leather"}, + {"animalia:leather", "default:steel_ingot", "animalia:leather"}, + {"farming:string", "", "farming:string"} + } +}) + +------------ +-- Shears -- Used to shear Sheep +------------ + +minetest.register_tool("animalia:shears", { + description = "Shears", + inventory_image = "animalia_shears.png", + groups = {flammable = 2} +}) + +minetest.register_craft({ + output = "animalia:shears", recipe = { - {"farming:string", "", "farming:string"}, - {"farming:string", "", "farming:string"}, - {"group:stick", "farming:string", ""} + {"", "default:steel_ingot", ""}, + {"", "animalia:leather", "default:steel_ingot"} } }) \ No newline at end of file diff --git a/init.lua b/init.lua index 7980a4c..a6e9449 100644 --- a/init.lua +++ b/init.lua @@ -1,32 +1,229 @@ -better_fauna = {} +animalia = {} +better_fauna = animalia -better_fauna.walkable_nodes = {} +animalia.mobkit_mobs = {} +animalia.walkable_nodes = {} +animalia.spawn_interval = 60 +animalia.mobs = {} minetest.register_on_mods_loaded(function() + for name in pairs(minetest.registered_entities) do + local mob = minetest.registered_entities[name] + if mob.logic or mob.brainfunc then + table.insert(animalia.mobkit_mobs, name) + end + end for name in pairs(minetest.registered_nodes) do if name ~= "air" and name ~= "ignore" then if minetest.registered_nodes[name].walkable then - table.insert(better_fauna.walkable_nodes, name) + table.insert(animalia.walkable_nodes, name) end end end end) -better_fauna.frame_blend = 0 +animalia.grassland_biomes = {} + +animalia.temperate_biomes = {} + +animalia.boreal_biomes = {} + +animalia.tropical_biomes = {} + +minetest.register_on_mods_loaded(function() + for name in pairs(minetest.registered_biomes) do + local biome = minetest.registered_biomes[name] + if name:find("forest") then + local turf = biome.node_top + local heat = biome.heat_point or 0 + local humidity = biome.humidity_point or 50 + if turf then + if turf:find("dirt") then + if heat >= 40 + and humidity >= 60 then + table.insert(animalia.tropical_biomes, name) + else + table.insert(animalia.boreal_biomes, name) + end + elseif turf:find("grass") then + if heat >= 40 then + table.insert(animalia.boreal_biomes, name) + else + table.insert(animalia.temperate_biomes, name) + end + elseif turf:find("litter") then + if heat >= 40 + and humidity >= 60 then + table.insert(animalia.tropical_biomes, name) + else + table.insert(animalia.temperate_biomes, name) + end + elseif turf:find("snow") then + table.insert(animalia.temperate_biomes, name) + end + end + else + local turf = biome.node_top + local heat = biome.heat_point or 0 + --local humidity = biome.humidity_point or 50 + if turf then + if turf:find("grass") + or (turf:find("dirt") + and heat < 60) then + table.insert(animalia.grassland_biomes, name) + end + end + end + end +end) + +animalia.frame_blend = 0 if minetest.has_feature("object_step_has_moveresult") then - better_fauna.frame_blend = 0.3 + animalia.frame_blend = 0.3 end -local path = minetest.get_modpath("better_fauna") +function animalia.register_mob(name, def) + minetest.register_entity("animalia:".. name, { + physical = true, + collide_with_objects = true, + visual = "mesh", + makes_footstep_sound = true, + static_save = true, + timeout = 0, + -- Stats + max_hp = def.health or 20, + armor_groups = {fleshy = def.fleshy}, + view_range = def.view_range or 32, + lung_capacity = def.lung_capacity or 10, + -- Visual + collisionbox = def.collisionbox, + visual_size = def.visual_size, + mesh = def.mesh, + textures = def.textures or nil, + scale_stage1 = def.scale_stage1 or 0.75, + scale_stage2 = def.scale_stage2 or 0.85, + scale_stage3 = def.scale_stage3 or 0.95, + female_textures = def.female_textures or nil, + male_textures = def.male_textures or nil, + child_textures = def.child_textures or nil, + animation = def.animations, + -- Physics + ignore_liquidflag = false, + push_on_collide = true, + buoyancy = def.buoyancy or 0.25, + max_speed = def.speed, + jump_height = def.jump_height or 1.1, + stepheight = def.stepheight or 0, + max_fall = def.max_fall or 2, + -- Attributes + sounds = def.sounds, + obstacle_avoidance_range = def.obstacle_avoidance_range or nil, + surface_avoidance_range = def.surface_avoidance_range or nil, + floor_avoidance_range = def.floor_avoidance_range or nil, + fall_damage = def.fall_damage or true, + igniter_damage = def.igniter_damage or 2, + reach = def.reach or 2, + damage = def.damage or 2, + knockback = def.knockback or 4, + punch_cooldown = def.punch_cooldown or 1, + core_growth = def.growth or true, + catch_with_net = def.catch_with_net or true, + driver_scale = def.driver_scale or nil, + player_rotation = def.player_rotation or nil, + driver_attach_at = def.driver_attach_at or nil, + driver_attach_bone = def.driver_attach_bone or nil, + driver_eye_offset = def.driver_eye_offset or nil, + -- Behavior + defend_owner = def.defend_owner, + follow = def.follow, + consumable_nodes = def.consumable_nodes or nil, + drops = def.drops, + -- Functions + head_data = def.head_data or nil, + register_targets = def.register_targets or nil, + physics = def.physics or nil, + logic = def.logic, + get_staticdata = mobkit.statfunc, + on_step = def.on_step, + on_activate = def.on_activate, + on_rightclick = def.on_rightclick, + on_punch = def.on_punch, + }) + table.insert(animalia.mobs, "animalia:" .. name) +end + +local path = minetest.get_modpath("animalia") dofile(path.."/api/api.lua") dofile(path.."/craftitems.lua") +dofile(path.."/mobs/cat.lua") dofile(path.."/mobs/chicken.lua") dofile(path.."/mobs/cow.lua") +dofile(path.."/mobs/horse.lua") dofile(path.."/mobs/pig.lua") dofile(path.."/mobs/sheep.lua") dofile(path.."/mobs/turkey.lua") +dofile(path.."/mobs/wolf.lua") +dofile(path.."/api/legacy_convert.lua") + +animalia.chunks_since_last_spawn = 0 + +local chunk_spawn_add_int = tonumber(minetest.settings:get("chunk_spawn_add_int")) or 32 + +animalia.spawn_queue = {} + +minetest.register_on_generated(function(minp, maxp) + animalia.chunks_since_last_spawn = animalia.chunks_since_last_spawn + 1 + local heightmap = minetest.get_mapgen_object("heightmap") + if not heightmap then return end + local pos = { + x = minp.x + math.floor((maxp.x - minp.x) / 2), + y = minp.y, + z = minp.z + math.floor((maxp.z - minp.z) / 2) + } + local hm_i = (pos.x - minp.x + 1) + (((pos.z - minp.z)) * 80) + pos.y = heightmap[hm_i] + if animalia.chunks_since_last_spawn > chunk_spawn_add_int + and pos.y > 0 then + local heightmap = minetest.get_mapgen_object("heightmap") + if not heightmap then return end + local center = { + x = math.floor(minp.x + ((maxp.x - minp.x) * 0.5) + 0.5), + y = minp.y, + z = math.floor(minp.z + ((maxp.z - minp.z) * 0.5) + 0.5), + } + local light = minetest.get_natural_light(center) + while center.y < maxp.y + and light < 10 do + center.y = center.y + 1 + light = minetest.get_natural_light(center) + end + table.insert(animalia.spawn_queue, {pos = center, mob = animalia.mobs[math.random(#animalia.mobs)]}) + animalia.chunks_since_last_spawn = 0 + end +end) + +local chunk_spawn_queue_int = tonumber(minetest.settings:get("chunk_spawn_queue_int")) or 10 + +local function spawn_queued() + local queue = animalia.spawn_queue + if #queue > 0 then + for i = #queue, 1, -1 do + local def = mob_core.registered_spawns[queue[i].mob].def + mob_core.spawn_at_pos( + queue[i].pos, + def.name, + def.nodes or nil, + def.group or 1, + def.optional or nil + ) + table.remove(animalia.spawn_queue, i) + end + end + minetest.after(chunk_spawn_queue_int, spawn_queued) +end +minetest.after(chunk_spawn_queue_int, spawn_queued) -minetest.log("action", "[MOD] Better Fauna [0.1] loaded") +minetest.log("action", "[MOD] Animalia [0.2] loaded") diff --git a/mobs/cat.lua b/mobs/cat.lua new file mode 100644 index 0000000..bff0d5f --- /dev/null +++ b/mobs/cat.lua @@ -0,0 +1,329 @@ +--------- +-- Cat -- +--------- + +local clamp_bone_rot = animalia.clamp_bone_rot + +local interp = animalia.interp + +local follow = { + "animalia:poultry_raw" +} + +if minetest.registered_items["ethereal:fish_raw"] then + follow = { + "ethereal:fish_raw", + "animalia:poultry_raw" + } +end + +local function cat_logic(self) + + if self.hp <= 0 then + mob_core.on_die(self) + return + end + + if self._anim == "run" then + local pos = self.object:get_pos() + minetest.add_particlespawner({ + amount = 1, + time = 0.25, + minpos = pos, + maxpos = pos, + minvel = vector.new(-1, 1, -1), + maxvel = vector.new(1, 2, 1), + minacc = vector.new(0, -9.81, 0), + maxacc = vector.new(0, -9.81, 0), + minsize = 0.25, + maxsize = 0.5, + collisiondetection = true, + texture = "default_dirt.png", + }) + end + + animalia.head_tracking(self, 0.25, 0.25) + + if mobkit.timer(self, 1) then + + local prty = mobkit.get_queue_priority(self) + local player = mobkit.get_nearby_player(self) + local trust = 0 + + mob_core.random_sound(self, 30) + mob_core.growth(self) + + if player then + if not self.trust[player:get_player_name()] then + self.trust[player:get_player_name()] = 0 + mobkit.remember(self, "trust", self.trust) + else + trust = self.trust[player:get_player_name()] + end + end + + if self.trust_cooldown > 0 then + self.trust_cooldown = mobkit.remember(self, "trust_cooldown", self.trust_cooldown - 1) + end + + if self.interact_sound_cooldown > 0 then + self.interact_sound_cooldown = self.interact_sound_cooldown - 1 + end + + if self.owner + and self.trust[self.owner] > 7 then + if prty < 22 + and self.order == "sit" then + if not mobkit.is_queue_empty_high(self) then + mobkit.clear_queue_high(self) + end + mobkit.animate(self, "sit") + return + end + + if prty < 21 + and self.owner_target then + if not mob_core.shared_owner(self, self.owner_target) then + animalia.hq_attack(self, 21, self.owner_target) + end + end + + if prty < 20 + and self.order == "follow" + and minetest.get_player_by_name(self.owner) then + local owner = minetest.get_player_by_name(self.owner) + animalia.hq_follow_player(self, 20, owner, true) + end + + if prty < 4 + and self.breeding then + animalia.hq_breed(self, 3) + end + end + + if prty < 5 + and self.isinliquid then + animalia.hq_go_to_land(self, 5) + end + + if prty < 3 + and player then + if player:get_velocity() + and vector.length(player:get_velocity()) < 2 then + if mob_core.follow_holding(self, player) + and trust >= 4 then + animalia.hq_follow_player(self, 3, player) + end + elseif player:get_wielded_item():get_name() == "animalia:cat_toy" then + animalia.hq_follow_player(self, 3, player, true) + return + end + end + + if player + and prty == 3 + and not mob_core.follow_holding(self, player) + and player:get_wielded_item():get_name() ~= "animalia:cat_toy" then + mobkit.clear_queue_high(self) + end + + if prty < 2 + and player + and trust > 4 then + local r = math.random(48) + if r < 2 then + animalia.hq_walk_in_front_of_player(self, 2, player) + elseif r < 3 then + animalia.hq_find_and_break_glass(self, 2) + end + end + + if mobkit.is_queue_empty_high(self) then + animalia.hq_wander_ranged(self, 0) + end + end +end + +animalia.register_mob("cat", { + -- Stats + health = 10, + fleshy = 100, + view_range = 32, + lung_capacity = 10, + -- Visual + collisionbox = {-0.2, 0, -0.2, 0.2, 0.4, 0.2}, + visual_size = {x = 6, y = 6}, + scale_stage1 = 0.5, + scale_stage2 = 0.65, + scale_stage3 = 0.80, + mesh = "animalia_cat.b3d", + textures = { + "animalia_cat_1.png", + "animalia_cat_2.png", + "animalia_cat_3.png", + "animalia_cat_4.png" + }, + animations = { + stand = {range = {x = 1, y = 39}, speed = 10, frame_blend = 0.3, loop = true}, + walk = {range = {x = 50, y = 90}, speed = 45, frame_blend = 0.3, loop = true}, + run = {range = {x = 100, y = 130}, speed = 50, frame_blend = 0.3, loop = true}, + sit = {range = {x = 140, y = 180}, speed = 10, frame_blend = 0.3, loop = true}, + smack = {range = {x = 190, y = 210}, speed = 40, frame_blend = 0.1, loop = true}, + }, + -- Physics + speed = 8, + max_fall = 4, + -- Attributes + sounds = { + alter_child_pitch = true, + random = { + name = "animalia_cat_idle", + gain = 0.25, + distance = 8 + }, + purr = { + name = "animalia_cat_purr", + gain = 0.6, + distance = 8 + }, + hurt = { + name = "animalia_cat_hurt", + gain = 0.25, + distance = 8 + }, + death = { + name = "animalia_cat_hurt", + gain = 0.25, + distance = 8 + } + }, + reach = 2, + damage = 3, + knockback = 2, + punch_cooldown = 1, + -- Behavior + defend_owner = true, + follow = follow, + -- Functions + head_data = { + offset = {x = 0, y = 0.17, z = 0}, + pitch_correction = -20, + pivot_h = 0.35, + pivot_v = 0.2 + }, + logic = cat_logic, + get_staticdata = mobkit.statfunc, + on_step = animalia.on_step, + on_activate = function(self, staticdata, dtime_s) + animalia.on_activate(self, staticdata, dtime_s) + self.trust = mobkit.recall(self, "trust") or {} + self.trust_cooldown = mobkit.recall(self, "trust_cooldown") or 0 + self.interact_sound_cooldown = 0 + end, + on_rightclick = function(self, clicker) + local item = clicker:get_wielded_item():get_name() + if item == "animalia:net" then return end + if not self.trust[clicker:get_player_name()] then + self.trust[clicker:get_player_name()] = 0 + mobkit.remember(self, "trust", self.trust) + end + local trust = self.trust[clicker:get_player_name()] + local pos = self.object:get_pos() + local prt_pos = vector.new(pos.x, pos.y + 0.5, pos.z) + local minppos = vector.add(prt_pos, 1) + local maxppos = vector.subtract(prt_pos, 1) + if animalia.feed_tame(self, clicker, math.random(3, 5), trust >= 10, trust >= 10) then + if self.trust_cooldown <= 0 + and trust < 10 then + self.trust[clicker:get_player_name()] = trust + 1 + self.trust_cooldown = mobkit.remember(self, "trust_cooldown", 60) + mobkit.remember(self, "trust", self.trust) + animalia.particle_spawner(prt_pos, "mob_core_green_particle.png", "float", minppos, maxppos) + end + return + end + mob_core.protect(self, clicker, true) + mob_core.nametag(self, clicker, true) + if mobkit.get_queue_priority(self) == 3 + and clicker:get_wielded_item():get_name() == "animalia:cat_toy" then + if trust < 10 then + self.trust[clicker:get_player_name()] = trust + 1 + mobkit.remember(self, "trust", self.trust) + animalia.particle_spawner(prt_pos, "mob_core_green_particle.png", "float", minppos, maxppos) + if self.interact_sound_cooldown <= 0 then + self.sounds["purr"].gain = 1 + self.interact_sound_cooldown = 3 + mobkit.make_sound(self, "purr") + end + end + end + + if not self.owner + or clicker:get_player_name() ~= self.owner then + return + end + if clicker:get_player_control().sneak then + if self.interact_sound_cooldown <= 0 then + self.sounds["purr"].gain = 0.15 * self.trust[self.owner] + self.interact_sound_cooldown = 3 + mobkit.make_sound(self, "purr") + end + end + if trust <= 7 then + if self.interact_sound_cooldown <= 0 then + self.interact_sound_cooldown = 3 + mobkit.make_sound(self, "random") + end + return + end + if self.order == "wander" then + self.order = "follow" + elseif self.order == "follow" then + self.order = "sit" + else + self.order = "wander" + end + mobkit.remember(self, "order", self.order) + end, + on_punch = function(self, puncher, _, tool_capabilities, dir) + mob_core.on_punch_basic(self, puncher, tool_capabilities, dir) + animalia.hq_sporadic_flee(self, 10) + if not self.trust[puncher:get_player_name()] then + self.trust[puncher:get_player_name()] = 0 + else + local trust = self.trust[puncher:get_player_name()] + self.trust[puncher:get_player_name()] = trust - 1 + end + local pos = self.object:get_pos() + local prt_pos = vector.new(pos.x, pos.y + 0.5, pos.z) + local minppos = vector.add(prt_pos, 1) + local maxppos = vector.subtract(prt_pos, 1) + animalia.particle_spawner(prt_pos, "mob_core_red_particle.png", "float", minppos, maxppos) + mobkit.remember(self, "trust", self.trust) + end +}) + +mob_core.register_spawn_egg("animalia:cat", "db9764" ,"cf8d5a") + +local house_nodes = {} + +minetest.register_on_mods_loaded(function() + for name in pairs(minetest.registered_nodes) do + if minetest.get_item_group(name, "stairs") > 0 + or minetest.get_item_group(name, "wood") > 0 then + table.insert(house_nodes, name) + end + end +end) + +mob_core.register_spawn({ + name = "animalia:cat", + nodes = house_nodes, + min_light = 0, + max_light = 15, + min_height = -31000, + max_height = 31000, + min_rad = 24, + max_rad = 256, + group = 0, +}, animalia.spawn_interval, 1) \ No newline at end of file diff --git a/mobs/chicken.lua b/mobs/chicken.lua index 900dc47..2ed0ee4 100644 --- a/mobs/chicken.lua +++ b/mobs/chicken.lua @@ -2,134 +2,167 @@ -- Chicken -- ------------- -local blend = better_fauna.frame_blend +local clamp_bone_rot = animalia.clamp_bone_rot + +local interp = animalia.interp local random = math.random local function chicken_logic(self) - if self.hp <= 0 then mob_core.on_die(self) return end - local prty = mobkit.get_queue_priority(self) - local player = mobkit.get_nearby_player(self) - if mobkit.timer(self,1) then + if self.status ~= "following" then + if self.attention_span > 1 then + self.attention_span = self.attention_span - self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end + else + self.attention_span = self.attention_span + self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end - mob_core.vitals(self) - mob_core.random_drop(self, 10, 1800, "better_fauna:chicken_egg") - mob_core.random_sound(self, 8) + if self.fall_start + and self.fall_start - mobkit.get_stand_pos(self).y > 2 then + mobkit.animate(self, "flap") + self.object:set_acceleration({x = 0, y = -3.1, z = 0}) + end + + animalia.head_tracking(self, 0.45, 0.25) + + if mobkit.timer(self, 4) then + + local prty = mobkit.get_queue_priority(self) + local player = mobkit.get_nearby_player(self) + + mob_core.random_sound(self, 14) + mob_core.random_drop(self, 10, 1800, "animalia:chicken_egg") + + if prty < 4 + and self.isinliquid then + animalia.hq_go_to_land(self, 4) + end if prty < 3 and self.breeding then - better_fauna.hq_fowl_breed(self, 3) - end - - if prty < 2 - and player then - if self.attention_span < 5 then - if mob_core.follow_holding(self, player) then - better_fauna.hq_follow_player(self, 2, player) - self.attention_span = self.attention_span + 1 - end - end + animalia.hq_fowl_breed(self, 3) end + if prty == 2 + and not self.lasso_player + and (not player + or not mob_core.follow_holding(self, player)) then + mobkit.clear_queue_high(self) + end + + if prty < 2 then + if self.caught_with_lasso + and self.lasso_player then + animalia.hq_follow_player(self, 2, self.lasso_player, true) + elseif player then + if self.attention_span < 5 then + if mob_core.follow_holding(self, player) then + animalia.hq_follow_player(self, 2, player) + self.attention_span = self.attention_span + 1 + end + end + end + end + if mobkit.is_queue_empty_high(self) then - mob_core.hq_roam(self, 0) + animalia.hq_wander_group(self, 0, 8) end end end -minetest.register_entity("better_fauna:chicken",{ - max_hp = 10, - view_range = 16, - armor_groups = {fleshy = 100}, - physical = true, - collide_with_objects = true, +animalia.register_mob("chicken", { + -- Stats + health = 10, + fleshy = 100, + view_range = 8, + lung_capacity = 10, + -- Visual collisionbox = {-0.2, -0.15, -0.2, 0.2, 0.3, 0.2}, visual_size = {x = 6, y = 6}, - scale_stage1 = 0.25, - scale_stage2 = 0.5, - scale_stage3 = 0.75, - visual = "mesh", - mesh = "better_fauna_chicken.b3d", + mesh = "animalia_chicken.b3d", female_textures = { - "better_fauna_chicken_1.png", - "better_fauna_chicken_2.png", - "better_fauna_chicken_3.png" + "animalia_chicken_1.png", + "animalia_chicken_2.png", + "animalia_chicken_3.png" }, male_textures = { - "better_fauna_rooster_1.png", - "better_fauna_rooster_2.png", - "better_fauna_rooster_3.png" + "animalia_rooster_1.png", + "animalia_rooster_2.png", + "animalia_rooster_3.png" }, - child_textures = {"better_fauna_chick.png"}, - animation = { - stand = {range = {x = 0, y = 0}, speed = 1, frame_blend = blend, loop = true}, - walk = {range = {x = 10, y = 30}, speed = 30, frame_blend = blend, loop = true}, - run = {range = {x = 10, y = 30}, speed = 45, frame_blend = blend, loop = true}, - fall = {range = {x = 40, y = 60}, speed = 30, frame_blend = blend, loop = true}, + child_textures = {"animalia_chick.png"}, + animations = { + stand = {range = {x = 0, y = 0}, speed = 1, frame_blend = 0.3, loop = true}, + walk = {range = {x = 10, y = 30}, speed = 30, frame_blend = 0.3, loop = true}, + run = {range = {x = 10, y = 30}, speed = 45, frame_blend = 0.3, loop = true}, + fall = {range = {x = 40, y = 60}, speed = 30, frame_blend = 0.3, loop = true}, }, + -- Physics + speed = 5, + max_fall = 6, + -- Attributes sounds = { alter_child_pitch = true, random = { - name = "better_fauna_chicken_idle", - gain = 1.0, + name = "animalia_chicken_idle", + gain = 0.5, distance = 8 }, hurt = { - name = "better_fauna_chicken_hurt", - gain = 1.0, + name = "animalia_chicken_hurt", + gain = 0.5, distance = 8 }, death = { - name = "better_fauna_chicken_death", - gain = 1.0, + name = "animalia_chicken_death", + gain = 0.5, distance = 8 } }, - max_speed = 4, - stepheight = 1.1, - jump_height = 1.1, - buoyancy = 0.25, - lung_capacity = 10, - timeout = 1200, - ignore_liquidflag = false, - core_growth = false, - push_on_collide = true, - catch_with_net = true, + fall_damage = false, + -- Behavior + defend_owner = false, follow = { "farming:seed_cotton", "farming:seed_wheat" }, drops = { - {name = "better_fauna:feather", chance = 1, min = 1, max = 2}, - {name = "better_fauna:chicken_raw", chance = 1, min = 1, max = 4} + {name = "animalia:feather", chance = 1, min = 1, max = 2}, + {name = "animalia:chicken_raw", chance = 1, min = 1, max = 4} + }, + -- Functions + head_data = { + offset = {x = 0, y = 0.15, z = 0}, + pitch_correction = 55, + pivot_h = 0.25, + pivot_v = 0.55 }, - on_step = better_fauna.on_step, - on_activate = better_fauna.on_activate, - get_staticdata = mobkit.statfunc, - phsyics = better_fauna.lightweight_physics, logic = chicken_logic, + get_staticdata = mobkit.statfunc, + on_step = animalia.on_step, + on_activate = animalia.on_activate, on_rightclick = function(self, clicker) - if better_fauna.feed_tame(self, clicker, 1, false, true) then return end - mob_core.protect(self, clicker, false) + if animalia.feed_tame(self, clicker, 1, false, true) then return end + mob_core.protect(self, clicker, true) mob_core.nametag(self, clicker, true) end, on_punch = function(self, puncher, _, tool_capabilities, dir) - mobkit.clear_queue_high(self) mob_core.on_punch_basic(self, puncher, tool_capabilities, dir) - better_fauna.hq_sporadic_flee(self, 10, puncher) + animalia.hq_sporadic_flee(self, 10) end, }) -mob_core.register_spawn_egg("better_fauna:chicken", "c6c6c6", "d22222") +mob_core.register_spawn_egg("animalia:chicken", "c6c6c6", "d22222") mob_core.register_spawn({ - name = "better_fauna:chicken", - nodes = {"default:dry_dirt_with_dry_grass", "default:dirt_with_grass"}, + name = "animalia:chicken", min_light = 0, max_light = 15, min_height = -31000, @@ -139,41 +172,39 @@ mob_core.register_spawn({ group = 6, optional = { biomes = { - "grassland", - "savanna", - "rainforest" + unpack(animalia.grassland_biomes), + unpack(animalia.tropical_biomes) } } -}, 16, 6) +}, animalia.spawn_interval, 4) - -minetest.register_craftitem("better_fauna:chicken_raw", { - description = "Raw Chicken", - inventory_image = "better_fauna_chicken_raw.png", +minetest.register_craftitem("animalia:poultry_raw", { + description = "Raw Poultry", + inventory_image = "animalia_poultry_raw.png", on_use = minetest.item_eat(1), groups = {flammable = 2}, }) -minetest.register_craftitem("better_fauna:chicken_cooked", { - description = "Cooked Chicken", - inventory_image = "better_fauna_chicken_cooked.png", +minetest.register_craftitem("animalia:poultry_cooked", { + description = "Cooked Poultry", + inventory_image = "animalia_poultry_cooked.png", on_use = minetest.item_eat(6), groups = {flammable = 2}, }) minetest.register_craft({ type = "cooking", - recipe = "better_fauna:chicken_raw", - output = "better_fauna:chicken_cooked", + recipe = "animalia:poultry_raw", + output = "animalia:poultry_cooked", }) -minetest.register_entity("better_fauna:chicken_egg_sprite", { +minetest.register_entity("animalia:chicken_egg_sprite", { hp_max = 1, physical = true, collisionbox = {0, 0, 0, 0, 0, 0}, visual = "sprite", visual_size = {x = 0.5, y = 0.5}, - textures = {"better_fauna_egg.png"}, + textures = {"animalia_egg.png"}, initial_sprite_basepos = {x = 0, y = 0}, is_visible = true, on_step = function(self, dtime) @@ -182,7 +213,7 @@ minetest.register_entity("better_fauna:chicken_egg_sprite", { local cube = minetest.find_nodes_in_area( vector.new(pos.x - 0.5, pos.y - 0.5, pos.z - 0.5), vector.new(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5), - better_fauna.walkable_nodes) + animalia.walkable_nodes) if #objects >= 2 then if objects[2]:get_armor_groups().fleshy then objects[2]:punch(self.object, 2.0, {full_punch_interval = 0.1, damage_groups = {fleshy = 1}}, nil) @@ -199,10 +230,10 @@ minetest.register_entity("better_fauna:chicken_egg_sprite", { minacc = vector.new(0, -9.81, 0), maxacc = vector.new(0, -9.81, 0), collisiondetection = true, - texture = "better_fauna_egg_fragment.png", + texture = "animalia_egg_fragment.png", }) - if random(1, 3) == 1 then - mob_core.spawn_child(pos, "better_fauna:chicken") + if random(1, 3) < 2 then + mob_core.spawn_child(pos, "animalia:chicken") self.object:remove() else self.object:remove() @@ -227,7 +258,7 @@ local mobs_shoot_egg = function (item, player, pointed_thing) x = pos.x, y = pos.y +1.5, z = pos.z - }, "better_fauna:chicken_egg_sprite") + }, "animalia:chicken_egg_sprite") local ent = obj:get_luaentity() local dir = player:get_look_dir() @@ -256,9 +287,28 @@ local mobs_shoot_egg = function (item, player, pointed_thing) return item end -minetest.register_craftitem("better_fauna:chicken_egg", { +minetest.register_craftitem("animalia:chicken_egg", { description = "Chicken Egg", - inventory_image = "better_fauna_egg.png", + inventory_image = "animalia_egg.png", on_use = mobs_shoot_egg, groups = {flammable = 2}, +}) + +minetest.register_craftitem("animalia:chicken_egg_fried", { + description = "Fried Chicken Egg", + inventory_image = "animalia_egg_fried.png", + on_use = minetest.item_eat(4), + groups = {flammable = 2}, +}) + +minetest.register_craft({ + type = "cooking", + recipe = "animalia:chicken_egg", + output = "animalia:chicken_egg_fried", +}) + +minetest.register_craftitem("animalia:feather", { + description = "Feather", + inventory_image = "animalia_feather.png", + groups = {flammable = 2, feather = 1}, }) \ No newline at end of file diff --git a/mobs/cow.lua b/mobs/cow.lua index 4cf6ed0..1d01b86 100644 --- a/mobs/cow.lua +++ b/mobs/cow.lua @@ -2,7 +2,12 @@ -- Cow -- --------- -local blend = better_fauna.frame_blend +local clamp_bone_rot = animalia.clamp_bone_rot + +local interp = animalia.interp + +local random = math.random +local blend = animalia.frame_blend local function cow_logic(self) @@ -11,126 +16,161 @@ local function cow_logic(self) return end - local pos = mobkit.get_stand_pos(self) - local prty = mobkit.get_queue_priority(self) - local player = mobkit.get_nearby_player(self) + if self.status ~= "following" then + if self.attention_span > 1 then + self.attention_span = self.attention_span - self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end + else + self.attention_span = self.attention_span + self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end - mob_core.random_sound(self, 16/self.dtime) + animalia.head_tracking(self, 0.75, 0.75) - if mobkit.timer(self,1) then + if mobkit.timer(self, 3) then - mob_core.vitals(self) + local prty = mobkit.get_queue_priority(self) + local player = mobkit.get_nearby_player(self) + + mob_core.random_sound(self, 14) mob_core.growth(self) - if math.random(1, 64) == 1 then + if random(1, 64) < 2 then self.gotten = mobkit.remember(self, "gotten", false) end - if self.status ~= "following" then - if self.attention_span > 1 then - self.attention_span = self.attention_span - 1 - mobkit.remember(self, "attention_span", self.attention_span) - end - else - self.attention_span = self.attention_span + 1 - mobkit.remember(self, "attention_span", self.attention_span) + if prty < 5 + and self.isinliquid then + animalia.hq_go_to_land(self, 5) end if prty < 4 and self.breeding then - better_fauna.hq_breed(self, 4) + animalia.hq_breed(self, 4) end if prty < 3 and self.gotten - and math.random(1, 16) == 1 then - better_fauna.hq_eat(self, 3) + and random(1, 16) < 2 then + animalia.hq_eat(self, 3) end - - if prty < 2 - and player then - if self.attention_span < 5 then - if mob_core.follow_holding(self, player) then - better_fauna.hq_follow_player(self, 2, player) - self.attention_span = self.attention_span + 1 - end - end + + if prty == 2 + and not self.lasso_player + and (not player + or not mob_core.follow_holding(self, player)) then + mobkit.clear_queue_high(self) + end + + if prty < 2 then + if self.caught_with_lasso + and self.lasso_player then + animalia.hq_follow_player(self, 2, self.lasso_player, true) + elseif player then + if self.attention_span < 5 then + if mob_core.follow_holding(self, player) then + animalia.hq_follow_player(self, 2, player) + self.attention_span = self.attention_span + 1 + end + end + end end if mobkit.is_queue_empty_high(self) then - mob_core.hq_roam(self, 0) + animalia.hq_wander_group(self, 0, 10) end end end -local random = math.random - -minetest.register_entity("better_fauna:cow",{ - max_hp = 20, - view_range = 16, - armor_groups = {fleshy = 100}, - physical = true, - collide_with_objects = true, - collisionbox = {-0.45, -0.55, -0.45, 0.45, 0.4, 0.45}, - visual_size = {x = 13, y = 13}, - scale_stage1 = 0.5, - scale_stage2 = 0.65, - scale_stage3 = 0.80, - visual = "mesh", - mesh = "better_fauna_cow.b3d", - textures = { - "better_fauna_cow_1.png", - "better_fauna_cow_2.png", - "better_fauna_cow_3.png", - "better_fauna_cow_4.png" +animalia.register_mob("cow", { + -- Stats + health = 20, + fleshy = 100, + view_range = 32, + lung_capacity = 10, + -- Visual + collisionbox = {-0.45, 0, -0.45, 0.45, 0.9, 0.45}, + visual_size = {x = 10, y = 10}, + mesh = "animalia_cow.b3d", + female_textures = { + "animalia_cow_1.png^animalia_cow_udder.png", + "animalia_cow_2.png^animalia_cow_udder.png", + "animalia_cow_3.png^animalia_cow_udder.png", + "animalia_cow_4.png^animalia_cow_udder.png" }, - animation = { - stand = {range = {x = 30, y = 50}, speed = 10, frame_blend = blend, loop = true}, - walk = {range = {x = 1, y = 20}, speed = 20, frame_blend = blend, loop = true}, - run = {range = {x = 1, y = 20}, speed = 30, frame_blend = blend, loop = true}, + male_textures = { + "animalia_cow_1.png", + "animalia_cow_2.png", + "animalia_cow_3.png", + "animalia_cow_4.png" }, + child_textures = { + "animalia_cow_1.png", + "animalia_cow_2.png", + "animalia_cow_3.png", + "animalia_cow_4.png" + }, + animations = { + stand = {range = {x = 1, y = 60}, speed = 10, frame_blend = 0.3, loop = true}, + walk = {range = {x = 70, y = 110}, speed = 40, frame_blend = 0.3, loop = true}, + run = {range = {x = 70, y = 110}, speed = 50, frame_blend = 0.3, loop = true}, + }, + -- Physics + speed = 4, + max_fall = 3, + -- Attributes sounds = { alter_child_pitch = true, random = { - name = "better_fauna_cow_idle", + name = "animalia_cow_idle", gain = 1.0, distance = 8 }, hurt = { - name = "better_fauna_cow_hurt", + name = "animalia_cow_hurt", gain = 1.0, distance = 8 }, death = { - name = "better_fauna_cow_death", + name = "animalia_cow_death", gain = 1.0, distance = 8 } }, - max_speed = 4, - stepheight = 1.1, - jump_height = 1.1, - buoyancy = 0.25, - lung_capacity = 10, - timeout = 1200, - ignore_liquidflag = false, - core_growth = false, - push_on_collide = true, - catch_with_net = true, + -- Behavior + defend_owner = false, follow = { "farming:wheat", }, - drops = { - {name = "mobs:leather", chance = 1, min = 1, max = 2}, - {name = "better_fauna:beef_raw", chance = 1, min = 1, max = 4} + consumable_nodes = { + { + name = "default:dirt_with_grass", + replacement = "default:dirt" + }, + { + name = "default:dry_dirt_with_dry_grass", + replacement = "default:dry_dirt" + } }, - on_step = better_fauna.on_step, - on_activate = better_fauna.on_activate, - get_staticdata = mobkit.statfunc, - logic = cow_logic, + drops = { + {name = "animalia:leather", chance = 2, min = 1, max = 2}, + {name = "animalia:beef_raw", chance = 1, min = 1, max = 4} + }, + -- Functions + head_data = { + offset = {x = 0, y = 0.5, z = 0}, + pitch_correction = -45, + pivot_h = 0.75, + pivot_v = 1 + }, + logic = cow_logic, + get_staticdata = mobkit.statfunc, + on_step = animalia.on_step, + on_activate = animalia.on_activate, on_rightclick = function(self, clicker) - if better_fauna.feed_tame(self, clicker, 1, false, true) then return end - mob_core.protect(self, clicker, false) + if animalia.feed_tame(self, clicker, 1, false, true) then return end + mob_core.protect(self, clicker, true) mob_core.nametag(self, clicker, true) local tool = clicker:get_wielded_item() @@ -152,12 +192,12 @@ minetest.register_entity("better_fauna:cow",{ tool:take_item() clicker:set_wielded_item(tool) - if inv:room_for_item("main", {name = "better_fauna:bucket_milk"}) then - clicker:get_inventory():add_item("main", "better_fauna:bucket_milk") + if inv:room_for_item("main", {name = "animalia:bucket_milk"}) then + clicker:get_inventory():add_item("main", "animalia:bucket_milk") else local pos = self.object:get_pos() pos.y = pos.y + 0.5 - minetest.add_item(pos, {name = "better_fauna:bucket_milk"}) + minetest.add_item(pos, {name = "animalia:bucket_milk"}) end self.gotten = mobkit.remember(self, "gotten", true) @@ -165,58 +205,55 @@ minetest.register_entity("better_fauna:cow",{ end end, on_punch = function(self, puncher, _, tool_capabilities, dir) - mobkit.clear_queue_high(self) mob_core.on_punch_basic(self, puncher, tool_capabilities, dir) - better_fauna.hq_sporadic_flee(self, 20, puncher) + animalia.hq_sporadic_flee(self, 10) end }) -minetest.register_craftitem("better_fauna:bucket_milk", { +minetest.register_craftitem("animalia:leather", { + description = "Leather", + inventory_image = "animalia_leather.png" +}) + +minetest.register_craftitem("animalia:bucket_milk", { description = "Bucket of Milk", - inventory_image = "better_fauna_milk_bucket.png", + inventory_image = "animalia_milk_bucket.png", stack_max = 1, on_use = minetest.item_eat(8, "bucket:bucket_empty"), groups = {food_milk = 1, flammable = 3}, }) -minetest.register_craftitem("better_fauna:beef_raw", { +minetest.register_craftitem("animalia:beef_raw", { description = "Raw Beef", - inventory_image = "better_fauna_beef_raw.png", + inventory_image = "animalia_beef_raw.png", on_use = minetest.item_eat(1), groups = {flammable = 2}, }) -minetest.register_craftitem("better_fauna:beef_cooked", { +minetest.register_craftitem("animalia:beef_cooked", { description = "Steak", - inventory_image = "better_fauna_beef_cooked.png", + inventory_image = "animalia_beef_cooked.png", on_use = minetest.item_eat(8), groups = {flammable = 2}, }) minetest.register_craft({ type = "cooking", - recipe = "better_fauna:beef_raw", - output = "better_fauna:beef_cooked", + recipe = "animalia:beef_raw", + output = "animalia:beef_cooked", }) -mob_core.register_spawn_egg("better_fauna:cow", "cac3a1" ,"464438") +mob_core.register_spawn_egg("animalia:cow", "cac3a1" ,"464438") mob_core.register_spawn({ - name = "better_fauna:cow", - nodes = { - "default:dirt_with_grass", - "default:dry_dirt_with_dry_grass" - }, + name = "animalia:cow", min_light = 0, max_light = 15, min_height = -31000, max_height = 31000, group = 3, optional = { - biomes = { - "grassland", - "savanna" - } + biomes = animalia.grassland_biomes } -}, 4, 6) \ No newline at end of file +}, animalia.spawn_interval, 2) \ No newline at end of file diff --git a/mobs/horse.lua b/mobs/horse.lua new file mode 100644 index 0000000..72253e9 --- /dev/null +++ b/mobs/horse.lua @@ -0,0 +1,295 @@ +----------- +-- Horse -- +----------- + +local random = math.random + +local function set_pattern(self) + local types = { + "spots", + "patches" + } + if mobkit.recall(self, "pattern") + and not mobkit.recall(self, "pattern"):find("better_fauna") then + local pattern = mobkit.recall(self, "pattern") + local texture = self.object:get_properties().textures[1] + self.object:set_properties({ + textures = {texture .. "^" .. pattern} + }) + else + local type = types[random(#types)] + local overlay = "(animalia_horse_".. type ..".png)" + if type == "patches" then + local colors = { + "brown", + "white" + } + if self.texture_no < 1 then + table.insert(colors, "black") + else + table.remove(colors, 1) + end + overlay = "(animalia_horse_".. colors[random(#colors)] .."_patches.png)" + end + if random(100) > 50 then + overlay = "transparency.png" + end + local texture = self.object:get_properties().textures[1] + self.object:set_properties({ + textures = {texture .. "^" .. overlay} + }) + mobkit.remember(self, "pattern", overlay) + end +end + +local function horse_logic(self) + + if self.hp <= 0 then + mob_core.on_die(self) + return + end + + if self.status ~= "following" then + if self.attention_span > 1 then + self.attention_span = self.attention_span - self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end + else + self.attention_span = self.attention_span + self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end + + animalia.head_tracking(self) + + if mobkit.timer(self, 1) then + + local prty = mobkit.get_queue_priority(self) + local player = mobkit.get_nearby_player(self) + local pos = self.object:get_pos() + + mob_core.random_sound(self, 14) + mob_core.growth(self) + + if self.breaking then + if not minetest.get_player_by_name(self.breaker) + or not self.driver then + self.breaking = nil + self.breaker = nil + else + local yaw = self.object:get_yaw() + local yaw2 = minetest.get_player_by_name(self.breaker):get_look_horizontal() + if math.abs(yaw - yaw2) > 5.8 + or math.abs(yaw - yaw2) < 0.5 then + self.breaking_progress = self.breaking_progress + 1 + else + self.breaking_progress = self.breaking_progress - 1 + end + animalia.hq_sporadic_flee(self, 10) + if self.breaking_progress < -5 + or minetest.get_player_by_name(self.breaker):get_player_control().sneak then + mob_core.detach(self.driver, {x = 1, y = 0, z = 1}) + mobkit.lq_idle(self, 0.5, "rear") + self.breaking = nil + self.breaker = nil + self.breaking_progress = nil + elseif self.breaking_progress > 5 then + mob_core.set_owner(self, self.breaker) + self.breaking = nil + self.breaker = nil + self.breaking_progress = nil + local prt_pos = vector.new(pos.x, pos.y + 2, pos.z) + local minppos = vector.add(prt_pos, 1) + local maxppos = vector.subtract(prt_pos, 1) + animalia.particle_spawner(prt_pos, "mob_core_green_particle.png", "float", minppos, maxppos) + mobkit.clear_queue_high(self) + end + end + return + end + + if prty < 20 + and self.driver + and not self.breaking then + animalia.hq_mount_logic(self, 20) + end + + if prty < 5 + and self.isinliquid then + animalia.hq_go_to_land(self, 5) + end + + if prty < 4 + and self.breeding then + animalia.hq_horse_breed(self, 4) + end + + if prty == 2 + and not self.lasso_player + and (not player + or not mob_core.follow_holding(self, player)) then + mobkit.clear_queue_high(self) + end + + if prty < 2 then + if self.caught_with_lasso + and self.lasso_player then + animalia.hq_follow_player(self, 2, self.lasso_player, true) + elseif player then + if self.attention_span < 5 then + if mob_core.follow_holding(self, player) then + animalia.hq_follow_player(self, 2, player) + self.attention_span = self.attention_span + 1 + end + end + end + end + + if mobkit.is_queue_empty_high(self) then + animalia.hq_wander_group(self, 0, 12) + end + end +end + +animalia.register_mob("horse", { + -- Stats + health = 40, + fleshy = 100, + view_range = 32, + lung_capacity = 10, + -- Visual + collisionbox = {-0.65, 0, -0.65, 0.65, 1.95, 0.65}, + visual_size = {x = 10, y = 10}, + mesh = "animalia_horse.b3d", + textures = { + "animalia_horse_1.png", + "animalia_horse_2.png", + "animalia_horse_3.png", + "animalia_horse_4.png", + "animalia_horse_5.png", + "animalia_horse_6.png" + }, + animations = { + stand = {range = {x = 1, y = 60}, speed = 10, frame_blend = 0.3, loop = true}, + walk = {range = {x = 70, y = 110}, speed = 25, frame_blend = 0.3, loop = true}, + run = {range = {x = 70, y = 110}, speed = 45, frame_blend = 0.3, loop = true}, + rear = {range = {x = 120, y = 150}, speed = 27, frame_blend = 0.2, loop = false}, + rear_constant = {range = {x = 130, y = 140}, speed = 20, frame_blend = 0.3, loop = true} + }, + -- Physics + speed = 10, + max_fall = 8, + -- Attributes + sounds = { + alter_child_pitch = true, + random = { + { + name = "animalia_horse_idle_1", + gain = 1.0, + distance = 8 + }, + { + name = "animalia_horse_idle_2", + gain = 1.0, + distance = 8 + }, + { + name = "animalia_horse_idle_3", + gain = 1.0, + distance = 8 + } + }, + hurt = { + name = "animalia_horse_hurt", + gain = 1.0, + distance = 8 + }, + death = { + name = "animalia_horse_death", + gain = 1.0, + distance = 8 + } + }, + -- Behavior + defend_owner = false, + follow = { + "farming:wheat", + }, + drops = { + {name = "animalia:leather", chance = 2, min = 1, max = 4}, + }, + player_rotation = {x = -60, y = 180, z = 0}, + driver_scale = {x = 0.1, y = 0.1}, + driver_attach_at = {x = 0, y = 1.1, z = 0.5}, + driver_attach_bone = "Torso", + driver_eye_offset = {{x = 0, y = 15, z = 0}, {x = 0, y = 15, z = 15}}, + -- Functions + head_data = { + bone = "Neck.CTRL", + offset = {x = 0, y = 1.98, z = 0}, + pitch_correction = 35, + pivot_h = 1, + pivot_v = 1.5 + }, + logic = horse_logic, + get_staticdata = mobkit.statfunc, + on_step = animalia.on_step, + on_activate = function(self, staticdata, dtime_s) + animalia.on_activate(self, staticdata, dtime_s) + set_pattern(self) + self.saddled = mobkit.recall(self, "saddled") or false + self.max_hp = mobkit.recall(self, "max_hp") or random(30, 45) + self.speed = mobkit.recall(self, "speed") or random(5, 10) + self.jump_power = mobkit.recall(self, "speed") or random(2, 5) + if self.saddled then + local texture = self.object:get_properties().textures[1] + self.object:set_properties({ + textures = {texture .. "^animalia_horse_saddle.png"} + }) + end + end, + on_rightclick = function(self, clicker) + if animalia.feed_tame(self, clicker, 1, false, true) then return end + mob_core.protect(self, clicker, false) + mob_core.nametag(self, clicker, true) + local tool = clicker:get_wielded_item() + if self.tamed + and self.owner == clicker:get_player_name() then + if self.saddled + and tool:get_name() == "" then + mob_core.mount(self, clicker) + elseif tool:get_name() == "animalia:saddle" then + self.saddled = mobkit.remember(self, "saddled", true) + local texture = self.object:get_properties().textures[1] + self.object:set_properties({ + textures = {texture .. "^animalia_horse_saddle.png"} + }) + tool:take_item() + clicker:set_wielded_item(tool) + end + elseif not self.tamed + and tool:get_name() == "" then + mob_core.mount(self, clicker) + self.breaking = true + self.breaker = clicker:get_player_name() + self.breaking_progress = 0 + end + end, + on_punch = function(self, puncher, _, tool_capabilities, dir) + mob_core.on_punch_basic(self, puncher, tool_capabilities, dir) + animalia.hq_sporadic_flee(self, 10) + end +}) + +mob_core.register_spawn_egg("animalia:horse", "ebdfd8" ,"653818") + +mob_core.register_spawn({ + name = "animalia:horse", + min_light = 0, + max_light = 15, + min_height = -31000, + max_height = 31000, + group = 6, + optional = { + biomes = animalia.grassland_biomes + } +}, animalia.spawn_interval, 8) \ No newline at end of file diff --git a/mobs/pig.lua b/mobs/pig.lua index e4c20e0..4df077f 100644 --- a/mobs/pig.lua +++ b/mobs/pig.lua @@ -2,8 +2,6 @@ -- Pig -- --------- -local blend = better_fauna.frame_blend - local function pig_logic(self) if self.hp <= 0 then @@ -11,148 +9,162 @@ local function pig_logic(self) return end - local pos = mobkit.get_stand_pos(self) - local prty = mobkit.get_queue_priority(self) - local player = mobkit.get_nearby_player(self) + if self.status ~= "following" then + if self.attention_span > 1 then + self.attention_span = self.attention_span - self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end + else + self.attention_span = self.attention_span + self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end - mob_core.random_sound(self, 16/self.dtime) + if mobkit.timer(self, 3) then - if mobkit.timer(self,1) then + local prty = mobkit.get_queue_priority(self) + local player = mobkit.get_nearby_player(self) - mob_core.vitals(self) + mob_core.random_sound(self, 14) mob_core.growth(self) - if self.status ~= "following" then - if self.attention_span > 1 then - self.attention_span = self.attention_span - 1 - mobkit.remember(self, "attention_span", self.attention_span) - end - else - self.attention_span = self.attention_span + 1 - mobkit.remember(self, "attention_span", self.attention_span) + if prty < 4 + and self.isinliquid then + animalia.hq_go_to_land(self, 4) end if prty < 3 and self.breeding then - better_fauna.hq_breed(self, 3) + animalia.hq_breed(self, 3) end - if prty < 2 - and player then - if self.attention_span < 5 then - if mob_core.follow_holding(self, player) then - better_fauna.hq_follow_player(self, 2, player) - self.attention_span = self.attention_span + 1 - end - end + if prty == 2 + and not self.lasso_player + and (not player + or not mob_core.follow_holding(self, player)) then + mobkit.clear_queue_high(self) + end + + if prty < 2 then + if self.caught_with_lasso + and self.lasso_player then + animalia.hq_follow_player(self, 2, self.lasso_player, true) + elseif player then + if self.attention_span < 5 then + if mob_core.follow_holding(self, player) then + animalia.hq_follow_player(self, 2, player) + self.attention_span = self.attention_span + 3 + end + end + end end if mobkit.is_queue_empty_high(self) then - mob_core.hq_roam(self, 0) + animalia.hq_wander_group(self, 0, 8) end end end -local random = math.random - -minetest.register_entity("better_fauna:pig",{ - max_hp = 20, - view_range = 16, - armor_groups = {fleshy = 100}, - physical = true, - collide_with_objects = true, +animalia.register_mob("pig", { + -- Stats + health = 20, + fleshy = 100, + view_range = 32, + lung_capacity = 10, + -- Visual collisionbox = {-0.35, -0.45, -0.35, 0.35, 0.4, 0.35}, visual_size = {x = 11, y = 11}, - scale_stage1 = 0.5, - scale_stage2 = 0.65, - scale_stage3 = 0.80, - visual = "mesh", - mesh = "better_fauna_pig.b3d", - textures = { - "better_fauna_pig_1.png", - "better_fauna_pig_2.png", - "better_fauna_pig_3.png" + mesh = "animalia_pig.b3d", + female_textures = { + "animalia_pig_1.png", + "animalia_pig_2.png", + "animalia_pig_3.png" }, - animation = { - stand = {range = {x = 30, y = 50}, speed = 10, frame_blend = blend, loop = true}, - walk = {range = {x = 1, y = 20}, speed = 30, frame_blend = blend, loop = true}, - run = {range = {x = 1, y = 20}, speed = 45, frame_blend = blend, loop = true}, + male_textures = { + "animalia_pig_1.png^animalia_pig_tusks.png", + "animalia_pig_2.png^animalia_pig_tusks.png", + "animalia_pig_3.png^animalia_pig_tusks.png" }, + child_textures = { + "animalia_pig_1.png", + "animalia_pig_2.png", + "animalia_pig_3.png" + }, + animations = { + stand = {range = {x = 30, y = 50}, speed = 10, frame_blend = 0.3, loop = true}, + walk = {range = {x = 1, y = 20}, speed = 30, frame_blend = 0.3, loop = true}, + run = {range = {x = 1, y = 20}, speed = 45, frame_blend = 0.3, loop = true}, + }, + -- Physics + speed = 4, + max_fall = 3, + -- Attributes sounds = { alter_child_pitch = true, random = { - name = "better_fauna_pig_idle", + name = "animalia_pig_idle", gain = 1.0, distance = 8 }, hurt = { - name = "better_fauna_pig_idle", + name = "animalia_pig_idle", gain = 1.0, pitch = 0.5, distance = 8 }, death = { - name = "better_fauna_pig_death", + name = "animalia_pig_death", gain = 1.0, distance = 8 } }, - max_speed = 4, - stepheight = 1.1, - jump_height = 1.1, - buoyancy = 0.25, - lung_capacity = 10, - timeout = 1200, - ignore_liquidflag = false, - core_growth = false, - push_on_collide = true, - catch_with_net = true, + -- Behavior + defend_owner = false, follow = { "farming:carrot" }, drops = { - {name = "better_fauna:porkchop_raw", chance = 1, min = 1, max = 4} + {name = "animalia:porkchop_raw", chance = 1, min = 1, max = 4} }, - on_step = better_fauna.on_step, - on_activate = better_fauna.on_activate, - get_staticdata = mobkit.statfunc, - logic = pig_logic, + -- Functions + logic = pig_logic, + get_staticdata = mobkit.statfunc, + on_step = animalia.on_step, + on_activate = animalia.on_activate, on_rightclick = function(self, clicker) - if better_fauna.feed_tame(self, clicker, 1, false, true) then return end - mob_core.protect(self, clicker, false) + if animalia.feed_tame(self, clicker, 1, false, true) then return end + mob_core.protect(self, clicker, true) mob_core.nametag(self, clicker, true) end, on_punch = function(self, puncher, _, tool_capabilities, dir) - mobkit.clear_queue_high(self) mob_core.on_punch_basic(self, puncher, tool_capabilities, dir) - better_fauna.hq_sporadic_flee(self, 20, puncher) + animalia.hq_sporadic_flee(self, 10) end }) -minetest.register_craftitem("better_fauna:porkchop_raw", { +minetest.register_craftitem("animalia:porkchop_raw", { description = "Raw Porkchop", - inventory_image = "better_fauna_porkchop_raw.png", + inventory_image = "animalia_porkchop_raw.png", on_use = minetest.item_eat(1), groups = {flammable = 2}, }) -minetest.register_craftitem("better_fauna:porkchop_cooked", { +minetest.register_craftitem("animalia:porkchop_cooked", { description = "Cooked Porkchop", - inventory_image = "better_fauna_porkchop_cooked.png", + inventory_image = "animalia_porkchop_cooked.png", on_use = minetest.item_eat(7), groups = {flammable = 2}, }) minetest.register_craft({ type = "cooking", - recipe = "better_fauna:porkchop_raw", - output = "better_fauna:porkchop_cooked", + recipe = "animalia:porkchop_raw", + output = "animalia:porkchop_cooked", }) -mob_core.register_spawn_egg("better_fauna:pig", "e0b1a7" ,"cc9485") +mob_core.register_spawn_egg("animalia:pig", "e0b1a7" ,"cc9485") mob_core.register_spawn({ - name = "better_fauna:pig", + name = "animalia:pig", nodes = {"default:dirt_with_grass"}, min_light = 0, max_light = 15, @@ -161,8 +173,9 @@ mob_core.register_spawn({ group = 3, optional = { biomes = { - "grassland", - "deciduous_forest" + unpack(animalia.grassland_biomes), + unpack(animalia.temperate_biomes), + unpack(animalia.boreal_biomes) } } -}, 16, 1) \ No newline at end of file +}, animalia.spawn_interval, 4) \ No newline at end of file diff --git a/mobs/sheep.lua b/mobs/sheep.lua index b7626c3..1e63c9b 100644 --- a/mobs/sheep.lua +++ b/mobs/sheep.lua @@ -2,7 +2,7 @@ -- Sheep -- ----------- -local blend = better_fauna.frame_blend +local creative = minetest.settings:get_bool("creative_mode") local palette = { {"black", "Black", "#000000b0"}, @@ -22,6 +22,10 @@ local palette = { {"yellow", "Yellow", "#e3ff0070"}, } +local clamp_bone_rot = animalia.clamp_bone_rot + +local interp = animalia.interp + local min = math.min local abs = math.abs local random = math.random @@ -33,109 +37,113 @@ local function sheep_logic(self) return end - local pos = mobkit.get_stand_pos(self) - local prty = mobkit.get_queue_priority(self) - local player = mobkit.get_nearby_player(self) + if self.status ~= "following" then + if self.attention_span > 1 then + self.attention_span = self.attention_span - self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end + else + self.attention_span = self.attention_span + self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end - mob_core.random_sound(self, 16/self.dtime) + animalia.head_tracking(self, 0.5, 0.5) - if mobkit.timer(self,1) then + if mobkit.timer(self, 3) then - mob_core.vitals(self) + local pos = mobkit.get_stand_pos(self) + local prty = mobkit.get_queue_priority(self) + local player = mobkit.get_nearby_player(self) + + mob_core.random_sound(self, 14) mob_core.growth(self) - if self.status ~= "following" then - if self.attention_span > 1 then - self.attention_span = self.attention_span - 1 - mobkit.remember(self, "attention_span", self.attention_span) - end - else - self.attention_span = self.attention_span + 1 - mobkit.remember(self, "attention_span", self.attention_span) + if prty < 5 + and self.isinliquid then + animalia.hq_go_to_land(self, 5) end if prty < 4 and self.breeding then - better_fauna.hq_breed(self, 4) + animalia.hq_breed(self, 4) end if prty < 3 and self.gotten and math.random(1, 16) == 1 then - better_fauna.hq_eat(self, 3) + animalia.hq_eat(self, 3) end - - if prty < 2 - and player then - if self.attention_span < 5 then - if mob_core.follow_holding(self, player) then - better_fauna.hq_follow_player(self, 2, player) - self.attention_span = self.attention_span + 1 - end - end + + if prty == 2 + and not self.lasso_player + and (not player + or not mob_core.follow_holding(self, player)) then + mobkit.clear_queue_high(self) + end + + if prty < 2 then + if self.caught_with_lasso + and self.lasso_player then + animalia.hq_follow_player(self, 2, self.lasso_player, true) + elseif player then + if self.attention_span < 5 then + if mob_core.follow_holding(self, player) then + animalia.hq_follow_player(self, 2, player) + self.attention_span = self.attention_span + 3 + end + end + end end if mobkit.is_queue_empty_high(self) then - mob_core.hq_roam(self, 0) + animalia.hq_wander_group(self, 0, 12) end end end -minetest.register_entity("better_fauna:sheep",{ - max_hp = 20, - view_range = 16, - armor_groups = {fleshy = 100}, - physical = true, - collide_with_objects = true, - collisionbox = {-0.4, -0.4, -0.4, 0.4, 0.4, 0.4}, +animalia.register_mob("sheep", { + -- Stats + health = 20, + fleshy = 100, + view_range = 32, + lung_capacity = 10, + -- Visual + collisionbox = {-0.4, 0, -0.4, 0.4, 0.8, 0.4}, visual_size = {x = 10, y = 10}, - scale_stage1 = 0.5, - scale_stage2 = 0.65, - scale_stage3 = 0.80, - visual = "mesh", - mesh = "better_fauna_sheep.b3d", - textures = {"better_fauna_sheep.png^better_fauna_sheep_wool.png"}, - child_textures = {"better_fauna_sheep.png"}, - animation = { - stand = {range = {x = 30, y = 50}, speed = 10, frame_blend = blend, loop = true}, - walk = {range = {x = 1, y = 20}, speed = 30, frame_blend = blend, loop = true}, - run = {range = {x = 1, y = 20}, speed = 45, frame_blend = blend, loop = true}, + mesh = "animalia_sheep.b3d", + textures = {"animalia_sheep.png^animalia_sheep_wool.png"}, + child_textures = {"animalia_sheep.png"}, + animations = { + stand = {range = {x = 1, y = 60}, speed = 10, frame_blend = 0.3, loop = true}, + walk = {range = {x = 70, y = 110}, speed = 40, frame_blend = 0.3, loop = true}, + run = {range = {x = 70, y = 110}, speed = 50, frame_blend = 0.3, loop = true}, }, + -- Physics + speed = 4, + max_fall = 3, + -- Attributes sounds = { alter_child_pitch = true, random = { - name = "better_fauna_sheep_idle", + name = "animalia_sheep_idle", gain = 1.0, distance = 8 }, hurt = { - name = "better_fauna_sheep_idle", + name = "animalia_sheep_hurt", gain = 1.0, - pitch = 0.5, distance = 8 }, death = { - name = "better_fauna_sheep_idle", + name = "animalia_sheep_death", gain = 1.0, - pitch = 0.25, distance = 8 } }, - max_speed = 4, - stepheight = 1.1, - jump_height = 1.1, - buoyancy = 0.25, - lung_capacity = 10, - timeout = 1200, - ignore_liquidflag = false, - core_growth = false, - push_on_collide = true, - catch_with_net = true, + -- Behavior + defend_owner = false, follow = { - "farming:wheat" - }, - drops = { - {name = "better_fauna:mutton_raw", chance = 1, min = 1, max = 4} + "farming:wheat", }, consumable_nodes = { { @@ -147,43 +155,53 @@ minetest.register_entity("better_fauna:sheep",{ replacement = "default:dry_dirt" } }, - get_staticdata = mobkit.statfunc, - logic = sheep_logic, + drops = { + {name = "animalia:mutton_raw", chance = 1, min = 1, max = 4} + }, + -- Functions + head_data = { + offset = {x = 0, y = 0.41, z = 0}, + pitch_correction = -45, + pivot_h = 0.75, + pivot_v = 0.85 + }, + logic = sheep_logic, + get_staticdata = mobkit.statfunc, on_step = function(self, dtime, moveresult) - better_fauna.on_step(self, dtime, moveresult) + animalia.on_step(self, dtime, moveresult) if mobkit.is_alive(self) then - if self.object:get_properties().textures[1] == "better_fauna_sheep.png" + if self.object:get_properties().textures[1] == "animalia_sheep.png" and not self.gotten then self.object:set_properties({ - textures = {"better_fauna_sheep.png^better_fauna_sheep_wool.png"}, + textures = {"animalia_sheep.png^animalia_sheep_wool.png"}, }) end end end, on_activate = function(self, staticdata, dtime_s) - better_fauna.on_activate(self, staticdata, dtime_s) + animalia.on_activate(self, staticdata, dtime_s) self.dye_color = mobkit.recall(self, "dye_color") or "white" self.dye_hex = mobkit.recall(self, "dye_hex") or "" if self.dye_color ~= "white" and not self.gotten then self.object:set_properties({ - textures = {"better_fauna_sheep.png^(better_fauna_sheep_wool.png^[colorize:" .. self.dye_hex .. ")"}, + textures = {"animalia_sheep.png^(animalia_sheep_wool.png^[colorize:" .. self.dye_hex .. ")"}, }) end if self.gotten then self.object:set_properties({ - textures = {"better_fauna_sheep.png"}, + textures = {"animalia_sheep.png"}, }) end end, on_rightclick = function(self, clicker) - if better_fauna.feed_tame(self, clicker, 1, false, true) then return end - mob_core.protect(self, clicker, false) + if animalia.feed_tame(self, clicker, 1, false, true) then return end + mob_core.protect(self, clicker, true) mob_core.nametag(self, clicker, true) local item = clicker:get_wielded_item() local itemname = item:get_name() local name = clicker:get_player_name() - if itemname == "mobs:shears" + if itemname == "animalia:shears" and not self.gotten and not self.child then if not minetest.get_modpath("wool") then @@ -204,7 +222,7 @@ minetest.register_entity("better_fauna:sheep",{ clicker:set_wielded_item(item) self.object:set_properties({ - textures = {"better_fauna_sheep.png"}, + textures = {"animalia_sheep.png"}, }) end for _, color in ipairs(palette) do @@ -218,15 +236,15 @@ minetest.register_entity("better_fauna:sheep",{ self.dye_hex = mobkit.remember(self, "dye_hex", color[3]) self.drops = { - {name = "better_fauna:mutton_raw", chance = 1, min = 1, max = 4}, + {name = "animalia:mutton_raw", chance = 1, min = 1, max = 4}, {name = "wool:"..self.dye_color, chance = 2, min = 1, max = 2}, } self.object:set_properties({ - textures = {"better_fauna_sheep.png^(better_fauna_sheep_wool.png^[colorize:" .. color[3] .. ")"}, + textures = {"animalia_sheep.png^(animalia_sheep_wool.png^[colorize:" .. color[3] .. ")"}, }) - if not mobs.is_creative(clicker:get_player_name()) then + if not creative then item:take_item() clicker:set_wielded_item(item) end @@ -235,44 +253,44 @@ minetest.register_entity("better_fauna:sheep",{ end end end, - on_punch = function(self, puncher, _, tool_capabilities, dir) - mobkit.clear_queue_high(self) mob_core.on_punch_basic(self, puncher, tool_capabilities, dir) - better_fauna.hq_sporadic_flee(self, 10, puncher) - end, + animalia.hq_sporadic_flee(self, 10) + end }) -mob_core.register_spawn_egg("better_fauna:sheep", "f4e6cf", "e1ca9b") +mob_core.register_spawn_egg("animalia:sheep", "f4e6cf", "e1ca9b") mob_core.register_spawn({ - name = "better_fauna:sheep", - nodes = {"default:dirt_with_grass"}, + name = "animalia:sheep", min_light = 0, max_light = 15, min_height = -31000, max_height = 31000, min_rad = 24, max_rad = 256, - group = 6 -}, 2, 8) + group = 6, + optional = { + biomes = animalia.grassland_biomes + } +}, animalia.spawn_interval, 4) -minetest.register_craftitem("better_fauna:mutton_raw", { +minetest.register_craftitem("animalia:mutton_raw", { description = "Raw Mutton", - inventory_image = "better_fauna_mutton_raw.png", + inventory_image = "animalia_mutton_raw.png", on_use = minetest.item_eat(1), groups = {flammable = 2}, }) -minetest.register_craftitem("better_fauna:mutton_cooked", { +minetest.register_craftitem("animalia:mutton_cooked", { description = "Cooked Mutton", - inventory_image = "better_fauna_mutton_cooked.png", + inventory_image = "animalia_mutton_cooked.png", on_use = minetest.item_eat(6), groups = {flammable = 2}, }) minetest.register_craft({ type = "cooking", - recipe = "better_fauna:mutton_raw", - output = "better_fauna:mutton_cooked", + recipe = "animalia:mutton_raw", + output = "animalia:mutton_cooked", }) \ No newline at end of file diff --git a/mobs/turkey.lua b/mobs/turkey.lua index b878060..99d139f 100644 --- a/mobs/turkey.lua +++ b/mobs/turkey.lua @@ -2,7 +2,9 @@ -- Turkey -- ------------ -local blend = better_fauna.frame_blend +local clamp_bone_rot = animalia.clamp_bone_rot + +local interp = animalia.interp local function turkey_logic(self) @@ -10,115 +12,141 @@ local function turkey_logic(self) mob_core.on_die(self) return end - local prty = mobkit.get_queue_priority(self) - local player = mobkit.get_nearby_player(self) - if mobkit.timer(self,1) then + if self.status ~= "following" then + if self.attention_span > 1 then + self.attention_span = self.attention_span - self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end + else + self.attention_span = self.attention_span + self.dtime + mobkit.remember(self, "attention_span", self.attention_span) + end - mob_core.vitals(self) - mob_core.random_sound(self, 12) + animalia.head_tracking(self, 0.45, 0.25) + + if mobkit.timer(self, 3) then + + local prty = mobkit.get_queue_priority(self) + local player = mobkit.get_nearby_player(self) + + mob_core.random_sound(self, 14) + + if prty < 4 + and self.isinliquid then + animalia.hq_go_to_land(self, 4) + end if prty < 3 and self.breeding then - better_fauna.hq_fowl_breed(self, 3) - end - - if prty < 2 - and player then - if self.attention_span < 5 then - if mob_core.follow_holding(self, player) then - better_fauna.hq_follow_player(self, 2, player) - self.attention_span = self.attention_span + 1 - end - end + animalia.hq_fowl_breed(self, 3) end + if prty == 2 + and not self.lasso_player + and (not player + or not mob_core.follow_holding(self, player)) then + mobkit.clear_queue_high(self) + end + + if prty < 2 then + if self.caught_with_lasso + and self.lasso_player then + animalia.hq_follow_player(self, 2, self.lasso_player, true) + elseif player then + if self.attention_span < 5 then + if mob_core.follow_holding(self, player) then + animalia.hq_follow_player(self, 2, player) + self.attention_span = self.attention_span + 3 + end + end + end + end + if mobkit.is_queue_empty_high(self) then - mob_core.hq_roam(self, 0) + animalia.hq_wander_group(self, 0, 8) end end end -minetest.register_entity("better_fauna:turkey",{ - max_hp = 10, - view_range = 16, - armor_groups = {fleshy = 100}, - physical = true, - collide_with_objects = true, +animalia.register_mob("turkey", { + -- Stats + health = 15, + fleshy = 100, + view_range = 26, + lung_capacity = 10, + -- Visual collisionbox = {-0.3, -0.2, -0.3, 0.3, 0.4, 0.3}, visual_size = {x = 7, y = 7}, - scale_stage1 = 0.25, - scale_stage2 = 0.5, - scale_stage3 = 0.75, - visual = "mesh", - mesh = "better_fauna_turkey.b3d", - female_textures = {"better_fauna_turkey_hen.png"}, - male_textures = {"better_fauna_turkey_tom.png"}, - child_textures = {"better_fauna_turkey_chick.png"}, - animation = { - stand = {range = {x = 0, y = 0}, speed = 1, frame_blend = blend, loop = true}, - walk = {range = {x = 10, y = 30}, speed = 30, frame_blend = blend, loop = true}, - run = {range = {x = 10, y = 30}, speed = 45, frame_blend = blend, loop = true}, - fall = {range = {x = 40, y = 60}, speed = 30, frame_blend = blend, loop = true}, + mesh = "animalia_turkey.b3d", + female_textures = {"animalia_turkey_hen.png"}, + male_textures = {"animalia_turkey_tom.png"}, + child_textures = {"animalia_turkey_chick.png"}, + animations = { + stand = {range = {x = 0, y = 0}, speed = 1, frame_blend = 0.3, loop = true}, + walk = {range = {x = 10, y = 30}, speed = 30, frame_blend = 0.3, loop = true}, + run = {range = {x = 40, y = 60}, speed = 45, frame_blend = 0.3, loop = true}, + fall = {range = {x = 70, y = 90}, speed = 30, frame_blend = 0.3, loop = true}, }, + -- Physics + speed = 5, + max_fall = 6, + -- Attributes sounds = { alter_child_pitch = true, random = { - name = "better_fauna_turkey_idle", + name = "animalia_turkey_idle", gain = 1.0, distance = 8 }, hurt = { - name = "better_fauna_turkey_hurt", + name = "animalia_turkey_hurt", gain = 1.0, distance = 8 }, death = { - name = "better_fauna_turkey_death", + name = "animalia_turkey_death", gain = 1.0, distance = 8 } }, - max_speed = 4, - stepheight = 1.1, - jump_height = 1.1, - buoyancy = 0.25, - lung_capacity = 10, - timeout = 1200, - ignore_liquidflag = false, - core_growth = false, - push_on_collide = true, - catch_with_net = true, + -- Behavior + defend_owner = false, follow = { "farming:seed_cotton", "farming:seed_wheat" }, drops = { - {name = "better_fauna:feather", chance = 1, min = 1, max = 2}, - {name = "better_fauna:turkey_raw", chance = 1, min = 3, max = 5} + {name = "animalia:feather", chance = 1, min = 1, max = 2}, + {name = "animalia:poultry_raw", chance = 1, min = 2, max = 5} }, - on_step = better_fauna.on_step, - on_activate = better_fauna.on_activate, - get_staticdata = mobkit.statfunc, - phsyics = better_fauna.lightweight_physics, + -- Functions + head_data = { + offset = {x = 0, y = 0.15, z = 0}, + pitch_correction = 45, + pivot_h = 0.45, + pivot_v = 0.65 + }, + physics = animalia.lightweight_physics, logic = turkey_logic, + get_staticdata = mobkit.statfunc, + on_step = animalia.on_step, + on_activate = animalia.on_activate, on_rightclick = function(self, clicker) - if better_fauna.feed_tame(self, clicker, 1, false, true) then return end - mob_core.protect(self, clicker, false) + if animalia.feed_tame(self, clicker, 1, false, true) then return end + mob_core.protect(self, clicker, true) mob_core.nametag(self, clicker, true) end, on_punch = function(self, puncher, _, tool_capabilities, dir) - mobkit.clear_queue_high(self) mob_core.on_punch_basic(self, puncher, tool_capabilities, dir) - better_fauna.hq_sporadic_flee(self, 10, puncher) + animalia.hq_sporadic_flee(self, 10) end, }) -mob_core.register_spawn_egg("better_fauna:turkey", "352b22", "2f2721") +mob_core.register_spawn_egg("animalia:turkey", "352b22", "2f2721") mob_core.register_spawn({ - name = "better_fauna:turkey", - nodes = {"default:dry_dirt_with_dry_grass", "default:dirt_with_grass"}, + name = "animalia:turkey", min_light = 0, max_light = 15, min_height = -31000, @@ -127,30 +155,6 @@ mob_core.register_spawn({ max_rad = 256, group = 6, optional = { - biomes = { - "deciduous_forest", - "taiga" - } + biomes = animalia.temperate_biomes } -}, 16, 6) - - -minetest.register_craftitem("better_fauna:turkey_raw", { - description = "Raw Turkey", - inventory_image = "better_fauna_turkey_raw.png", - on_use = minetest.item_eat(1), - groups = {flammable = 2}, -}) - -minetest.register_craftitem("better_fauna:turkey_cooked", { - description = "Cooked Turkey", - inventory_image = "better_fauna_turkey_cooked.png", - on_use = minetest.item_eat(6), - groups = {flammable = 2}, -}) - -minetest.register_craft({ - type = "cooking", - recipe = "better_fauna:turkey_raw", - output = "better_fauna:turkey_cooked", -}) \ No newline at end of file +}, animalia.spawn_interval, 6) \ No newline at end of file diff --git a/mobs/wolf.lua b/mobs/wolf.lua new file mode 100644 index 0000000..cff5c35 --- /dev/null +++ b/mobs/wolf.lua @@ -0,0 +1,237 @@ +---------- +-- Wolf -- +---------- + +local clamp_bone_rot = animalia.clamp_bone_rot + +local interp = animalia.interp + +local follow = { + "animalia:mutton_raw", + "animalia:beef_raw", + "animalia:porkchop_raw", + "animalia:poultry_raw" +} + +if minetest.registered_items["bonemeal:bone"] then + follow = { + "bonemeal:bone", + "animalia:beef_raw", + "animalia:porkchop_raw", + "animalia:mutton_raw", + "animalia:poultry_raw" + } +end + +function animalia.bh_attack(self, prty, target) + if mobkit.is_alive(target) then + if target:is_player() then + if not self.tamed + or target:get_player_name() ~= self.owner then + animalia.hq_attack(self, prty, target) + end + elseif target:get_luaentity() then + if not self.tamed + or not mob_core.shared_owner(self, target) then + animalia.hq_attack(self, prty, target) + end + end + end +end + +local function wolf_logic(self) + + if self.hp <= 0 then + mob_core.on_die(self) + return + end + + animalia.head_tracking(self, 0.5, 0.75) + + if mobkit.timer(self, 1) then + + local prty = mobkit.get_queue_priority(self) + local player = mobkit.get_nearby_player(self) + + mob_core.random_sound(self, 22) + mob_core.growth(self) + + if self.status ~= "following" then + if self.attention_span > 1 then + self.attention_span = self.attention_span - 1 + mobkit.remember(self, "attention_span", self.attention_span) + end + else + self.attention_span = self.attention_span + 1 + mobkit.remember(self, "attention_span", self.attention_span) + end + + if prty < 22 + and self.order == "sit" then + if not mobkit.is_queue_empty_high(self) then + mobkit.clear_queue_high(self) + end + mobkit.animate(self, "sit") + return + end + + if prty < 21 + and self.owner_target then + if not mob_core.shared_owner(self, self.owner_target) then + animalia.hq_attack(self, 21, self.owner_target) + end + end + + if prty < 20 + and self.order == "follow" + and self.owner + and minetest.get_player_by_name(self.owner) then + local owner = minetest.get_player_by_name(self.owner) + animalia.hq_follow_player(self, 20, owner, true) + end + + if prty < 5 + and self.isinliquid then + animalia.hq_go_to_land(self, 5) + end + + if prty < 4 + and self.breeding then + animalia.hq_breed(self, 4) + end + + if prty == 3 + and not self.lasso_player + and (not player + or not mob_core.follow_holding(self, player)) then + mobkit.clear_queue_high(self) + end + + if prty < 3 then + if self.caught_with_lasso + and self.lasso_player then + animalia.hq_follow_player(self, 3, self.lasso_player, true) + elseif player then + if self.attention_span < 5 then + if mob_core.follow_holding(self, player) then + animalia.hq_follow_player(self, 3, player) + self.attention_span = self.attention_span + 3 + end + end + end + end + + if prty < 2 then + local target = mobkit.get_closest_entity(self, "animalia:sheep") + if target then + animalia.bh_attack(self, 2, target) + end + end + + if mobkit.is_queue_empty_high(self) then + animalia.hq_wander_group(self, 0, 8) + end + end +end + +animalia.register_mob("wolf", { + -- Stats + health = 25, + fleshy = 100, + view_range = 32, + lung_capacity = 10, + -- Visual + collisionbox = {-0.35, -0.375, -0.35, 0.35, 0.4, 0.35}, + visual_size = {x = 9, y = 9}, + scale_stage1 = 0.5, + scale_stage2 = 0.65, + scale_stage3 = 0.80, + mesh = "animalia_wolf.b3d", + textures = {"animalia_wolf.png"}, + animations = { + stand = {range = {x = 30, y = 49}, speed = 10, frame_blend = 0.3, loop = true}, + sit = {range = {x = 60, y = 90}, speed = 20, frame_blend = 0.3, loop = true}, + walk = {range = {x = 1, y = 20}, speed = 30, frame_blend = 0.3, loop = true}, + run = {range = {x = 1, y = 20}, speed = 45, frame_blend = 0.3, loop = true}, + }, + -- Physics + speed = 8, + max_fall = 4, + -- Attributes + sounds = { + alter_child_pitch = true, + random = { + name = "animalia_wolf_idle", + gain = 1.0, + distance = 8 + }, + hurt = { + name = "animalia_wolf_hurt", + gain = 1.0, + pitch = 0.5, + distance = 8 + }, + death = { + name = "animalia_wolf_death", + gain = 1.0, + distance = 8 + } + }, + reach = 2, + damage = 3, + knockback = 2, + punch_cooldown = 1, + -- Behavior + defend_owner = true, + follow = { + "bonemeal:bone", + "animalia:beef_raw", + "animalia:porkchop_raw", + "animalia:mutton_raw", + "animalia:poultry_raw" + }, + -- Functions + head_data = { + offset = {x = 0, y = 0.22, z = 0}, + pitch_correction = -20, + pivot_h = 0.65, + pivot_v = 0.65 + }, + logic = wolf_logic, + get_staticdata = mobkit.statfunc, + on_step = animalia.on_step, + on_activate = animalia.on_activate, + on_rightclick = function(self, clicker) + if animalia.feed_tame(self, clicker, math.random(3, 5), true, true) then return end + mob_core.protect(self, clicker, false) + mob_core.nametag(self, clicker, true) + if not self.owner + or clicker:get_player_name() ~= self.owner then return end + if self.order == "wander" then + self.order = "follow" + elseif self.order == "follow" then + self.order = "sit" + else + self.order = "wander" + end + mobkit.remember(self, "order", self.order) + end, + on_punch = function(self, puncher, _, tool_capabilities, dir) + mob_core.on_punch_basic(self, puncher, tool_capabilities, dir) + animalia.bh_attack(self, 10, puncher) + end +}) + +mob_core.register_spawn_egg("animalia:wolf", "a19678" ,"231b13") + +mob_core.register_spawn({ + name = "animalia:wolf", + min_light = 0, + max_light = 15, + min_height = -31000, + max_height = 31000, + group = 4, + optional = { + biomes = animalia.temperate_biomes + } +}, animalia.spawn_interval, 4) \ No newline at end of file diff --git a/mod.conf b/mod.conf index a7f02c8..6cb5b52 100644 --- a/mod.conf +++ b/mod.conf @@ -1,3 +1,4 @@ -name = better_fauna -depends = mobkit, mob_core, default +name = animalia +depends = mobkit, mob_core +optional_depends = default description = Basic Fauna for Minetest Game diff --git a/models/animalia_cat.b3d b/models/animalia_cat.b3d new file mode 100644 index 0000000..de68a36 Binary files /dev/null and b/models/animalia_cat.b3d differ diff --git a/models/animalia_chicken.b3d b/models/animalia_chicken.b3d new file mode 100644 index 0000000..f674639 Binary files /dev/null and b/models/animalia_chicken.b3d differ diff --git a/models/animalia_cow.b3d b/models/animalia_cow.b3d new file mode 100644 index 0000000..91bea08 Binary files /dev/null and b/models/animalia_cow.b3d differ diff --git a/models/animalia_horse.b3d b/models/animalia_horse.b3d new file mode 100644 index 0000000..93e779b Binary files /dev/null and b/models/animalia_horse.b3d differ diff --git a/models/animalia_lasso.b3d b/models/animalia_lasso.b3d new file mode 100644 index 0000000..ddeb5c0 Binary files /dev/null and b/models/animalia_lasso.b3d differ diff --git a/models/animalia_pig.b3d b/models/animalia_pig.b3d new file mode 100644 index 0000000..cdc6dd7 Binary files /dev/null and b/models/animalia_pig.b3d differ diff --git a/models/animalia_sheep.b3d b/models/animalia_sheep.b3d new file mode 100644 index 0000000..22b615f Binary files /dev/null and b/models/animalia_sheep.b3d differ diff --git a/models/animalia_turkey.b3d b/models/animalia_turkey.b3d new file mode 100644 index 0000000..f57e1bd Binary files /dev/null and b/models/animalia_turkey.b3d differ diff --git a/models/animalia_wolf.b3d b/models/animalia_wolf.b3d new file mode 100644 index 0000000..6f43a40 Binary files /dev/null and b/models/animalia_wolf.b3d differ diff --git a/settingtypes.txt b/settingtypes.txt new file mode 100644 index 0000000..94e09d5 --- /dev/null +++ b/settingtypes.txt @@ -0,0 +1,5 @@ +# How many chunks can generate before adding another to spawn queue +chunk_spawn_add_int (Chunk Spawning Addition Interval) float 32 + +# How often (in seconds) the spawn queue is executed and cleared +chunk_spawn_queue_int (Chunk Spawning Queue Execution Interval) float 10 \ No newline at end of file