From 288d15225b37918e209567b99ca7da588d8941a2 Mon Sep 17 00:00:00 2001 From: ElCeejo Date: Wed, 2 Mar 2022 22:21:46 -0800 Subject: [PATCH] Fix multiple crashes --- api/behaviors.lua | 8 +- api/spawning.lua | 6 +- behaviors.lua | 1663 ---------------------- mobs/wolf.lua | 1 + textures/animalia_shears.png | Bin 5360 -> 0 bytes textures/items/animalia_cat_toy.png | Bin 6180 -> 6405 bytes textures/items/animalia_egg.png | Bin 5462 -> 5537 bytes textures/items/animalia_feather.png | Bin 5442 -> 5656 bytes textures/items/animalia_porkchop_raw.png | Bin 6008 -> 6203 bytes textures/items/animalia_saddle.png | Bin 771 -> 5298 bytes 10 files changed, 10 insertions(+), 1668 deletions(-) delete mode 100644 behaviors.lua delete mode 100644 textures/animalia_shears.png diff --git a/api/behaviors.lua b/api/behaviors.lua index fd2db4b..00cb627 100644 --- a/api/behaviors.lua +++ b/api/behaviors.lua @@ -745,8 +745,8 @@ creatura.register_utility("animalia:boid_wander", function(self, group) group_tick = 0 end if (move - and goal) - or far_from_group then + or far_from_group) + and goal then animalia.action_boid_walk(self, goal, 6, "creatura:neighbors", 0.35) else creatura.action_idle(self, idle_time) @@ -979,7 +979,9 @@ creatura.register_utility("animalia:boid_flee_from_player", function(self, playe and vec_dist(pos, self.lasso_pos) > 10 then escape_pos = self.lasso_pos end - animalia.action_boid_walk(self, escape_pos, 6, "creatura:obstacle_avoidance", 1, "run") + if escape_pos then + animalia.action_boid_walk(self, escape_pos, 6, "creatura:obstacle_avoidance", 1, "run") + end end if vec_dist(pos, tpos) > self.tracking_range + (#mobs_in_group or 0) then return true diff --git a/api/spawning.lua b/api/spawning.lua index a15f680..24a66a8 100644 --- a/api/spawning.lua +++ b/api/spawning.lua @@ -257,7 +257,8 @@ minetest.register_on_generated(function(minp, maxp) z = zcen, } local spawnable_mobs = get_spawnable_mobs(center) - if spawnable_mobs then + if spawnable_mobs + and #spawnable_mobs > 0 then local mob = spawnable_mobs[random(#spawnable_mobs)] table.insert(animalia.spawn_queue, {pos = center, mob = mob, group = random(3, 4)}) table.insert(animalia.spawn_points, center) @@ -280,7 +281,8 @@ minetest.register_globalstep(function(dtime) if dist_to_nearest_player(point) < 48 and minetest.get_node_or_nil(point) then local spawnable_mobs = get_spawnable_mobs(point) - if spawnable_mobs then + if spawnable_mobs + and #spawnable_mobs > 0 then local mob = spawnable_mobs[random(#spawnable_mobs)] local objects = minetest.get_objects_inside_radius(point, 32) local spawn = true diff --git a/behaviors.lua b/behaviors.lua deleted file mode 100644 index 9d6f0bb..0000000 --- a/behaviors.lua +++ /dev/null @@ -1,1663 +0,0 @@ ---------------- --- Behaviors -- ---------------- - --- Math -- - -local abs = math.abs -local random = math.random -local ceil = math.ceil -local floor = math.floor -local rad = math.rad - -local function average(t) - local sum = 0 - for _,v in pairs(t) do -- Get the sum of all numbers in t - sum = sum + v - end - return sum / #t -end - -local function clamp(val, min, max) - if val < min then - val = min - elseif max < val then - val = max - end - return val -end - --- Vector Math -- - -local vec_dist = vector.distance -local vec_dir = vector.direction -local vec_sub = vector.subtract -local vec_add = vector.add -local vec_multi = vector.multiply -local vec_normal = vector.normalize - -local function vec_raise(v, n) - return {x = v.x, y = v.y + n, z = v.z} -end - -local yaw2dir = minetest.yaw_to_dir -local dir2yaw = minetest.dir_to_yaw - --------------- --- Settings -- --------------- - ------------- --- Tables -- ------------- - -local is_flyable = {} -local is_liquid = {} -local is_solid = {} - -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 - or minetest.registered_nodes[name].drawtype == "liquid" then - is_flyable[name] = true - if minetest.registered_nodes[name].walkable then - is_solid[name] = true - else - is_liquid[name] = true - end - end - end - end -end) - ---------------------- --- Local Utilities -- ---------------------- - -local moveable = creatura.is_pos_moveable -local fast_ray_sight = creatura.fast_ray_sight - -local function get_ground_level(pos2, max_height) - local node = minetest.get_node(pos2) - local node_under = minetest.get_node({ - x = pos2.x, - y = pos2.y - 1, - z = pos2.z - }) - local height = 0 - local walkable = is_solid[node_under.name] and not is_solid[node.name] - if walkable then - return pos2 - elseif not walkable then - if not is_solid[node_under.name] then - while not is_solid[node_under.name] - and height < max_height do - pos2.y = pos2.y - 1 - node_under = minetest.get_node({ - x = pos2.x, - y = pos2.y - 1, - z = pos2.z - }) - height = height + 1 - end - else - while is_solid[node.name] - and height < max_height do - pos2.y = pos2.y + 1 - node = minetest.get_node(pos2) - height = height + 1 - end - end - return pos2 - end -end - -local function get_ceiling_positions(pos, range) - local walkable = minetest.find_nodes_in_area( - {x = pos.x + range, y = pos.y + range, z = pos.z + range}, - {x = pos.x - range, y = pos.y, z = pos.z - range}, - animalia.walkable_nodes - ) - if #walkable < 1 then return {} end - local output = {} - for i = 1, #walkable do - local i_pos = walkable[i] - local under = { - x = i_pos.x, - y = i_pos.y - 1, - z = i_pos.z - } - if minetest.get_node(under).name == "air" - and is_solid[minetest.get_node(i_pos).name] then - table.insert(output, i_pos) - end - end - return output -end - -local function get_obstacle_avoidance(self, lift) - local pos = self.object:get_pos() - local yaw = self.object:get_yaw() - pos.y = pos.y + self.stepheight - local vel = self.object:get_velocity() - local vel_len = abs(vector.length(vel)) - if vel_len < 1.5 then - vel_len = 1.5 - end - local dir = yaw2dir(yaw) - dir.y = lift - local outset = vec_add(pos, vec_multi(dir, vel_len)) - local pos2 - local obstacle = false - if not fast_ray_sight(pos, outset) then - pos2 = vec_add(pos, vec_multi(dir, -vel_len)) - obstacle = true - end - return pos2, obstacle -end - -local function get_wander_pos_3d(self, range) - local outset = random(range or 4) - local pos = self.object:get_pos() - local move_dir = { - x = random(-10, 10) * 0.1, - z = random(-10, 10) * 0.1, - y = random(-10, 10) * 0.1 - } - local pos2 = vec_add(pos, vec_multi(vec_normal(move_dir), random(1, outset))) - local sight, dist = fast_ray_sight(pos, pos2, true) - if not sight then - pos2 = vec_add(pos, vec_multi(vec_normal(move_dir), dist - 0.5)) - end - return pos2 -end - -local function get_boid_members(pos, radius, name, texture_no) - local objects = minetest.get_objects_inside_radius(pos, radius) - if #objects < 2 then return {} end - local members = {} - local max_boid = minetest.registered_entities[name].max_boids or 7 - for i = 1, #objects do - if #members > max_boid then break end - local object = objects[i] - if object:get_luaentity() - and object:get_luaentity().name == name - and object:get_luaentity().texture_no == texture_no then - object:get_luaentity().boid_heading = rad(random(360)) - table.insert(members, object) - end - end - return members -end - ----------------------- --- Movement Methods -- ----------------------- - -local function movement_fly(self, pos2) - -- Initial Properties - local pos = self.object:get_pos() - local turn_rate = self.turn_rate or 10 - local speed = self.speed or 2 - self:animate("fly") - self:set_gravity(0) - -- Collision Avoidance - local temp_goal = self._movement_data.temp_goal - local obstacle = self._movement_data.obstacle or false - if not temp_goal - or self:pos_in_box(temp_goal, self.width) then - self._movement_data.temp_goal, self._movement_data.obstacle = get_obstacle_avoidance(self, vec_dir(pos, pos2).y) - end - local neighbor = self._movement_data.temp_goal - -- Calculate Movement - local dir = vector.direction(pos, pos2) - local tyaw = minetest.dir_to_yaw(dir) - if neighbor then - local lift = dir.y - dir = vector.direction(pos, neighbor) - if not obstacle then - dir.y = lift - end - tyaw = minetest.dir_to_yaw(dir) - end - if self._path - and #self._path > 1 then - neighbor = self._path[2] - dir = vector.direction(pos, neighbor) - tyaw = minetest.dir_to_yaw(dir) - if self:pos_in_box(neighbor, self.width + 0.2) then - table.remove(self._path, 1) - end - else - self._path = creatura.find_path(self, pos, pos2, self.width, self.height, 300, false, true) - end - -- Apply Movement - self:turn_to(tyaw, turn_rate) - self:set_forward_velocity(speed) - local v_speed = speed * dir.y - local vel = self.object:get_velocity() - vel.y = vel.y + (v_speed - vel.y) * 0.2 - self:set_vertical_velocity(vel.y) - if self:pos_in_box(pos2) then - self:halt() - end -end - -creatura.register_movement_method("animalia:fly_path", movement_fly) - -local function movement_fly_waypoints(self, pos2, speed) - -- Initial Properties - local pos = self.object:get_pos() - self:animate("fly") - self:set_gravity(0) - -- Collision Avoidance - local temp_goal = self._movement_data.temp_goal - local obstacle = self._movement_data.obstacle or false - if not temp_goal - or self:pos_in_box(temp_goal, 0.4) then - self._movement_data.temp_goal, self._movement_data.obstacle = get_obstacle_avoidance(self, vec_dir(pos, pos2).y) - end - local neighbor = self._movement_data.temp_goal - -- Calculate Movement - local dir = vector.direction(pos, pos2) - local tyaw = minetest.dir_to_yaw(dir) - local turn_rate = self.turn_rate or 10 - local speed = self.speed or 2 - if neighbor then - local lift = dir.y - dir = vector.direction(pos, neighbor) - if not obstacle then - dir.y = lift - end - tyaw = minetest.dir_to_yaw(dir) - end - -- Apply Movement - self:turn_to(boid_angle or tyaw, turn_rate) - self:set_forward_velocity(speed) - local v_speed = speed * dir.y - local vel = self.object:get_velocity() - vel.y = vel.y + (v_speed - vel.y) * 0.2 - self:set_vertical_velocity(vel.y) - if self:pos_in_box(pos2) then - self:halt() - end -end - -creatura.register_movement_method("animalia:fly_waypoints", movement_fly_waypoints) - --- Fly Obstacle Avoidance -- - -local function movement_fly_obstacle_avoidance(self, pos2, speed) - -- Initial Properties - local pos = self.object:get_pos() - local turn_rate = self.turn_rate or 10 - local speed = self.speed or 2 - self:animate("fly") - self:set_gravity(0) - -- Collision Avoidance - local temp_goal = self._movement_data.temp_goal - local obstacle = self._movement_data.obstacle or false - local timer = self._movement_data.timer - if not temp_goal - or self:pos_in_box(temp_goal, 0.4) - or (timer - and timer <= 0) then - self._movement_data.temp_goal, self._movement_data.obstacle = get_obstacle_avoidance(self, vec_dir(pos, pos2).y) - temp_goal = self._movement_data.temp_goal - obstacle = self._movement_data.obstacle or false - if temp_goal then - self._movement_data.timer = 3 - end - end - if timer then - self._movement_data.timer = self._movement_data.timer - self.dtime - end - -- Calculate Movement - local dir = vector.direction(pos, pos2) - local tyaw = minetest.dir_to_yaw(dir) - if temp_goal - and obstacle then - dir = vector.direction(pos, temp_goal) - dir.y = vec_dir(pos, pos2).y - tyaw = minetest.dir_to_yaw(dir) - end - -- Apply Movement - self:turn_to(tyaw, turn_rate) - self:set_forward_velocity(speed) - local v_speed = (speed) * dir.y - local vel = self.object:get_velocity() - vel.y = vel.y + (v_speed - vel.y) * 0.2 - self:set_vertical_velocity(vel.y) - if self:pos_in_box(pos2, 0.5) then - self:halt() - end -end - -creatura.register_movement_method("animalia:fly_obstacle_avoidance", movement_fly_obstacle_avoidance) - --- Swimming -- - -local function movement_swim_obstacle_avoidance(self, pos2, speed) - -- Initial Properties - local pos = self.object:get_pos() - self:animate("swim") - self:set_gravity(0) - -- Collision Avoidance - local temp_goal = self._movement_data.temp_goal - local obstacle = self._movement_data.obstacle or false - local timer = self._movement_data.timer - if not temp_goal - or self:pos_in_box(temp_goal, 0.4) - or (timer - and timer <= 0) then - self._movement_data.temp_goal, self._movement_data.obstacle = get_obstacle_avoidance(self, vec_dir(pos, pos2).y) - temp_goal = self._movement_data.temp_goal - obstacle = self._movement_data.obstacle or false - if temp_goal then - self._movement_data.timer = vec_dist(pos, temp_goal) / self.speed - end - end - if timer then - self._movement_data.timer = self._movement_data.timer - self.dtime - end - -- Calculate Movement - local dir = vector.direction(pos, pos2) - local tyaw = minetest.dir_to_yaw(dir) - if temp_goal - and obstacle then - dir = vector.direction(pos, temp_goal) - tyaw = minetest.dir_to_yaw(dir) - end - -- Apply Movement - self:turn_to(tyaw, turn_rate) - self:set_forward_velocity(speed) - local v_speed = speed * dir.y - local vel = self.object:get_velocity() - vel.y = vel.y + (v_speed - vel.y) * 0.2 - if not is_liquid[minetest.get_node(vec_raise(pos, 1)).name] - and vel.y > 0 then - vel.y = 0 - end - self:set_vertical_velocity(vel.y) - if self:pos_in_box(pos2) then - self:halt() - end -end - -creatura.register_movement_method("animalia:swim_obstacle_avoidance", movement_swim_obstacle_avoidance) - -------------- --- Actions -- -------------- - -function animalia.action_fall(self) - local function func(self) - self:animate("fall") - self:set_gravity(-1) - local vel = self.object:get_velocity() - if vel.y < -3.8 then - self:set_vertical_velocity(-0.1) - end - self._fall_start = nil - if self.touching_ground then - return true - end - end - self:set_action(func) -end - -function animalia.action_punch(self, target) - local function func(self) - if not creatura.is_alive(target) then - return true - end - local yaw = self.object:get_yaw() - local pos = self.object:get_pos() - local tpos = target:get_pos() - local dir = vector.direction(pos, tpos) - local tyaw = minetest.dir_to_yaw(dir) - self:turn_to(tyaw) - if self.touching_ground then - self:animate("leap") - local jump_vel = vec_multi(dir, self.speed) - jump_vel.y = 3 - self.object:add_velocity(jump_vel) - end - if vec_dist(pos, tpos) < 2 then - self:punch_target(target) - return true - end - end - self:set_action(func) -end - -function animalia.action_latch_to_ceil(self, time, anim) - local timer = time - local function func(self) - self:halt() - self:set_forward_velocity(0) - self:set_vertical_velocity(9) - self:set_gravity(3) - self:animate(anim or "latch") - timer = timer - self.dtime - if timer <= 0 then - return true - end - end - self:set_action(func) -end - -function animalia.action_boid_move(self, pos2, timeout, method) - local boids = get_boid_members(self.object:get_pos(), 6, self.name, self.texture_no) - local timer = timeout - local goal = pos2 - local function func(self) - local pos = self.object:get_pos() - timer = timer - self.dtime - if #boids > 2 then - local boid_angle, boid_lift = creatura.get_boid_angle(self, boids, 6) - if boid_angle then - local dir2goal = vec_dir(pos, pos2) - local yaw2goal = minetest.dir_to_yaw(dir2goal) - boid_angle = boid_angle + (yaw2goal - boid_angle) * 0.15 - local boid_dir = minetest.yaw_to_dir(boid_angle) - if boid_lift then - boid_dir.y = boid_lift + (vec_dir(pos, goal).y - boid_lift) * 0.5 - else - boid_dir.y = vec_dir(pos, goal).y - end - pos2 = vec_add(pos, vec_multi(boid_dir, 4)) - end - end - if timer <= 0 - or self:pos_in_box(pos2, 0.25) then - self:halt() - return true - end - self:move(pos2, method or "animalia:fly_obstacle_avoidance", 1) - end - self:set_action(func) -end - -function animalia.action_boid_walk(self, pos2, timeout, method, speed_factor, anim) - local boids = creatura.get_boid_members(self.object:get_pos(), 12, self.name) - local timer = timeout - local move_init = false - local goal = pos2 - local function func(self) - local pos = self.object:get_pos() - timer = timer - self.dtime - if #boids > 2 then - local boid_angle = creatura.get_boid_angle(self, boids, 12) - if boid_angle then - local dir2goal = vec_dir(pos, goal) - local yaw2goal = minetest.dir_to_yaw(dir2goal) - boid_angle = boid_angle + (yaw2goal - boid_angle) * 0.15 - local boid_dir = minetest.yaw_to_dir(boid_angle) - pos2 = get_ground_level(vec_add(pos, vec_multi(boid_dir, 4)), 2) - end - end - if not pos2 - or (move_init - and not self._movement_data.goal) then - return true - end - if timer <= 0 - or self:pos_in_box({x = goal.x, y = pos.y + 0.1, z = goal.z}) - or vec_dist(pos, goal) < 1 then - self:halt() - return true - end - self:move(pos2, method or "creatura:obstacle_avoidance", speed_factor or 1, anim or "walk") - move_init = true - end - self:set_action(func) -end - -function animalia.action_swim(self, pos, timeout, method, speed_factor, anim) - local timer = timeout or 4 - local function func(self) - timer = timer - self.dtime - if timer <= 0 - or self:pos_in_box(pos) then - self:halt() - self:set_gravity(0) - return true - end - self:move(pos, method or "animalia:swim_obstacle_avoidance", speed_factor or 0.5, anim) - self:set_gravity(0) - end - self:set_action(func) -end - -function animalia.action_horse_spin(self, speed, anim) - local tyaw = random(math.pi * 2) - local function func(self) - self:set_gravity(-9.8) - self:halt() - self:animate(anim or "stand") - self:turn_to(tyaw, speed) - if abs(tyaw - self.object:get_yaw()) < 0.1 then - return true - end - end - self:set_action(func) -end - ---------------- --- Behaviors -- ---------------- - ------------------------- --- Register Utilities -- ------------------------- - --- Wander - -creatura.register_utility("animalia:wander", function(self, group) - local idle_time = 3 - local move_probability = 5 - local far_from_group = false - local group_tick = 1 - local function func(self) - local pos = self.object:get_pos() - if not self:get_action() then - local goal - local move = random(move_probability) < 2 - if self.lasso_pos - and vec_dist(pos, self.lasso_pos) > 10 then - goal = self.lasso_pos - end - if not goal - and move then - goal = self:get_wander_pos(1, 2) - end - if group - and goal - and group_tick > 3 then - local range = self.tracking_range * 0.5 - local group_positions = animalia.get_group_positions(self.name, pos, range + 1) - if #group_positions > 2 then - local center = animalia.get_average_pos(group_positions) - if center - and vec_dist(pos, center) > range * 0.33 - or vec_dist(goal, center) > range * 0.33 then - goal = center - far_from_group = true - else - far_from_group = false - end - end - group_tick = 0 - end - if (move - and goal) - or far_from_group then - creatura.action_walk(self, goal, 2, "creatura:neighbors") - else - creatura.action_idle(self, idle_time) - end - if group then - group_tick = group_tick + 1 - end - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:skittish_wander", function(self) - local idle_time = 3 - local move_probability = 3 - local force_move = false - local avoid_tick = 1 - local function func(self) - local pos = self.object:get_pos() - if not self:get_action() then - local goal - local move = random(move_probability) < 2 - if avoid_tick > 3 - and move then - local range = self.tracking_range * 0.5 - local player = creatura.get_nearby_player(self) - if player then - local target_alive, line_of_sight, player_pos = self:get_target(player) - if target_alive - and line_of_sight - and vec_dist(pos, player_pos) < 8 then - force_move = true - local dir = vec_dir(player_pos, pos) - goal = self:get_wander_pos(2, 3, dir) - end - end - avoid_tick = 0 - end - if self.lasso_pos - and vec_dist(pos, self.lasso_pos) > 10 then - goal = self.lasso_pos - end - if not goal - and move then - goal = self:get_wander_pos(4, 4) - end - if move - and goal then - creatura.action_walk(self, goal, 3, "creatura:neighbors", 0.35) - else - creatura.action_idle(self, idle_time) - end - avoid_tick = avoid_tick + 1 - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:skittish_boid_wander", function(self) - local idle_time = 3 - local move_probability = 3 - local group_tick = 0 - local force_move = false - local function func(self) - local pos = self.object:get_pos() - local goal - if self:timer(3) then - local range = self.tracking_range * 0.5 - local group_positions = animalia.get_group_positions(self.name, pos, range + 1) - if #group_positions > 2 then - local center = animalia.get_average_pos(group_positions) - if center - and vec_dist(pos, center) > range then - goal = center - force_move = true - else - force_move = false - end - else - force_move = false - end - group_tick = 2 - local player = creatura.get_nearby_player(self) - if player then - local target_alive, line_of_sight, player_pos = self:get_target(player) - if target_alive - and line_of_sight - and vec_dist(pos, player_pos) < 8 then - force_move = true - local dir = vec_dir(player_pos, pos) - goal = self:get_wander_pos(2, 3, dir) - end - end - end - if not self:get_action() then - local move = random(move_probability) < 2 - if self.lasso_pos - and vec_dist(pos, self.lasso_pos) > 10 then - goal = self.lasso_pos - end - if not goal - and move then - goal = self:get_wander_pos(4, 4) - end - if move - and goal then - animalia.action_boid_walk(self, goal, 6, "creatura:neighbors", 0.35) - else - creatura.action_idle(self, idle_time) - end - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:boid_wander", function(self, group) - local idle_time = 3 - local move_probability = 5 - local group_tick = 1 - local function func(self) - local pos = self.object:get_pos() - if not self:get_action() then - local goal - local move = random(move_probability) < 2 - if self.lasso_pos - and vec_dist(pos, self.lasso_pos) > 10 then - goal = self.lasso_pos - end - if not goal - and move then - goal = self:get_wander_pos(1, 2) - end - if group - and goal - and group_tick > 3 then - local range = self.tracking_range * 0.5 - local group_positions = animalia.get_group_positions(self.name, pos, range + 1) - if #group_positions > 2 then - local center = animalia.get_average_pos(group_positions) - if center - and vec_dist(pos, center) > range * 0.33 - or vec_dist(goal, center) > range * 0.33 then - goal = center - far_from_group = true - else - far_from_group = false - end - end - group_tick = 0 - end - if (move - or far_from_group) - and goal then - animalia.action_boid_walk(self, goal, 6, "creatura:neighbors", 0.35) - else - creatura.action_idle(self, idle_time) - end - group_tick = group_tick + 1 - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:wander_water_surface", function(self) - local idle_time = 3 - local move_probability = 3 - local function func(self) - if not self.in_liquid then return true end - local pos = self.object:get_pos() - local random_goal = get_wander_pos_3d(self, 2) - if not self:get_action() then - 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 - animalia.action_swim(self, random_goal) - else - creatura.action_idle(self, idle_time, "float") - end - end - self:set_gravity(0) - end - self:set_utility(func) -end) - --- "Eat" nodes - -creatura.register_utility("animalia:eat_from_turf", function(self) - local action_init = false - local function func(self) - local pos = self.object:get_pos() - local look_dir = yaw2dir(self.object:get_yaw()) - local under = vec_add(pos, vec_multi(look_dir, self.width)) - under.y = pos.y - 0.5 - if not action_init then - for i, 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 - self:memorize("gotten", self.gotten) - if not self:get_action() then - creatura.action_idle(self, 1, "eat") - action_init = true - end - break - elseif i == #self.consumable_nodes then - return true - end - end - elseif not self:get_action() then - return true - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:eat_bug_nodes", function(self) - local timer = 0.2 - local pos = self.object:get_pos() - local food = minetest.find_nodes_in_area(vec_sub(pos, 1.5), vec_add(pos, 1.5), self.follow) - local function func(self) - pos = self.object:get_pos() - if food[1] then - local dist = vector.distance(pos, food[1]) - local dir = vec_dir(pos, food[1]) - local frame = floor(dist * 10) - self:turn_to(dir2yaw(dir)) - if frame < 15 - and frame > 1 then - animalia.move_head(self, dir2yaw(dir), dir.y) - creatura.action_idle(self, 0.1, "tongue_" .. frame) - timer = timer - self.dtime - elseif not self:get_action() then - local pos2 = vec_add(food[1], vec_multi(vec_normal(vec_dir(food[1], pos)), 0.25)) - creatura.action_walk(self, pos2) - end - else - return true - end - if timer <= 0 - and food[1] then - minetest.remove_node(food[1]) - return true - end - end - self:set_utility(func) -end) - --- Escape Water - -creatura.register_utility("animalia:swim_to_land", function(self) - local init = false - local tpos = nil - local function func(self) - if not init then - for i = 1, 359, 15 do - local yaw = rad(i) - local dir = yaw2dir(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 = self.object:get_pos() - local yaw = self.object:get_yaw() - local tyaw = minetest.dir_to_yaw(vec_dir(pos, tpos)) - if abs(tyaw - yaw) > 0.1 then - self:turn_to(tyaw, 12) - end - self:set_gravity(-9.8) - self:set_forward_velocity(self.speed * 0.66) - self:animate("walk") - if vector.distance(pos, tpos) < 1 - or (not self.in_liquid - and self.touching_ground) then - return true - end - else - self.liquid_recovery_cooldown = 5 - return true - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:flop", function(self) - local function func(self) - if self.in_liquid then - return true - end - if not self:get_action() then - creatura.action_idle(self, 0.1, "flop") - end - self:set_vertical_velocity(0) - self:set_gravity(-9.8) - end - self:set_utility(func) -end) - --- Player Interaction - -creatura.register_utility("animalia:flee_from_player", function(self, player, range) - range = range or self.tracking_range - local function func(self) - local target_alive, line_of_sight, tpos = self:get_target(player) - if not target_alive then return true end - local pos = self.object:get_pos() - local dir = vec_dir(pos, tpos) - local escape_pos = vec_add(pos, vec_multi(vec_add(dir, {x = random(-10, 10) * 0.1, y = 0, z = random(-10, 10) * 0.1}), -3)) - if not self:get_action() then - escape_pos = get_ground_level(escape_pos, 1) - if self.lasso_pos - and vec_dist(pos, self.lasso_pos) > 10 then - escape_pos = self.lasso_pos - end - creatura.action_walk(self, escape_pos, 2, "creatura:obstacle_avoidance", 1, "run") - end - if vec_dist(pos, tpos) > range then - return true - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:boid_flee_from_player", function(self, player, group) - local mobs_in_group = animalia.get_group(self) - if group then - if #mobs_in_group > 0 then - for i = 1, #mobs_in_group do - local mob = mobs_in_group[i] - mob:get_luaentity():initiate_utility("animalia:boid_flee_from_player", mob:get_luaentity(), player) - mob:get_luaentity():set_utility_score(1) - end - end - end - local function func(self) - local target_alive, line_of_sight, tpos = self:get_target(player) - if not target_alive then return true end - local pos = self.object:get_pos() - local dir = vec_dir(pos, tpos) - local escape_pos = vec_add(pos, vec_multi(vec_add(dir, {x = random(-10, 10) * 0.1, y = 0, z = random(-10, 10) * 0.1}), -3)) - if not self:get_action() then - escape_pos = get_ground_level(escape_pos, 1) - if self.lasso_pos - and vec_dist(pos, self.lasso_pos) > 10 then - escape_pos = self.lasso_pos - end - animalia.action_boid_walk(self, escape_pos, 6, "creatura:obstacle_avoidance", 1, "run") - end - if vec_dist(pos, tpos) > self.tracking_range + (#mobs_in_group or 0) then - return true - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:flee_to_water", function(self) - local function func(self) - local pos = self.object:get_pos() - local water = minetest.find_nodes_in_area_under_air(vec_sub(pos, 3), vec_add(pos, 3), {"default:water_source"}) - if water[1] - and vec_dist(pos, water[1]) < 0.5 then - return true - end - if water[1] - and not self:get_action() then - creatura.action_walk(self, water[1]) - else - return true - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:follow_player", function(self, player, force) - local function func(self) - local player_alive, line_of_sight, tpos = self:get_target(player) - -- Return if player is dead, not holding food, or behavior isn't forced - if not player_alive - or (not self:follow_wielded_item(player) - and not force) then - return true - end - local pos = self.object:get_pos() - local dist = vec_dist(pos, tpos) - if dist > self.tracking_range then - return true - end - if not self:get_action() then - if dist > self:get_hitbox(self)[4] + 1.5 then - creatura.action_walk(self, tpos, 6, "creatura:pathfind") - else - creatura.action_idle(self, 0.1, "stand") - end - end - self.head_tracking = player - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:sporadic_flee", function(self) - local timer = 18 - self:clear_action() - if group then - local mobs_in_group = animalia.get_group(self) - if #mobs_in_group > 0 then - for i = 1, #mobs_in_group do - local mob = mobs_in_group[i] - animalia.bh_flee(mob:get_luaentity()) - end - end - end - local function func(self) - local pos = self.object:get_pos() - local random_goal = { - x = pos.x + random(-4, 4), - y = pos.y, - z = pos.z + random(-4, 4) - } - if not self:get_action() then - random_goal = get_ground_level(random_goal, 1) - local node = minetest.get_node(random_goal) - if minetest.registered_nodes[node.name].drawtype == "liquid" - or minetest.registered_nodes[node.name].walkable then - return - end - if self.lasso_pos - and vec_dist(pos, self.lasso_pos) > 10 then - random_goal = self.lasso_pos - end - self._movement_data.speed = self.speed * 1.5 - creatura.action_walk(self, random_goal, 4, "creatura:obstacle_avoidance", 1.5) - end - timer = timer - self.dtime - if timer <= 0 then - return true - end - end - self:set_utility(func) -end) - --- Mob Interaction - -creatura.register_utility("animalia:mammal_breed", function(self) - local mate = animalia.get_nearby_mate(self, self.name) - if not mate then self.breeding = false return end - local breeding_time = 0 - local function func(self) - if not creatura.is_alive(mate) then - return true - end - local pos = self:get_center_pos() - local tpos = mate:get_pos() - local dist = vec_dist(pos, tpos) - abs(self:get_hitbox(self)[4]) - if dist < 1.75 then - breeding_time = breeding_time + self.dtime - end - if breeding_time >= 2 then - if self.gender == "female" then - for i = 1, self.birth_count or 1 do - local object = minetest.add_entity(pos, self.name) - local ent = object:get_luaentity() - ent.growth_scale = 0.7 - animalia.initialize_api(ent) - animalia.protect_from_despawn(ent) - end - end - self.breeding = false - self.breeding_cooldown = 300 - self:memorize("breeding", self.breeding) - self:memorize("breeding_time", self.breeding_time) - self:memorize("breeding_cooldown", self.breeding_cooldown) - local minp = vector.subtract(pos, 1) - local maxp = vec_add(pos, 1) - animalia.particle_spawner(pos, "heart.png", "float", minp, maxp) - return true - end - if not self:get_action() then - creatura.action_walk(self, tpos) - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:horse_breed", function(self) - local mate = animalia.get_nearby_mate(self, self.name) - if not mate then self.breeding = false return end - local breeding_time = 0 - local function func(self) - if not creatura.is_alive(mate) then - return true - end - local pos = self:get_center_pos() - local tpos = mate:get_pos() - local dist = vec_dist(pos, tpos) - abs(self:get_hitbox(self)[4]) - if dist < 1.75 then - breeding_time = breeding_time + self.dtime - end - if breeding_time >= 2 then - if self.gender == "female" then - local object = minetest.add_entity(pos, self.name) - object:get_luaentity().growth_scale = 0.7 - local ent = object:get_luaentity() - local tex_no = self.texture_no - if random(2) < 2 then - tex_no = mate:get_luaentity().texture_no - end - ent:memorize("texture_no", tex_no) - ent:memorize("speed", random(mate:get_luaentity().speed, self.speed)) - ent:memorize("jump_power", random(mate:get_luaentity().jump_power, self.jump_power)) - ent:memorize("max_hp", random(mate:get_luaentity().max_hp, self.max_hp)) - ent.speed = ent:recall("speed") - ent.jump_power = ent:recall("jump_power") - ent.max_hp = ent:recall("max_hp") - animalia.initialize_api(ent) - animalia.protect_from_despawn(ent) - end - self.breeding = false - self.breeding_cooldown = 300 - self:memorize("breeding", self.breeding) - self:memorize("breeding_time", self.breeding_time) - self:memorize("breeding_cooldown", self.breeding_cooldown) - local minp = vector.subtract(pos, 1) - local maxp = vec_add(pos, 1) - animalia.particle_spawner(pos, "heart.png", "float", minp, maxp) - return true - end - if not self:get_action() then - creatura.action_walk(self, tpos) - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:bird_breed", function(self) - local mate = animalia.get_nearby_mate(self, self.name) - if not mate then self.breeding = false return end - local breeding_time = 0 - local function func(self) - if not creatura.is_alive(mate) then - return true - end - local pos = self:get_center_pos() - local tpos = mate:get_pos() - local dist = vec_dist(pos, tpos) - abs(self:get_hitbox(self)[4]) - if dist < 1.75 then - breeding_time = breeding_time + self.dtime - end - if 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", - }) - for i = 1, self.birth_count or 1 do - local object = minetest.add_entity(pos, self.name) - local ent = object:get_luaentity() - ent.growth_scale = 0.7 - animalia.initialize_api(ent) - animalia.protect_from_despawn(ent) - end - end - self.breeding = false - self.breeding_cooldown = 300 - self:memorize("breeding", self.breeding) - self:memorize("breeding_time", self.breeding_time) - self:memorize("breeding_cooldown", self.breeding_cooldown) - local minp = vector.subtract(pos, 1) - local maxp = vec_add(pos, 1) - animalia.particle_spawner(pos, "heart.png", "float", minp, maxp) - return true - end - if not self:get_action() then - creatura.action_walk(self, tpos) - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:attack", function(self, target, group) - local punch_init = false - if group then - local allies = creatura.get_nearby_entities(self, self.name) - if #allies > 0 then - for i = 1, #allies do - allies[i]:get_luaentity():initiate_utility("animalia:attack", allies[i]:get_luaentity(), target) - allies[i]:get_luaentity():set_utility_score(1) - end - end - end - local function func(self) - local target_alive, line_of_sight, tpos = self:get_target(target) - if not target_alive then - return true - end - local pos = self.object:get_pos() - local dist = vec_dist(pos, tpos) - if not self:get_action() then - if punch_init then return true end - --if dist > self:get_hitbox(self)[4] then - creatura.action_walk(self, tpos, 6, "creatura:theta_pathfind", 1) - --end - end - if dist <= self:get_hitbox(self)[4] + 1 - and not punch_init then - animalia.action_punch(self, target) - punch_init = true - end - end - self:set_utility(func) -end) - --- Flight - -creatura.register_utility("animalia:aerial_flock", function(self, scale) - local range = ceil(8 * scale) - local function func(self) - if self:timer(2) - and self.stamina <= 0 then - local boids = creatura.get_boid_members(self.object:get_pos(), 6, self.name) - if #boids > 1 then - for i = 1, #boids do - local boid = boids[i] - local ent = boid:get_luaentity() - ent.stamina = ent:memorize("stamina", 0) - ent.is_landed = ent:memorize("is_landed", true) - end - end - end - local dist2floor = creatura.sensor_floor(self, 2, true) - local dist2ceil = creatura.sensor_ceil(self, 2, true) - if self.in_liquid then - dist2floor = 0 - dist2ceil = 2 - end - if dist2floor < 2 - and dist2ceil < 2 then - self.is_landed = true - return true - end - if not self:get_action() - or (dist2floor < 2 - or dist2ceil < 2) then - local pos = self.object:get_pos() - local pos2 = self:get_wander_pos_3d(1, range) - if dist2ceil < 2 then - pos2.y = pos.y - 1 - end - if dist2floor < 2 then - pos2.y = pos.y + 1 - end - if self.in_liquid then - pos2.y = pos.y + 2 - end - animalia.action_boid_move(self, pos2, 2) - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:aerial_swarm", function(self, scale) - local function func(self) - if self:timer(2) - and self.stamina <= 0 then - local boids = creatura.get_boid_members(self.object:get_pos(), 6, self.name) - if #boids > 1 then - for i = 1, #boids do - local boid = boids[i] - local ent = boid:get_luaentity() - ent.stamina = ent:memorize("stamina", 0) - ent.is_landed = ent:memorize("is_landed", true) - end - end - end - local dist2floor = creatura.sensor_floor(self, 2, true) - local dist2ceil = creatura.sensor_ceil(self, 2, true) - if self.in_liquid then - dist2floor = 0 - dist2ceil = 2 - end - if not self:get_action() - or (dist2floor < 2 - or dist2ceil < 2) then - local pos = self.object:get_pos() - local pos2 = self:get_wander_pos_3d(1, 3) - if dist2floor < 2 then - pos2.y = pos.y + 1 - end - if dist2ceil < 2 then - pos2.y = pos.y - 1 - end - animalia.action_boid_move(self, pos2, 2) - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:land", function(self, scale) - local function func(self) - if self.touching_ground then return true end - local _, node = creatura.sensor_floor(self, 3, true) - if node and is_liquid[node.name] then self.is_landed = false return true end - scale = scale or 1 - local width = self.width - local pos = self.object:get_pos() - local pos2 - if self:timer(1) then - local offset = random(2 * scale, 3 * scale) - if random(2) < 2 then - offset = offset * -1 - end - pos2 = { - x = pos.x + offset, - y = pos.y, - z = pos.z + offset - } - pos2.y = pos2.y - (3 * scale) - end - if not self:get_action() - and pos2 then - self:animate("fly") - creatura.action_walk(self, pos2, 2, "animalia:fly_path", 1) - end - end - self:set_utility(func) -end) - --- Swimming - -creatura.register_utility("animalia:schooling", function(self) - local pos = self.object:get_pos() - local water = minetest.find_nodes_in_area(vector.subtract(pos, 5), vector.add(pos, 5), {"group:water"}) - local function func(self) - if not self:get_action() then - if #water < 1 then return true end - local iter = random(#water) - local pos2 = water[iter] - table.remove(water, iter) - animalia.action_boid_move(self, pos2, 2, "animalia:swim_obstacle_avoidance") - end - end - self:set_utility(func) -end) - --- Resist Fall - -creatura.register_utility("animalia:resist_fall", function(self) - local function func(self) - if not self:get_action() then - animalia.action_fall(self) - end - if self.touching_ground - or self.in_liquid then - creatura.action_idle(self, "stand") - self:set_gravity(-9.8) - return true - end - end - self:set_utility(func) -end) - --- Die - -creatura.register_utility("animalia:die", function(self) - local timer = 1.5 - local init = false - local function func(self) - if not init then - self:play_sound("death") - creatura.action_fallover(self) - init = true - end - timer = timer - self.dtime - if timer <= 0 then - local pos = self.object:get_pos() - minetest.add_particlespawner({ - amount = 8, - time = 0.25, - minpos = {x = pos.x - 0.1, y = pos.y, z = pos.z - 0.1}, - maxpos = {x = pos.x + 0.1, y = pos.y + 0.1, z = pos.z + 0.1}, - minacc = {x = 0, y = 2, z = 0}, - maxacc = {x = 0, y = 3, z = 0}, - minvel = {x = random(-1, 1), y = -0.25, z = random(-1, 1)}, - maxvel = {x = random(-2, 2), y = -0.25, z = random(-2, 2)}, - minexptime = 0.75, - maxexptime = 1, - minsize = 4, - maxsize = 4, - texture = "creatura_smoke_particle.png", - animation = { - type = 'vertical_frames', - aspect_w = 4, - aspect_h = 4, - length = 1, - }, - glow = 1 - }) - creatura.drop_items(self) - self.object:remove() - end - end - self:set_utility(func) -end) - --- Cat Exclusive Behaviors - -creatura.register_utility("animalia:find_and_break_glass_vessels", function(self) - local timer = 12 - local pos = self.object:get_pos() - local pos2 = nil - local nodes = minetest.find_nodes_in_area( - vector.subtract(pos, 8), - vec_add(pos, 8), - {"vessels:glass_bottle", "vessels:drinking_glass"} - ) - if #nodes > 0 then - pos2 = nodes[1] - end - local func = function(self) - if not pos2 then - return - end - pos = self.object:get_pos() - if not self:get_action() then - creatura.action_walk(self, pos2, 6, "pathfind") - end - if vector.distance(pos, pos2) <= 0.5 then - creatura.action_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 - timer = timer - self.dtime - if timer < 0 then return true end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:walk_ahead_of_player", function(self, player) - if not player then return end - local timer = 8 - local func = function(self) - if not creatura.is_alive(player) then - return true - end - local pos = self.object:get_pos() - 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 = self:memorize("status", "following") - local dist = vec_dist(pos, tpos) - if dist > self.view_range then - self.status = self:memorize("status", "") - return true - end - if not self:get_action() then - if vec_dist(pos, tpos) > self.width + 0.5 then - creatura.action_walk(self, tpos, 6, "pathfind", 0.75) - else - creatura.action_idle(self, 0.1, "stand") - end - end - timer = timer - self.dtime - if timer < 0 then self.status = self:memorize("status", "") return true end - end - self:set_utility(func) -end) - --- Bat Exclusive Behaviors - -creatura.register_utility("animalia:return_to_home", function(self) - local init = false - local tpos = nil - local function func(self) - if not self.home_position then return true end - local pos = self.object:get_pos() - local pos2 = self.home_position - if not self:get_action() then - creatura.action_walk(self, vec_raise(pos2, -1), 6, "animalia:fly_path", 1) - end - local dist = vec_dist(pos, pos2) - if dist < 2 then - if is_solid[minetest.get_node(vec_raise(pos, 1)).name] then - creatura.action_idle(self, 1, "latch") - self:set_gravity(9.8) - self.object:set_velocity({x = 0, y = 0, z = 0}) - end - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:find_home", function(self) - local init = false - local tpos = nil - local pos = self.object:get_pos() - local range = self.tracking_range - local ceiling = get_ceiling_positions(pos, range / 2) - local iter = 1 - local function func(self) - if not ceiling[1] then - return true - else - iter = random(#ceiling) - end - pos = self.object:get_pos() - if not self:get_action() then - local pos2 = get_wander_pos_3d(self, range) - local dist2floor = creatura.sensor_floor(self, 5, true) - local dist2ceil = creatura.sensor_ceil(self, 5, true) - if dist2floor < 4 then - pos2.y = pos.y + 2 - elseif dist2ceil < 4 then - pos2.y = pos.y - 1 - end - animalia.action_boid_move(self, pos2, 2) - end - if ceiling[iter] then - local pos2 = { - x = ceiling[iter].x, - y = ceiling[iter].y - 1, - z = ceiling[iter].z - } - local line_of_sight = fast_ray_sight(pos, pos2) - if line_of_sight then - self.home_position = self:memorize("home_position", ceiling[iter]) - return true - end - end - if self:timer(1) then - iter = iter + 1 - if iter > #ceiling then - return true - end - end - end - self:set_utility(func) -end) - --- Horse Exclusive Behaviors - -creatura.register_utility("animalia:horse_breaking", function(self) - local timer = 18 - self:clear_action() - local function func(self) - if not self:get_action() then - animalia.action_horse_spin(self, random(4, 6), "stand") - end - timer = timer - self.dtime - if timer <= 0 then - return true - end - end - self:set_utility(func) -end) - --- Tamed Animal Orders - -creatura.register_utility("animalia:sit", function(self) - local function func(self) - if self.order ~= "sit" then - return true - end - if not self:get_action() then - creatura.action_idle(self, 0.1, "sit") - end - end - self:set_utility(func) -end) - -creatura.register_utility("animalia:mount", function(self, player) - local function func(self) - if not creatura.is_alive(player) then - return true - end - local anim = "stand" - local control = player:get_player_control() - local speed_factor = 0 - local vel = self.object:get_velocity() - if control.up then - speed_factor = 1 - if control.aux1 then - speed_factor = 1.5 - end - end - if control.jump - and self.touching_ground then - self.object:add_velocity({ - x = 0, - y = self.jump_power + (abs(self._movement_data.gravity) * 0.33), - z = 0 - }) - elseif not self.touching_ground then - speed_factor = speed_factor * 0.5 - end - local total_speed = vector.length(vel) - if total_speed > 0.2 then - anim = "walk" - if control.aux1 then - anim = "run" - end - if not self.touching_ground - and not self.in_liquid - and vel.y > 0 then - anim = "rear_constant" - end - end - self:turn_to(player:get_look_horizontal()) - self:set_forward_velocity(self.speed * speed_factor) - self:animate(anim) - if control.sneak - or not self.rider then - animalia.mount(self, player) - return true - end - end - self:set_utility(func) -end) \ No newline at end of file diff --git a/mobs/wolf.lua b/mobs/wolf.lua index c887a0a..c2b40bf 100644 --- a/mobs/wolf.lua +++ b/mobs/wolf.lua @@ -101,6 +101,7 @@ creatura.register_mob("animalia:wolf", { end if target then if is_attacking + and self._utility_data.args[2] and self._utility_data.args[2] == target then return 0 end diff --git a/textures/animalia_shears.png b/textures/animalia_shears.png deleted file mode 100644 index 6f1eade1758548de9a3ca5fa4a98c403d13e11bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5360 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 A%>V!Z diff --git a/textures/items/animalia_cat_toy.png b/textures/items/animalia_cat_toy.png index 8e382b4820bbdd57985b728c9fc306ade51dbcbb..3926a2a41e15185aa89821b5aafd50d756127c35 100644 GIT binary patch delta 1931 zcmV;62Xy$PFoiOZBmsYsB|d*PvJ4`n6vF2C@853z!Nug%I3G2n5G*d2T;dEt?Yi=O zl6CXBU+gK|>CN?lp%V16UN!9Tr=IMcCCZTejUlc3L%IKu+_tX|hW0`-5HW4~n}jEBT&`!e$Q?joYsJe0zG2#Lj$#VvmTGHy1Kr;u@b z3|z8DKc2dgARy1;WN%QJM^-M0KG8prB4Ozb6!*alSN~K`{(11t4Drp%Qm>Wo^h`EB zx^v#WH#NN<%e(9XLK?PHrz=y!z=Dv=oEFd`ZVx%mmeAB7dn?#!w%USLDG`H~PL3L7 z>U7M$QUyfE2S@bGm1TeGR3ay?VhzZ2qfzH5Nt!5ltDsk)*(Tyws>!Ai_(K2x^jKaOF_gkU!BWawvQ8&U+tx^y-t(L4pfDgb>3hemP2X z(Z>*D)Rff#Y~3AHXK2bl;l+GSUX=QM+WjnD{#jG3@0iwP1W& z&I#e*pE#RCW#FaF^$2!~_*eL{H-8jzl$``j2?zLgRP{#$nzHkk=cJ)8IGEPhW`B;& zJo^A_y0I~;1s&Vv^qD0oyy@3^e-!S^8T@k8y42wz(cj+vw>u@))b?)X8)W8$c7v?0 z1V~!5oCSdD&cha>yubUquiJfc4|T?TTOYnI4_6S(Ul!L7Q8N!=yN}ijJeoI<+*LaB zq11g-!C22teP1KY&x*utLA$9HIc;t5rk%`wQ9thLg>K1MJW{rJE%BhsDfZYtcasju?QAQTcK44lS{v# zNkfw2;wZQl9Q;_UI=DFN>fkB}f*&A`PELw0QsV!TLW>v=j{EWM-sA2aAT%mWHM=GO zRkMs#JSJvyt76Y91Pmd7aYQ9%>WTDX2A<>V9zMR_MR=C?xj#prk~bOP6N%@4m~L3a z8^kl4md<&fIK)blLVQjM%IyEplHM4aNX$XHfX*>S_000JJ zOGiWia{xX77wQ*mJpcdz32;bRa{vGf6951U69E94oEQKA00(qQO+^Rh0vQT2I|}q6 z$p8QV_en%SR5;76lfe$bKoExiZAgWKa6%l3#K9A|i8yg_^AsM!)ob+DOM7a1^$Zf> z0i1}2q^s6A$TroMt}uV6%})Mr{+XQxehl_5G3eC^0OpG&3c8NrXv&lUgI=AgpinAgVI1OOq!6r5Rh3N0EVL}vn_)XfNLO>-(R}WqV`Yeiv^N%$VKfRy=?pO zeF$6N3ie&cq-LY}au))GV*8a!H3{H1QmWo=b+FxS(*H%jTca$1>zIUp#{-;6bE{`> R+|2+0002ovPDHLkV1kdqhkyV8 delta 1844 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 KMNUMnLSTYBfeRM^ diff --git a/textures/items/animalia_feather.png b/textures/items/animalia_feather.png index 1692f30ad9179b8d3bcdce6dca86ec28eb11fead..9c8d2bf6a0eb064facf106538b054b4c731c30dd 100644 GIT binary patch delta 1950 zcmV;P2VwZaDwr&gB!5VHR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3004N}?U!A4^C%34 z?^(qx0Z9nNa`-Xl%noMx^8${Wq-jt4wRbc2u~B3!2|WTFcl`7BaDU-o^6Y{pi#bM* zBjk{|Krp|LvhL)=x}QJ%O?c3|>j6V0Xl4Jjc=o^P$v;@40)I*G3|TuL)W-)ooc8U> z(CJ8KL=2&yhVp4hct4=iK-t6cFQrdZvD!urAqGA;p=E9?OJ|Zea})0XGt83)C&|*qK&%0;K+9Jw zwEX7fu93lUC-h{3G1~a~cg_zDjWJ7}$lH}G#Kp5v5r4)cH}B{LAT(}uq$|O<^ZA%u z1q5}a*)YK3>o!r9&f1!FBnz~lqoUo@f>J*XAVu)42!;d%d?ZndsGk%JHB?kIsA|-trddmr z7^21)Q-7R`UrCZe(iBrlmU1d47ECQ#GIN%#WX^)cQFF{GTh6%@I4!VT@OD9mQYu%e zp=ymaRjav{hVp5lX^Sm2b1k>hxl6}7(PK~DdhX?*1Eq9?Lyvf*!;XBEfl?bX!q5>% z8aDDM7d3Jl>$~&o)aXWy7pd7XUDWVuN;eZUmwywT%s`9-fq0q(P|!S?d7}{6O>~o) z&x~22j3{-&=`@LfU^s|%(8cbJ+ygfk!jHJ|jhvh4euJEw=sqC#p4$s*eeO2Ri(7=| zG@MEGg6W42hZVLmH?=Rf?z=DiZ|H0XxGu5z0~2D&9tg)0D;9Tqe#QMlFx${;=he1v zEPsGvY0H>Ep9JN85X3_a?qv`Z*N>nyC&zp%_|7vICkvP-tNBc2AZ9-WrH+R9P4 zSUslVExPWG?WdWi&^?| z_*UF!?PC}DQFgA@UFiz-mMwgco%<17o?+!n^+6Wg_X_9Zf^x6ZqUS`2sa3uCW&6aO z`<-}{-CI0wga2X>53SSRNz#AO5dN<8?EVGH*}kThO&$`Hfdn9bQ$;Bi2Ro=ZWT=8* zL9B?QRIvyaN?V~-2a`*`ph-iL;^HW{799LotU9+0Yt2!bCVj!sUBE>hzEl0u6Z z503ls?%w0>9UwF+Of|bE0ade%R6HhTbE{&{D+CN7fN?}6X6lLbVg{b$>mEM7-bHwp z_qjhupOQBj;1h{|=a_C-#2dsjo0iUbpE$%yl0tk=JYmoUi66NxyZpwv=&-;uLq;Yw zPaGl^iybU?Fe@1<@icK*Q8mgJvMwu}w>Ybn8f)E?zc85BR?=LjIf6KrkU$b5WYkbb z1r{Q-YNVJ*(SFRsKkWFEw3Vu9k0rgF|4fNZIQi@9ypF?cX!4{(b<_TXN0!X}BB!01nn^ zR9JLaO-wqo9tLg!le`BilZ*#2ljaAzBV%P|Wj8c2Eiy4NV=Xi_G%+n=V>vP{Wi>Z6 zF=01lH8N&5lf(#6lPw894m344I5smiI5sjjH844odI=ws6bTu#nF$yIlk*8&BsgI+ zVq-ZpI4xy2F=j0^H!?9TI5uK1Ei^PVH)3UFF*0N@WRr6WM-VbCFfuJNHB>M%IyEvn zGdQ!U3up)yvqiHf00006VoOIv0CNC702k^PY(0}F6dr%(0vQT4CO@d^mH+?%-bqA3 zR5;7k(xGp{KorOEZ?|S6T-ZXEARr_&wM_-Ct{OYN>HR@ys%z2ba0s%@keZSPM1vS@ zuJ)#A3e8IE*t73Gzjya?@GnaSzLtTn0nD}#z;Qj+OG)E^GE8Ee4}; zG1ams#TkF24qrmY^-x(#__e}&dpG_Jkb$qu?>s@39@$4>fdTI8u;Y64`Y-$(5*{8q zY&YL$xUz#woa|!6(=+=h1gPLjK&}+)rNpu)s4T68l_f6bGu;(}Tq(LjT!m)=Wjy`( kG}?TwyYlth;@hZS-cfO#g@8~500000NkvXXu0jG}f_SW(asU7T delta 1922 zcmV-|2YvXMEW#>~B!5(TR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3004N}?N(Wq#3l^< z=M*^tk^sSRFw1+nL5`ndv8T6LGs#y>+oA=QM3oRW?XN$(`2!c*5RCI0Lkhv-a>*sm z=tR4&JfCFUeC|K??A+;fPSdr=oUql1YDAeZ@CKvUcveVr|#MT6`uV5iz*541vw z7_@Y9R4G%ZHf5oL5FIyGNag~fhAxV7;v&|7Og9>JR!LI97fS)X0xe&$(Da+8yFw9; zD`6%RjM3oQOu~Pmy=$oVk@7?~URWV69*K%D26N+yS$`me`k6<50(`c|Yw#*y&`+2n z7MSg^OdOteZjC&WNwi?1hg{o&v~B}{2);dnAprw6ka!AYm05{^V+21Lh9Wi%AE1D| z=ZYxVi3E@ndHBXVN^>rIUTV}V5aA_=f|?{5TsagrfT}QnYS0L)p6PsD`qk@ zw&4KP3btS8n8z<;Z#a+NI8Hv5=BpF`H}o)|C?~V)Qn+-btu8YAUyBHi&9i5B%pNKy za({p10Zy%+YMV)saU{*o_~5yf=27vv>{*skoM)%5WwUfan5OqQ%JYcGQb~38q60jA z!^7{b&U|6{&#U*kR!_+;XE?G;U6J4$ZAo$YjMuGj9ZOSP+a@OaFn>$)sIUN_ub13$ z=}-eE?D1N7I_^U^Hk3V9a2(7~obn1Cdw;phF@yIGRcFn!FQs`?01I6&u|HoT)RoyQ zA;<~GNmY%TpMjKFV|j;w-06Gt>%mNEJ_v?)F!mw@C-90Upq5W)n0JzxXpi)hO#UFV zZ!@-~1w6j zdfGcM^S8J0kY=G{Pjq6nXL2MuiD?f@d9{3Fzk$OmdBR~P9(g+#d>5SCB+|DqH@^_f zjOAJyHLIT$uaBhpS*_nr@d2aYdv1}RM(`^U{r85n-f5dhp%@rp5=b-&(SLv3-*TUS|@<-8MsnA z{#p~5`6Rv3(V|B{-!^b@-O;2y;Bp5Tc+x~&v?U)+Z?OoxpV2qvf&N>dXU*xYy^qre zAWK~>-v9@Pz-WoG*InKn>hA5|GwuF<0FYsFvr;Mv(f|Mr_i0pEbXZMHI2P%`=2fHIRHDzTrWH&82WMVWeG&W>8EjcqXH!U$RIAk|DHDNb7IA)W$2vC!B z2^o_J2|o@pI59LZFgP$YH8wOfGLuyaAG3`K7y`3|3}6Tr%y8xy00006VoOIv0P6tX z0NALSWm=Q^6dr%&2n7WQ07K!^fdBvi+et)0R5;7k(ypj8(F4Eh${ zX3fU|85|AAB=@AyYt@1Ka1Vf?8~Got3J>}g0P3s&FnfL`$vwf_teH&b+RFgfvD49T zoKMVP9N+K*KgR&gSCgY-gxNC@LU6ra5Tea?w=Lk(4l=CH3PU$IsUuQ$@B<&9gi8Up zILKwuYQp&fm5b%s=^MzLDxEk5x8~hvf01)wR-Izx(?*IS*07*qo IM6N<$g7Zm*F8}}l diff --git a/textures/items/animalia_porkchop_raw.png b/textures/items/animalia_porkchop_raw.png index fd3afee785628a1579c11e3e9d00c2bfe207be5a..6510b6a0f900d4d2c990b7a67dbe741a5d8f8576 100644 GIT binary patch delta 2016 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 diff --git a/textures/items/animalia_saddle.png b/textures/items/animalia_saddle.png index 178cfc35d4bc2dec83dcf6e98b8776ad0ace33fa..092a55dac9a80a7e6d3decfe2852ddb05bc6f568 100644 GIT binary patch literal 5298 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 N;<EX>4Tx04R}tkv&MmKpe$i)7FYq9PA+C zkfAzR5S8L6RV;#q(pG5I!Q|2}XktiGTpR`0f`cE6RRJ9VO+2?W?X{Cqrqmx;h|?yKovo>ZdVZu%JUMOjo9_kwh2GXN&xO(3c8sx&6SS| zmZkEVEAw1%Sws~?8-fH^z};E`(D%xFA-v^ECuI}7Q&eg=P2k$@AW8b(;xkYSQ9%UC zC;(iAnb|5xS;<%Z9Za#Qc{1qyu$56Zs}0_o^I>1dqk%SGrdjWuHi`YMB!sa4Bq0Qy eOeXrLzwr|hYLFOr%Bo5L0000