Harvest Update
							
								
								
									
										15
									
								
								.luacheckrc
									
									
									
									
									
								
							
							
						
						| @ -1,15 +0,0 @@ | ||||
| max_line_length = 120 | ||||
| 
 | ||||
| globals = { | ||||
| 	"minetest", | ||||
| 	"animalia", | ||||
| 	"creatura", | ||||
| } | ||||
| 
 | ||||
| read_globals = { | ||||
| 	"vector", | ||||
| 	"ItemStack", | ||||
| 	table = {fields = {"copy"}} | ||||
| } | ||||
| 
 | ||||
| ignore = {"212/self", "212/this"} | ||||
							
								
								
									
										33
									
								
								api/api.lua
									
									
									
									
									
								
							
							
						
						| @ -315,6 +315,22 @@ end | ||||
| -- Mobs -- | ||||
| ---------- | ||||
| 
 | ||||
| function animalia.get_dropped_food(self, item) | ||||
| 	local pos = self.object:get_pos() | ||||
| 	if not pos then return end | ||||
| 	local objects = minetest.get_objects_inside_radius(pos, self.tracking_range) | ||||
| 	for _, object in ipairs(objects) do | ||||
| 		local ent = object:get_luaentity() | ||||
| 		if ent | ||||
| 		and ent.name == "__builtin:item" | ||||
| 		and ent.itemstring | ||||
| 		and ((item and ent.itemstring:match(item)) | ||||
| 		or self:follow_item(ItemStack(ent.itemstring))) then | ||||
| 			return object, object:get_pos() | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| function animalia.protect_from_despawn(self) | ||||
| 	self._despawn = self:memorize("_despawn", false) | ||||
| 	self.despawn_after = self:memorize("despawn_after", false) | ||||
| @ -334,7 +350,7 @@ function animalia.set_nametag(self, clicker) | ||||
| 		return | ||||
| 	end | ||||
| 	self.nametag = self:memorize("nametag", name) | ||||
| 	self.despawn_after = self:memorize("despawn_after", nil) | ||||
| 	self.despawn_after = self:memorize("despawn_after", false) | ||||
| 	activate_nametag(self) | ||||
| 	if not minetest.is_creative_enabled(plyr_name) then | ||||
| 		item:take_item() | ||||
| @ -343,7 +359,6 @@ function animalia.set_nametag(self, clicker) | ||||
| 	return true | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function animalia.initialize_api(self) | ||||
| 	self.gender = self:recall("gender") or nil | ||||
| 	if not self.gender then | ||||
| @ -448,8 +463,9 @@ function animalia.feed(self, clicker, breed, tame) | ||||
| 	local item, item_name = self:follow_wielded_item(clicker) | ||||
| 	if item_name then | ||||
| 		-- Eat Animation | ||||
| 		local offset_h = self.head_data.pivot_h or 0.5 | ||||
| 		local offset_v = self.head_data.pivot_v or 0.5 | ||||
| 		local head = self.head_data | ||||
| 		local offset_h = (head and head.pivot_h) or 0.5 | ||||
| 		local offset_v = (head and head.pivot_v) or 0.5 | ||||
| 		local head_pos = { | ||||
| 			x = pos.x + sin(yaw) * -offset_h, | ||||
| 			y = pos.y + offset_v, | ||||
| @ -546,6 +562,15 @@ function animalia.mount(self, player, params) | ||||
| 	end) | ||||
| end | ||||
| 
 | ||||
| function animalia.punch(self, puncher, ...) | ||||
| 	creatura.basic_punch_func(self, puncher, ...) | ||||
| 	self._puncher = puncher | ||||
| 	if self.flee_puncher | ||||
| 	and (self:get_utility() or "") ~= "animalia:flee_from_target" then | ||||
| 		self:clear_utility() | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| -------------- | ||||
| -- Spawning -- | ||||
| -------------- | ||||
|  | ||||
| @ -36,6 +36,7 @@ end | ||||
| 
 | ||||
| local vec_dir = vector.direction | ||||
| local vec_dist = vector.distance | ||||
| local vec_divide = vector.divide | ||||
| local vec_len = vector.length | ||||
| local vec_normal = vector.normalize | ||||
| local vec_round = vector.round | ||||
| @ -50,6 +51,22 @@ local yaw2dir = minetest.yaw_to_dir | ||||
| -- Local Tools -- | ||||
| ----------------- | ||||
| 
 | ||||
| local farming_enabled = minetest.get_modpath("farming") and farming.registered_plants | ||||
| 
 | ||||
| if farming_enabled then | ||||
| 	minetest.register_on_mods_loaded(function() | ||||
| 		for name, def in pairs(minetest.registered_nodes) do | ||||
| 			local item_string = name:sub(1, #name - 2) | ||||
| 			local item_name = item_string:split(":")[2] | ||||
| 			if farming.registered_plants[item_string] | ||||
| 			or farming.registered_plants[item_name] then | ||||
| 				def.groups.crop = 2 | ||||
| 			end | ||||
| 			minetest.register_node(":" .. name, def) | ||||
| 		end | ||||
| 	end) | ||||
| end | ||||
| 
 | ||||
| local animate_player = {} | ||||
| 
 | ||||
| if minetest.get_modpath("default") | ||||
| @ -108,6 +125,38 @@ local function add_break_particle(pos) | ||||
| 	}) | ||||
| end | ||||
| 
 | ||||
| local function add_eat_particle(self, item_name) | ||||
| 	local pos, yaw = self.object:get_pos(), self.object:get_yaw() | ||||
| 	if not pos then return end | ||||
| 	local head = self.head_data | ||||
| 	local offset_h = (head and head.pivot_h) or self.width | ||||
| 	local offset_v = (head and head.pivot_v) or self.height | ||||
| 	local head_pos = { | ||||
| 		x = pos.x + sin(yaw) * -offset_h, | ||||
| 		y = pos.y + offset_v, | ||||
| 		z = pos.z + cos(yaw) * offset_h | ||||
| 	} | ||||
| 	local def = minetest.registered_items[item_name] | ||||
| 	local image = def.inventory_image | ||||
| 	if def.tiles then | ||||
| 		image = def.tiles[1].name or def.tiles[1] | ||||
| 	end | ||||
| 	if image then | ||||
| 		local crop = "^[sheet:4x4:" .. random(4) .. "," .. random(4) | ||||
| 		minetest.add_particlespawner({ | ||||
| 			pos = head_pos, | ||||
| 			time = 0.5, | ||||
| 			amount = 12, | ||||
| 			collisiondetection = true, | ||||
| 			collision_removal = true, | ||||
| 			vel = {min = {x = -1, y = 1, z = -1}, max = {x = 1, y = 2, z = 1}}, | ||||
| 			acc = {x = 0, y = -9.8, z = 0}, | ||||
| 			size = {min = 1, max = 2}, | ||||
| 			texture = image .. crop | ||||
| 		}) | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| local function get_group_positions(self) | ||||
|     local objects = creatura.get_nearby_objects(self, self.name) | ||||
|     local group = {} | ||||
| @ -118,6 +167,11 @@ local function get_group_positions(self) | ||||
|     return group | ||||
| end | ||||
| 
 | ||||
| local function reset_attack_vals(self) | ||||
| 	self.punch_cooldown = 0 | ||||
| 	self.target = nil | ||||
| end | ||||
| 
 | ||||
| -------------- | ||||
| -- Movement -- | ||||
| -------------- | ||||
| @ -157,46 +211,32 @@ creatura.register_movement_method("animalia:fly_simple", function(self) | ||||
| end) | ||||
| 
 | ||||
| creatura.register_movement_method("animalia:fly_obstacle_avoidance", function(self) | ||||
| 	local box = clamp(self.width, 0.5, 1.5) | ||||
| 	local steer_to | ||||
| 	local steer_timer = 0.25 | ||||
| 	local init_dist | ||||
| 	self:set_gravity(0) | ||||
| 	local function func(_self, goal, speed_factor) | ||||
| 		local pos = _self.object:get_pos() | ||||
| 		if not pos then return end | ||||
| 		-- Return true when goal is reached | ||||
| 		if vec_dist(pos, goal) < box * 1.33 then | ||||
| 		local dist = vec_dist(pos, goal) | ||||
| 		init_dist = dist | ||||
| 		if dist < clamp(self.width, 0.5, 1) + 1.5 then | ||||
| 			_self:halt() | ||||
| 			return true | ||||
| 		end | ||||
| 		steer_timer = steer_timer - self.dtime | ||||
| 		if steer_timer <= 0 then | ||||
| 			steer_to = get_avoidance_dir(_self) | ||||
| 		end | ||||
| 		-- Get movement direction | ||||
| 		local goal_dir = vec_dir(pos, goal) | ||||
| 		if steer_to then | ||||
| 			steer_to.y = goal_dir.y | ||||
| 			goal_dir = steer_to | ||||
| 		end | ||||
| 		local yaw = _self.object:get_yaw() | ||||
| 		local goal_yaw = dir2yaw(goal_dir) | ||||
| 		-- Calculate Movement | ||||
| 		steer_timer = (steer_timer > 0 and steer_timer - self.dtime) or 0.25 | ||||
| 		steer_to = (steer_timer <= 0 and creatura.get_context_steering(self, goal, 4, true)) or steer_to | ||||
| 		local speed = abs(_self.speed or 2) * speed_factor or 0.5 | ||||
| 		local turn_rate = abs(_self.turn_rate or 5) | ||||
| 		-- Movement | ||||
| 		local yaw_diff = abs(diff(yaw, goal_yaw)) | ||||
| 		if yaw_diff < pi * 0.25 | ||||
| 		or steer_to then | ||||
| 			_self:set_forward_velocity(speed) | ||||
| 		else | ||||
| 			_self:set_forward_velocity(speed * 0.33) | ||||
| 		end | ||||
| 		self:set_vertical_velocity(speed * goal_dir.y) | ||||
| 		_self:turn_to(goal_yaw, turn_rate) | ||||
| 		if _self.touching_ground | ||||
| 		or _self.in_liquid then | ||||
| 			_self.object:add_velocity({x = 0, y = 2, z = 0}) | ||||
| 		end | ||||
| 		local dist_weight = (init_dist - dist) / init_dist | ||||
| 		-- Apply Movement | ||||
| 		local dir = (steer_to or vec_dir(pos, goal)) | ||||
| 		speed = speed - (speed * 0.75) * dist_weight | ||||
| 		turn_rate = turn_rate + turn_rate * dist_weight | ||||
| 		_self:set_forward_velocity(speed) | ||||
| 		_self:set_vertical_velocity(speed * dir.y) | ||||
| 		_self:turn_to(dir2yaw(dir), turn_rate) | ||||
| 	end | ||||
| 	return func | ||||
| end) | ||||
| @ -223,7 +263,7 @@ creatura.register_movement_method("animalia:swim_simple", function(self) | ||||
| 		if yaw_diff < pi * 0.25 then | ||||
| 			_self:set_forward_velocity(speed) | ||||
| 		else | ||||
| 			_self:set_forward_velocity(speed * 0.33) | ||||
| 			_self:set_forward_velocity(speed * 0.66) | ||||
| 		end | ||||
| 		self:set_vertical_velocity(speed * goal_dir.y) | ||||
| 		_self:turn_to(goal_yaw, turn_rate) | ||||
| @ -280,7 +320,7 @@ function animalia.action_pursue(self, target, timeout, method, speed_factor, ani | ||||
| 	local timer = timeout or 4 | ||||
| 	local goal | ||||
| 	local function func(_self) | ||||
| 		local target_alive, line_of_sight, tgt_pos = self:get_target(target) | ||||
| 		local target_alive, line_of_sight, tgt_pos = _self:get_target(target) | ||||
| 		if not target_alive then | ||||
| 			return true | ||||
| 		end | ||||
| @ -307,6 +347,92 @@ function animalia.action_pursue(self, target, timeout, method, speed_factor, ani | ||||
| 	self:set_action(func) | ||||
| end | ||||
| 
 | ||||
| function animalia.action_pursue_glide(self, target, timeout, method, speed_factor, anim) | ||||
| 	local timer = timeout or 4 | ||||
| 	local goal | ||||
| 	local speed_x = speed_factor | ||||
| 	local function func(_self) | ||||
| 		local target_alive, line_of_sight, tgt_pos = _self:get_target(target) | ||||
| 		if not target_alive then | ||||
| 			return true | ||||
| 		end | ||||
| 		goal = goal or tgt_pos | ||||
| 		timer = timer - _self.dtime | ||||
| 		self:animate(anim or "walk") | ||||
| 		if line_of_sight | ||||
| 		and vec_dist(goal, tgt_pos) > 3 then | ||||
| 			goal = tgt_pos | ||||
| 		end | ||||
| 		local vel = self.object:get_velocity() | ||||
| 		if vel.y < 0 and speed_x < speed_factor + 0.5 then speed_x = speed_x + self.dtime * 0.5 end | ||||
| 		if vel.y >= 0 and speed_x > speed_factor then speed_x = speed_x - self.dtime * 0.25 end | ||||
| 		if timer <= 0 | ||||
| 		or _self:move_to(goal, method or "animalia:fly_obstacle_avoidance", speed_x) then | ||||
| 			return true | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_action(func) | ||||
| end | ||||
| 
 | ||||
| function animalia.action_flight_attack(self, target, timeout) | ||||
| 	local anim = self.animations["fly_punch"] | ||||
| 	local anim_len = (anim.range.y - anim.range.x) / anim.speed | ||||
| 	local anim_time = 0 | ||||
| 	local timer = timeout or 12 | ||||
| 	local cooldown = 0 | ||||
| 	local speed_x = 0.5 | ||||
| 	local goal | ||||
| 	local function func(_self) | ||||
| 		local pos = _self.stand_pos | ||||
| 		if timer <= 0 then return true end | ||||
| 		local target_alive, los, tgt_pos = _self:get_target(target) | ||||
| 		if not target_alive then return true end | ||||
| 		local dist = vec_dist(pos, tgt_pos) | ||||
| 
 | ||||
| 		if dist > 32 then return true end | ||||
| 
 | ||||
| 		local vel = self.object:get_velocity() | ||||
| 		if vel.y < 0 and speed_x < 1 then speed_x = speed_x + self.dtime * 0.5 end | ||||
| 		if vel.y >= 0 and speed_x > 0.5 then speed_x = speed_x - self.dtime end | ||||
| 
 | ||||
| 		if anim_time > 0 then | ||||
| 			_self:animate("fly_punch") | ||||
| 			anim_time = anim_time - _self.dtime | ||||
| 		else | ||||
| 			if speed_x > 0.6 then | ||||
| 				_self:animate("glide") | ||||
| 			else | ||||
| 				_self:animate("fly") | ||||
| 			end | ||||
| 		end | ||||
| 
 | ||||
| 		if cooldown > 0 then | ||||
| 			goal = goal or _self:get_wander_pos_3d(3, 6, nil, 1) | ||||
| 			cooldown = cooldown - _self.dtime | ||||
| 		else | ||||
| 			goal = nil | ||||
| 			cooldown = 0 | ||||
| 		end | ||||
| 
 | ||||
| 		if goal | ||||
| 		and _self:move_to(goal, "animalia:fly_obstacle_avoidance", speed_x) then | ||||
| 			goal = nil | ||||
| 		end | ||||
| 		 | ||||
| 		if not goal | ||||
| 		and _self:move_to(tgt_pos, "animalia:fly_obstacle_avoidance", speed_x) then | ||||
| 			if dist < _self.width + 1 then | ||||
| 				_self:punch_target(target) | ||||
| 				cooldown = timeout / 3 | ||||
| 				anim_time = anim_len | ||||
| 			end | ||||
| 		end | ||||
| 
 | ||||
| 		timer = timer - _self.dtime | ||||
| 	end | ||||
| 	self:set_action(func) | ||||
| end | ||||
| 
 | ||||
| function animalia.action_move_flock(self, pos2, timeout, method, speed_factor, anim, boid_steer) | ||||
| 	local old_boids = (self._movement_data and self._movement_data.boids) or {} | ||||
| 	local boids = (#old_boids > 2 and old_boids) or creatura.get_boid_members(self.object:get_pos(), 12, self.name) | ||||
| @ -357,6 +483,55 @@ function animalia.action_move_flock(self, pos2, timeout, method, speed_factor, a | ||||
| 	self:set_action(func) | ||||
| end | ||||
| 
 | ||||
| function animalia.action_move_boid(self, pos2, timeout, method, speed_factor, anim, water) | ||||
| 	local timer = timeout or 4 | ||||
| 	local goal | ||||
| 	local function func(_self) | ||||
| 		local pos, vel = self.object:get_pos(), self.object:get_velocity() | ||||
| 		if not pos then return end | ||||
| 		local move_data = self._movement_data or {} | ||||
| 		-- Tick down timer | ||||
| 		timer = timer - _self.dtime | ||||
| 		-- Check if goal is safe | ||||
| 		local safe = true | ||||
| 		local max_fall = (_self.max_fall or 0) > 0 and _self.max_fall | ||||
| 		if max_fall then | ||||
| 			safe = _self:is_pos_safe(goal) | ||||
| 		end | ||||
| 		-- Boid calculation | ||||
| 		local boid_dir, boids = move_data.boid_dir or creatura.get_boid_dir(self) | ||||
| 		if boid_dir then | ||||
| 			local heading = vec_dir(pos, pos2) | ||||
| 			boid_dir.y = boid_dir.y + heading.y / 2 | ||||
| 			goal = vec_add(pos, vec_multi(boid_dir, self.width + 1)) | ||||
| 			if max_fall then | ||||
| 				goal = creatura.get_ground_level(goal, 2) | ||||
| 			end | ||||
| 			if boids then | ||||
| 				for _, obj in ipairs(boids) do | ||||
| 					local ent = obj and obj:get_luaentity() | ||||
| 					if ent then | ||||
| 						ent._movement_data.boid_dir = boid_dir | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
| 		else | ||||
| 			goal = pos2 | ||||
| 		end | ||||
| 		local steer_dir = creatura.get_context_steering(self, goal, 1, water) | ||||
| 		goal = (steer_dir and vec_add(pos, steer_dir)) or goal | ||||
| 		-- Main movement | ||||
| 		if timer <= 0 | ||||
| 		or not safe | ||||
| 		or _self:move_to(goal, method or "creatura:obstacle_avoidance", speed_factor or 0.5) then | ||||
| 			return true | ||||
| 		end | ||||
| 		self:animate(anim or "walk") | ||||
| 		self._movement_data.boid_dir = nil | ||||
| 	end | ||||
| 	self:set_action(func) | ||||
| end | ||||
| 
 | ||||
| function animalia.action_float(self, time, anim) | ||||
| 	local timer = time | ||||
| 	local function func(_self) | ||||
| @ -528,7 +703,7 @@ creatura.register_utility("animalia:swim_to_land", function(self) | ||||
| 			_self:set_gravity(-9.8) | ||||
| 			_self:set_forward_velocity(_self.speed * 0.66) | ||||
| 			_self:animate("walk") | ||||
| 			if vector.distance(pos, tpos) < 1 | ||||
| 			if vec_dist(pos, tpos) < 1 | ||||
| 			or (not _self.in_liquid | ||||
| 			and _self.touching_ground) then | ||||
| 				return true | ||||
| @ -569,7 +744,7 @@ creatura.register_utility("animalia:wander_group", function(self) | ||||
| 	local center = self.object:get_pos() | ||||
| 	if not center then return end | ||||
| 	local group_tick = 500 | ||||
| 	local move = self.wander_action or animalia.action_move_flock | ||||
| 	local move = self.wander_action or animalia.action_move_boid | ||||
| 	local function func(_self) | ||||
| 		group_tick = group_tick - 1 | ||||
| 		if group_tick <= 0 then | ||||
| @ -631,6 +806,7 @@ creatura.register_utility("animalia:aerial_wander", function(self) | ||||
| 	local center = self.object:get_pos() | ||||
| 	if not center then return end | ||||
| 	local height_tick = 0 | ||||
| 	local move = self.wander_action or creatura.action_move | ||||
| 	local function func(_self) | ||||
| 		local pos = self.object:get_pos() | ||||
| 		if not pos then return end | ||||
| @ -643,7 +819,7 @@ creatura.register_utility("animalia:aerial_wander", function(self) | ||||
| 		if not _self:get_action() then | ||||
| 			local move_dir = (vec_dist(pos, center) > 8 and vec_dir(pos, center)) or nil | ||||
| 			local pos2 = _self:get_wander_pos_3d(2, 5, move_dir) | ||||
| 			animalia.action_move_flock(_self, pos2, 3, "animalia:fly_simple", 1, "fly", true) | ||||
| 			move(_self, pos2, 3, "animalia:fly_simple", 1, "fly", true) | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| @ -652,25 +828,24 @@ end) | ||||
| creatura.register_utility("animalia:fly_to_roost", function(self) | ||||
| 	local home = self.home_position | ||||
| 	local roost = self.roost_action or creatura.action_idle | ||||
| 	local is_home = self.is_roost or function(pos, home_pos) | ||||
| 		if abs(pos.x - home_pos.x) < 0.5 | ||||
| 		and abs(pos.z - home_pos.z) < 0.5 | ||||
| 		and abs(pos.y - home_pos.y) < 0.75 then | ||||
| 			return true | ||||
| 		end | ||||
| 		return false | ||||
| 	end | ||||
| 	local function func(_self) | ||||
| 		local pos = _self.object:get_pos() | ||||
| 		if not pos then return end | ||||
| 		if not home then return true end | ||||
| 		if not _self:get_action() then | ||||
| 			if abs(pos.x - home.x) < 0.5 | ||||
| 			and abs(pos.z - home.z) < 0.5 then | ||||
| 				local y_diff = abs(pos.y - home.y) | ||||
| 				if y_diff < 0.7 then | ||||
| 					roost(_self, 1, "stand") | ||||
| 					return | ||||
| 				end | ||||
| 				if y_diff <= 2 | ||||
| 				and not minetest.line_of_sight(pos, home) then | ||||
| 					self.home_positon = nil | ||||
| 					self:forget("home_position") | ||||
| 				end | ||||
| 			if is_home(pos, home) then | ||||
| 				roost(_self, 1, "stand") | ||||
| 				return | ||||
| 			end | ||||
| 			creatura.action_move(_self, home, 3, "animalia:fly_simple", 1, "fly") | ||||
| 			creatura.action_move(_self, home, 3, "animalia:fly_obstacle_avoidance", 1, "fly") | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| @ -714,7 +889,7 @@ creatura.register_utility("animalia:aquatic_wander_school", function(self) | ||||
| 			water_nodes = minetest.find_nodes_in_area(vec_sub(center, 4), vec_add(center, 4), {"group:water"}) | ||||
| 		end | ||||
| 		if not _self:get_action() then | ||||
| 			animalia.action_move_flock(_self, water_nodes[random(#water_nodes)], 3, "animalia:swim_simple", 1, "swim", true) | ||||
| 			animalia.action_move_boid(_self, water_nodes[random(#water_nodes)], 3, "animalia:swim_simple", 1, "swim") | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| @ -844,7 +1019,7 @@ creatura.register_utility("animalia:follow_player", function(self, player, force | ||||
| 		local dist = vec_dist(pos, plyr_pos) | ||||
| 		if not _self:get_action() then | ||||
| 			if dist > width + 1 then | ||||
| 				animalia.action_pursue(_self, player, 3, "creatura:obstacle_avoidance", 0.75) | ||||
| 				animalia.action_pursue(_self, player, 3, "creatura:context_based_steering", 0.75) | ||||
| 			else | ||||
| 				creatura.action_idle(_self, 1) | ||||
| 			end | ||||
| @ -872,7 +1047,7 @@ creatura.register_utility("animalia:flee_from_target", function(self, target) | ||||
| 			local flee_dir = vec_dir(tgt_pos, pos) | ||||
| 			local pos2 = _self:get_wander_pos(2, 3, flee_dir) | ||||
| 			local anim = (_self.animations["run"] and "run") or "walk" | ||||
| 			creatura.action_move(_self, pos2, 2, "creatura:obstacle_avoidance", 1, anim) | ||||
| 			creatura.action_move(_self, pos2, 2, "creatura:context_based_steering", 1, anim) | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| @ -985,8 +1160,9 @@ creatura.register_utility("animalia:attack_target", function(self, target) | ||||
| 		local pos = _self.object:get_pos() | ||||
| 		if not pos then return end | ||||
| 		local tgt_alive, _, tgt_pos = _self:get_target(target) | ||||
| 		if not tgt_alive then return true end | ||||
| 		if not tgt_alive then reset_attack_vals(self) return true end | ||||
| 		local dist = vec_dist(pos, tgt_pos) | ||||
| 		if dist > self.tracking_range then reset_attack_vals(self) return true end | ||||
| 		local punch_cooldown = self.punch_cooldown or 0 | ||||
| 		if punch_cooldown > 0 then | ||||
| 			punch_cooldown = punch_cooldown - self.dtime | ||||
| @ -997,16 +1173,40 @@ creatura.register_utility("animalia:attack_target", function(self, target) | ||||
| 		and not punch_init then | ||||
| 			punch_init = true | ||||
| 			animalia.action_punch(_self, target) | ||||
| 			self.punch_cooldown = 3 | ||||
| 			self.punch_cooldown = 1.5 | ||||
| 		end | ||||
| 		if not _self:get_action() then | ||||
| 			if punch_init then return true end | ||||
| 			if punch_init then reset_attack_vals(self) return true end | ||||
| 			animalia.action_pursue(_self, target, 3, "creatura:pathfind", 0.75) | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| end) | ||||
| 
 | ||||
| creatura.register_utility("animalia:glide_attack_target", function(self, target) | ||||
| 	local hidden_timer = 1 | ||||
| 	local attack_init = false | ||||
| 	local function func(_self) | ||||
| 		local pos, yaw = _self.object:get_pos(), _self.object:get_yaw() | ||||
| 		if not pos then return end | ||||
| 		local target_alive, los, tgt_pos = _self:get_target(target) | ||||
| 		if not target_alive then | ||||
| 			_self._target = nil | ||||
| 			return true | ||||
| 		end | ||||
| 		if not _self:get_action() then | ||||
| 			if attack_init then return true end | ||||
| 			local dist = vec_dist(pos, tgt_pos) | ||||
| 			if dist > 14 then | ||||
| 				creatura.action_move(_self, tgt_pos, 3, "animalia:fly_obstacle_avoidance", 0.5, "fly") | ||||
| 			else | ||||
| 				animalia.action_flight_attack(_self, target, 12) | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| end) | ||||
| 
 | ||||
| creatura.register_utility("animalia:breed", function(self) | ||||
| 	local mate = animalia.get_nearby_mate(self, self.name) | ||||
| 	if not mate then self.breeding = false return end | ||||
| @ -1050,6 +1250,174 @@ creatura.register_utility("animalia:breed", function(self) | ||||
| 	self:set_utility(func) | ||||
| end) | ||||
| 
 | ||||
| creatura.register_utility("animalia:fly_to_food", function(self, food_item) | ||||
| 	local eat_init = false | ||||
| 	local function func(_self) | ||||
| 		local pos, tgt_pos = _self.object:get_pos(), food_item and food_item:get_pos() | ||||
| 		if not pos then return end | ||||
| 		if not tgt_pos and not eat_init then return true end | ||||
| 		local dist = vec_dist(pos, tgt_pos or pos) | ||||
| 		if dist < 1 | ||||
| 		and not eat_init then | ||||
| 			eat_init = true | ||||
| 			local food_ent = food_item:get_luaentity() | ||||
| 			local stack = ItemStack(food_ent.itemstring) | ||||
| 			if stack | ||||
| 			and stack:get_count() > 1 then | ||||
| 				stack:take_item() | ||||
| 				food_ent.itemstring = stack:to_string() | ||||
| 			else | ||||
| 				food_item:remove() | ||||
| 			end | ||||
| 			self.object:get_yaw(dir2yaw(vec_dir(pos, tgt_pos))) | ||||
| 			add_eat_particle(self, "animalia:rat_raw") | ||||
| 			creatura.action_idle(_self, 1, "eat") | ||||
| 			self.eat_cooldown = 60 | ||||
| 			if self.on_eat_drop then | ||||
| 				self:on_eat_drop() | ||||
| 			end | ||||
| 		end | ||||
| 		if not _self:get_action() then | ||||
| 			if eat_init then return true end | ||||
| 			creatura.action_move(_self, tgt_pos, 3, "animalia:fly_simple", 1, "fly") | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| end) | ||||
| 
 | ||||
| creatura.register_utility("animalia:walk_to_food", function(self, food_item) | ||||
| 	local eat_init = false | ||||
| 	local function func(_self) | ||||
| 		local pos, tgt_pos = _self.object:get_pos(), food_item and food_item:get_pos() | ||||
| 		if not pos then return end | ||||
| 		if not tgt_pos and not eat_init then return true end | ||||
| 		local dist = vec_dist(pos, tgt_pos or pos) | ||||
| 		if dist < 1 | ||||
| 		and not eat_init then | ||||
| 			eat_init = true | ||||
| 			local food_ent = food_item:get_luaentity() | ||||
| 			local stack = ItemStack(food_ent.itemstring) | ||||
| 			if stack | ||||
| 			and stack:get_count() > 1 then | ||||
| 				stack:take_item() | ||||
| 				food_ent.itemstring = stack:to_string() | ||||
| 			else | ||||
| 				food_item:remove() | ||||
| 			end | ||||
| 			self.object:get_yaw(dir2yaw(vec_dir(pos, tgt_pos))) | ||||
| 			add_eat_particle(self, "animalia:rat_raw") | ||||
| 			local anim = (self.animations["eat"] and "eat") or "stand" | ||||
| 			creatura.action_idle(_self, 1, anim) | ||||
| 			self.eat_cooldown = 60 | ||||
| 			if self.on_eat_drop then | ||||
| 				self:on_eat_drop() | ||||
| 			end | ||||
| 		end | ||||
| 		if not _self:get_action() then | ||||
| 			if eat_init then return true end | ||||
| 			creatura.action_move(_self, tgt_pos, 3, "creatura:context_based_steering", 0.5, "walk") | ||||
| 		end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| end) | ||||
| 
 | ||||
| -- Pest Behavior | ||||
| 
 | ||||
| creatura.register_utility("animalia:eat_crop", function(self) | ||||
| 	local center = self.object:get_pos() | ||||
| 	if not center then return end | ||||
| 	local width = self.width | ||||
| 	local timeout = 8 | ||||
| 	local nodes = minetest.find_nodes_in_area(vec_sub(center, 6), vec_add(center, 6), "group:crop") or {} | ||||
| 	local pos2 | ||||
| 	local anim_init = false | ||||
| 	local function func(_self) | ||||
| 		local pos = _self.object:get_pos() | ||||
| 		if not pos then return end | ||||
| 		if #nodes < 1 then return true end | ||||
| 		if not _self:get_action() then | ||||
| 			if anim_init then return true end | ||||
| 			pos2 = pos2 or nodes[random(#nodes)] | ||||
| 			local node_name = minetest.get_node(pos2).name | ||||
| 			local dist = vec_dist(pos, pos2) | ||||
| 			if dist < width + 0.5 then | ||||
| 				local new_name = node_name:sub(1, #node_name - 1) .. (tonumber(node_name:sub(-1)) or 2) - 1 | ||||
| 				local new_def = minetest.registered_nodes[new_name] | ||||
| 				if not new_def then return true end | ||||
| 				local p2 = new_def.place_param2 or 1 | ||||
| 				minetest.set_node(pos2, {name = new_name, param2 = p2}) | ||||
| 				add_eat_particle(self, new_name) | ||||
| 				anim_init = true | ||||
| 				creatura.action_idle(_self, 0.5, "eat") | ||||
| 			else | ||||
| 				creatura.action_move(_self, pos2, 4, "creatura:context_based_steering") | ||||
| 			end | ||||
| 		end | ||||
| 		timeout = timeout - _self.dtime | ||||
| 		if timeout <= 0 then return true end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| end) | ||||
| 
 | ||||
| local function take_food_from_chest(self, pos) | ||||
| 	local inv = minetest.get_inventory({type = "node", pos = pos}) | ||||
| 	if inv | ||||
| 	and inv:get_list("main") then | ||||
| 		for i, stack in ipairs(inv:get_list("main")) do | ||||
| 			local item_name = stack:get_name() | ||||
| 			local def = minetest.registered_items[item_name] | ||||
| 			for group in pairs(def.groups) do | ||||
| 				if group:match("food_") then | ||||
| 					stack:take_item() | ||||
| 					inv:set_stack("main", i, stack) | ||||
| 					add_eat_particle(self, item_name) | ||||
| 					return true | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| creatura.register_utility("animalia:steal_from_chest", function(self) | ||||
| 	local center = self.object:get_pos() | ||||
| 	if not center then return end | ||||
| 	local width = self.width | ||||
| 	local timeout = 8 | ||||
| 	local nodes = minetest.find_nodes_with_meta(vec_sub(center, 6), vec_add(center, 6)) or {} | ||||
| 	local pos2 | ||||
| 	for _, node_pos in ipairs(nodes) do | ||||
| 		local meta = minetest.get_meta(node_pos) | ||||
| 		if meta:get_string("owner") == "" then | ||||
| 			local inv = minetest.get_inventory({type = "node", pos = node_pos}) | ||||
| 			if inv | ||||
| 			and inv:get_list("main") then | ||||
| 				pos2 = node_pos | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	local anim_init = false | ||||
| 	local function func(_self) | ||||
| 		local pos = _self.object:get_pos() | ||||
| 		if not pos then return end | ||||
| 		if not pos2 then return true end | ||||
| 		if not _self:get_action() then | ||||
| 			if anim_init then return true end | ||||
| 			local dist = vec_dist(pos, pos2) | ||||
| 			if dist < width + 1.1 then | ||||
| 				anim_init = true | ||||
| 				if take_food_from_chest(self, pos2) then | ||||
| 					creatura.action_idle(_self, 0.5, "eat") | ||||
| 				end | ||||
| 			else | ||||
| 				creatura.action_move(_self, pos2, 2, "creatura:context_based_steering") | ||||
| 			end | ||||
| 		end | ||||
| 		timeout = timeout - _self.dtime | ||||
| 		if timeout <= 0 then return true end | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| end) | ||||
| 
 | ||||
| -- Domesticated Behavior | ||||
| 
 | ||||
| creatura.register_utility("animalia:stay", function(self) | ||||
| @ -1189,7 +1557,7 @@ creatura.register_utility("animalia:mount_horse", function(self, player) | ||||
| 		and vel.y < 1 then | ||||
| 			_self.object:add_velocity({ | ||||
| 				x = 0, | ||||
| 				y = _self.jump_power + (abs(_self._movement_data.gravity) * 0.33), | ||||
| 				y = _self.jump_power, | ||||
| 				z = 0 | ||||
| 			}) | ||||
| 		elseif not _self.touching_ground then | ||||
| @ -1230,3 +1598,35 @@ creatura.register_utility("animalia:flop", function(self) | ||||
| 	end | ||||
| 	self:set_utility(func) | ||||
| end) | ||||
| 
 | ||||
| -- Global Utilities | ||||
| 
 | ||||
| animalia.global_utils = { | ||||
| 	["basic_follow"] = { | ||||
| 		utility = "animalia:follow_player", | ||||
| 		get_score = function(self) | ||||
| 			local lasso_tgt = self._lassod_to | ||||
| 			local lasso = type(lasso_tgt) == "string" and minetest.get_player_by_name(lasso_tgt) | ||||
| 			local force = lasso and lasso ~= false | ||||
| 			local player = (force and lasso) or creatura.get_nearby_player(self) | ||||
| 			if player | ||||
| 			and (self:follow_wielded_item(player) | ||||
| 			or force) then | ||||
| 				return 0.4, {self, player, force} | ||||
| 			end | ||||
| 			return 0 | ||||
| 		end | ||||
| 	}, | ||||
| 	["basic_flee"] = { | ||||
| 		utility = "animalia:flee_from_target", | ||||
| 		get_score = function(self) | ||||
| 			local puncher = self._puncher | ||||
| 			if puncher | ||||
| 			and puncher:get_pos() then | ||||
| 				return 0.6, {self, puncher} | ||||
| 			end | ||||
| 			self._puncher = nil | ||||
| 			return 0 | ||||
| 		end | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										584
									
								
								api/lasso.lua
									
									
									
									
									
								
							
							
						
						| @ -3,359 +3,253 @@ | ||||
| ----------- | ||||
| 
 | ||||
| local abs = math.abs | ||||
| local asin = math.asin | ||||
| local atan2 = math.atan2 | ||||
| local cos = math.cos | ||||
| local deg = math.deg | ||||
| local sin = math.sin | ||||
| 
 | ||||
| function animalia.initialize_lasso(self) | ||||
|     self.lasso_origin = self:recall("lasso_origin") or nil | ||||
|     if self.lasso_origin then | ||||
|         self.caught_with_lasso = true | ||||
|         if type(self.lasso_origin) == "table" | ||||
|         and minetest.get_item_group(minetest.get_node(self.lasso_origin).name, "fence") > 0 then | ||||
|             local object = minetest.add_entity(self.lasso_origin, "animalia:lasso_fence_ent") | ||||
|             object:get_luaentity().parent = self.object | ||||
|         elseif type(self.lasso_origin) == "string" | ||||
|         and minetest.get_player_by_name(self.lasso_origin) then | ||||
|             self.lasso_origin = minetest.get_player_by_name(self.lasso_origin) | ||||
|         else | ||||
|             self:forget("lasso_origin") | ||||
|         end | ||||
|     end | ||||
| local function diff(a, b) -- Get difference between 2 angles | ||||
| 	return atan2(sin(b - a), cos(b - a)) | ||||
| end | ||||
| 
 | ||||
| function animalia.set_lasso_visual(self, target) | ||||
|     if not creatura.is_alive(self) | ||||
|     or (self.lasso_visual | ||||
|     and self.lasso_visual:get_luaentity()) then return end | ||||
|     local pos = self.object:get_pos() | ||||
|     local object = minetest.add_entity(pos, "animalia:lasso_visual") | ||||
|     local ent = object:get_luaentity() | ||||
|     self.lasso_visual = object | ||||
|     self.lasso_origin = target | ||||
|     ent.parent = self.object | ||||
|     ent.lasso_origin = target | ||||
|     return object | ||||
| local vec_add, vec_dir, vec_dist, vec_len = vector.add, vector.direction, vector.distance, vector.length | ||||
| local dir2yaw, dir2rot = minetest.dir_to_yaw, vector.dir_to_rotation | ||||
| 
 | ||||
| -- Entities -- | ||||
| 
 | ||||
| local using_lasso = {} | ||||
| 
 | ||||
| minetest.register_entity("animalia:lasso_entity", { | ||||
| 	visual = "mesh", | ||||
| 	mesh = "animalia_lasso_entity.b3d", | ||||
| 	textures = {"animalia_lasso_entity.png"}, | ||||
| 	pointable = false, | ||||
| 	on_activate = function(self) | ||||
| 		self.object:set_armor_groups({immortal = 1}) | ||||
| 	end, | ||||
| 	_scale = 1, | ||||
| 	on_step = function(self, dtime) | ||||
| 		local pos, parent = self.object:get_pos(), (self.object:get_attach() or self._attached) | ||||
| 		local pointed_ent = self._point_to and self._point_to:get_luaentity() | ||||
| 		local point_to = self._point_to and self._point_to:get_pos() | ||||
| 		if not pos or not parent or not point_to then self.object:remove() return end | ||||
| 		if type(parent) == "string" then | ||||
| 			parent = minetest.get_player_by_name(parent) | ||||
| 			local tgt_pos = parent:get_pos() | ||||
| 			local dist = vec_dist(pos, tgt_pos) | ||||
| 			if dist > 0.5 then | ||||
| 				self.object:set_pos(tgt_pos) | ||||
| 			else | ||||
| 				self.object:move_to(tgt_pos) | ||||
| 			end | ||||
| 			self.object:set_rotation(dir2rot(vec_dir(tgt_pos, point_to))) | ||||
| 		elseif parent.x then | ||||
| 			point_to.y = point_to.y + pointed_ent.height * 0.5 | ||||
| 			self.object:set_rotation(dir2rot(vec_dir(pos, point_to))) | ||||
| 		else | ||||
| 			self.object:remove() | ||||
| 			return | ||||
| 		end | ||||
| 		local size = vec_dist(pos, point_to) | ||||
| 		if abs(size - self._scale) > 0.1 then | ||||
| 			self.object:set_properties({ | ||||
| 				visual_size = {x = 1, y = 1, z = size} | ||||
| 			}) | ||||
| 			self._scale = size | ||||
| 		end | ||||
| 	end | ||||
| }) | ||||
| 
 | ||||
| local function remove_from_fence(self) | ||||
| 	local pos = self.object:get_pos() | ||||
| 	local mob = self._mob and self._mob:get_luaentity() | ||||
| 	if not mob then | ||||
| 		self.object:remove() | ||||
| 		return | ||||
| 	end | ||||
| 	mob._lassod_to = nil | ||||
| 	mob:forget("_lassod_to") | ||||
| 	mob._lasso_ent:remove() | ||||
| 	local dirs = { | ||||
| 		{x = 0.5, y = 0, z = 0}, | ||||
| 		{x = -0.5, y = 0, z = 0}, | ||||
| 		{x = 0, y = 0.5, z = 0}, | ||||
| 		{x = 0, y = -0.5, z = 0}, | ||||
| 		{x = 0, y = 0, z = 0.5}, | ||||
| 		{x = 0, y = 0, z = -0.5} | ||||
| 	} | ||||
| 	for i = 1, 6 do | ||||
| 		local i_pos = vec_add(pos, dirs[i]) | ||||
| 		if not creatura.get_node_def(i_pos).walkable then | ||||
| 			minetest.add_item(i_pos, "animalia:lasso") | ||||
| 			break | ||||
| 		end | ||||
| 	end | ||||
| 	self.object:remove() | ||||
| end | ||||
| 
 | ||||
| minetest.register_entity("animalia:tied_lasso_entity", { | ||||
| 	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_tied_lasso_entity.png", | ||||
| 		"animalia_tied_lasso_entity.png", | ||||
| 		"animalia_tied_lasso_entity.png", | ||||
| 		"animalia_tied_lasso_entity.png", | ||||
| 		"animalia_tied_lasso_entity.png", | ||||
| 		"animalia_tied_lasso_entity.png", | ||||
| 	}, | ||||
| 	on_activate = function(self) | ||||
| 		self.object:set_armor_groups({immortal = 1}) | ||||
| 	end, | ||||
| 	on_step = function(self) | ||||
| 		local mob = self._mob and self._mob:get_luaentity() | ||||
| 		if not mob then remove_from_fence(self) return end | ||||
| 	end, | ||||
| 	on_rightclick = remove_from_fence, | ||||
| 	on_punch = remove_from_fence | ||||
| }) | ||||
| 
 | ||||
| -- API -- | ||||
| 
 | ||||
| local function add_lasso(self, origin) | ||||
| 	local pos = self.object:get_pos() | ||||
| 	if not pos then return end | ||||
| 	local object = minetest.add_entity(pos, "animalia:lasso_entity") | ||||
| 	local ent = object and object:get_luaentity() | ||||
| 	if not ent then return end | ||||
| 	-- Attachment point of entity | ||||
| 	ent._attached = origin | ||||
| 	if type(origin) == "string" then | ||||
| 		local player = minetest.get_player_by_name(origin) | ||||
| 		--object:set_attach(player) | ||||
| 	else | ||||
| 		object:set_pos(origin) | ||||
| 	end | ||||
| 	self._lassod_to = origin | ||||
| 	ent._point_to = self.object | ||||
| 	self:memorize("_lassod_to", origin) | ||||
| 	return object | ||||
| end | ||||
| 
 | ||||
| local function get_rope_velocity(pos1, pos2, dist) | ||||
| 	local force = dist / 10 | ||||
| 	local vel = vector.new((pos2.x - pos1.x) * force, ((pos2.y - pos1.y) / (24 + dist)), (pos2.z - pos1.z) * force) | ||||
| 	return vel | ||||
| end | ||||
| 
 | ||||
| function animalia.initialize_lasso(self) | ||||
| 	self._lassod_to = self:recall("_lassod_to") or self:recall("lasso_origin") | ||||
| 	if self._lassod_to then | ||||
| 		local origin = self._lassod_to | ||||
| 		if type(origin) == "table" | ||||
| 		and minetest.get_item_group(minetest.get_node(origin).name, "fence") > 0 then | ||||
| 			local object = minetest.add_entity(origin, "animalia:tied_lasso_entity") | ||||
| 			object:get_luaentity()._mob = self.object | ||||
| 			self._lasso_ent = add_lasso(self, origin) | ||||
| 		elseif type(origin) == "string" then | ||||
| 			self._lassod_to = origin | ||||
| 			self._lasso_ent = add_lasso(self, origin) | ||||
| 		else | ||||
| 			self:forget("_lassod_to") | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| function animalia.update_lasso_effects(self) | ||||
|     if not creatura.is_alive(self) then return end | ||||
|     if self.caught_with_lasso | ||||
|     and self.lasso_origin then | ||||
|         local pos = self.object:get_pos() | ||||
|         pos.y = pos.y + (self:get_height() * 0.5) | ||||
|         animalia.set_lasso_visual(self, self.lasso_origin) | ||||
|         if type(self.lasso_origin) == "userdata" | ||||
|         or type(self.lasso_origin) == "string" then | ||||
|             if type(self.lasso_origin) == "string" then | ||||
|                 self.lasso_origin = minetest.get_player_by_name(self.lasso_origin) | ||||
|                 if not self.lasso_origin then | ||||
|                     self.caught_with_lasso = nil | ||||
|                     self.lasso_origin = nil | ||||
|                     self:forget("lasso_origin") | ||||
|                     if self.lasso_visual then | ||||
|                         self.lasso_visual:remove() | ||||
|                         self.lasso_visual = nil | ||||
|                     end | ||||
|                     return | ||||
|                 end | ||||
|             end | ||||
|             self:memorize("lasso_origin", self.lasso_origin:get_player_name()) | ||||
|             -- Get distance to lasso player | ||||
|             local player = self.lasso_origin | ||||
|             local lasso_origin = player:get_pos() | ||||
|             lasso_origin.y = lasso_origin.y + 1 | ||||
|             local dist = vector.distance(pos, lasso_origin) | ||||
|             if player:get_wielded_item():get_name() ~= "animalia:lasso" | ||||
|             or vector.distance(pos, lasso_origin) > 16 then | ||||
|                 self.caught_with_lasso = nil | ||||
|                 self.lasso_origin = nil | ||||
|                 self:forget("lasso_origin") | ||||
|                 if self.lasso_visual then | ||||
|                     self.lasso_visual:remove() | ||||
|                     self.lasso_visual = nil | ||||
|                 end | ||||
|             end | ||||
|             -- Apply physics | ||||
|             if dist > 6 | ||||
|             or abs(lasso_origin.y - pos.y) > 8 then | ||||
|                 local p_target = vector.add(pos, vector.multiply(vector.direction(pos, lasso_origin), 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)) * ((lasso_origin.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 | ||||
|         elseif type(self.lasso_origin) == "table" then | ||||
|             self:memorize("lasso_origin", self.lasso_origin) | ||||
|             local lasso_origin = self.lasso_origin | ||||
|             local dist = vector.distance(pos, lasso_origin) | ||||
|             if dist > 6 | ||||
|             or abs(lasso_origin.y - pos.y) > 8 then | ||||
|                 local p_target = vector.add(pos, vector.multiply(vector.direction(pos, lasso_origin), 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)) * ((lasso_origin.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(lasso_origin, 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_origin = nil | ||||
|                 self:forget("lasso_origin") | ||||
|                 if self.lasso_visual then | ||||
|                     self.lasso_visual:remove() | ||||
|                     self.lasso_visual = nil | ||||
|                 end | ||||
|             end | ||||
|         else | ||||
|             local objects = minetest.get_objects_inside_radius(self.lasso_origin, 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 | ||||
|             self.caught_with_lasso = nil | ||||
|             self.lasso_origin = nil | ||||
|             self:forget("lasso_origin") | ||||
|             if self.lasso_visual then | ||||
|                 self.lasso_visual:remove() | ||||
|                 self.lasso_visual = nil | ||||
|             end | ||||
|         end | ||||
|     end | ||||
| 	local pos = self.object:get_pos() | ||||
| 	if not creatura.is_alive(self) then return end | ||||
| 	if self._lassod_to then | ||||
| 		local lasso = self._lassod_to | ||||
| 		self._lasso_ent = self._lasso_ent or add_lasso(self, lasso) | ||||
| 		if type(lasso) == "string" then | ||||
| 			using_lasso[lasso] = self | ||||
| 			local name = lasso | ||||
| 			lasso = minetest.get_player_by_name(lasso) | ||||
| 			if lasso:get_wielded_item():get_name() ~= "animalia:lasso" then | ||||
| 				using_lasso[name] = nil | ||||
| 				self._lasso_ent:remove() | ||||
| 				self._lasso_ent = nil | ||||
| 				self._lassod_to = nil | ||||
| 				self:forget("_lassod_to") | ||||
| 				return | ||||
| 			end | ||||
| 			local lasso_pos = lasso:get_pos() | ||||
| 			local dist = vec_dist(pos, lasso_pos) | ||||
| 			local vel = self.object:get_velocity() | ||||
| 			if not vel or dist < 8 and self.touching_ground then return end | ||||
| 			if vec_len(vel) < 8 then | ||||
| 				self.object:add_velocity(get_rope_velocity(pos, lasso_pos, dist)) | ||||
| 			end | ||||
| 			return | ||||
| 		elseif type(lasso) == "table" then | ||||
| 			local dist = vec_dist(pos, lasso) | ||||
| 			local vel = self.object:get_velocity() | ||||
| 			if not vel or dist < 8 and self.touching_ground then return end | ||||
| 			if vec_len(vel) < 8 then | ||||
| 				self.object:add_velocity(get_rope_velocity(pos, lasso, dist)) | ||||
| 			end | ||||
| 			return | ||||
| 		end | ||||
| 	end | ||||
| 	if self._lasso_ent then | ||||
| 		self._lasso_ent:remove() | ||||
| 		self._lasso_ent = nil | ||||
| 		self._lassod_to = nil | ||||
| 		self:forget("_lassod_to") | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| 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_origin | ||||
|             and type(ent.lasso_origin) == "userdata" | ||||
|             and ent.lasso_origin == player then | ||||
|                 return true | ||||
|             end | ||||
|         end | ||||
|     end | ||||
|     return false | ||||
| end | ||||
| 
 | ||||
| local function update_lasso_rotation(self) | ||||
|     if not self.parent | ||||
|     or not self.lasso_origin then self.object:remove() return end | ||||
|     local lasso_origin = self.lasso_origin | ||||
|     if type(lasso_origin) == "userdata" then | ||||
|         lasso_origin = lasso_origin:get_pos() | ||||
|         lasso_origin.y = lasso_origin.y + 1 | ||||
|     end | ||||
|     local object = self.parent | ||||
|     if not object then return end | ||||
|     local pos = object:get_pos() | ||||
|     pos.y = pos.y + object:get_luaentity():get_height() | ||||
|     local rot = vector.dir_to_rotation(vector.direction(lasso_origin, pos)) | ||||
|     self.object:set_pos(lasso_origin) | ||||
|     self.object:set_rotation(rot) | ||||
|     self.object:set_properties({ | ||||
|         visual_size = {x = 6, z = 10 * vector.distance(pos, lasso_origin), y = 6} | ||||
|     }) | ||||
| end | ||||
| 
 | ||||
| minetest.register_entity("animalia:lasso_visual", { | ||||
|     hp_max = 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) | ||||
|         self.object:set_armor_groups({immortal = 1}) | ||||
|         if not self.parent | ||||
|         or not self.lasso_origin | ||||
|         or (self.parent | ||||
|         and (not creatura.is_alive(self.parent) | ||||
|         or not self.parent:get_luaentity().caught_with_lasso)) then | ||||
|             self.object:remove() | ||||
|             return | ||||
|         end | ||||
|         update_lasso_rotation(self) | ||||
|     end | ||||
| }) | ||||
| 
 | ||||
| minetest.register_entity("animalia:frog_tongue_visual", { | ||||
|     hp_max = 1, | ||||
|     physical = false, | ||||
|     collisionbox = {0, 0, 0, 0, 0, 0}, | ||||
|     visual = "mesh", | ||||
|     mesh = "animalia_lasso.b3d", | ||||
|     visual_size = {x = 2, y = 2}, | ||||
|     textures = {"animalia_frog_tongue.png"}, | ||||
|     is_visible = true, | ||||
|     makes_footstep_sound = false, | ||||
|     on_step = function(self) | ||||
|         self.object:set_armor_groups({immortal = 1}) | ||||
|         if not self.parent | ||||
|         or not self.lasso_origin | ||||
|         or (self.parent | ||||
|         and (not creatura.is_alive(self.parent) | ||||
|         or not self.parent:get_luaentity().caught_with_lasso)) then | ||||
|             self.object:remove() | ||||
|             return | ||||
|         end | ||||
|         update_lasso_rotation(self) | ||||
|     end | ||||
| }) | ||||
| 
 | ||||
| minetest.register_entity("animalia:lasso_fence_ent", { | ||||
|     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_origin 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_origin = ent:memorize("lasso_origin", 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_origin = ent:memorize("lasso_origin", 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_origin = ent:memorize("lasso_origin", 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 | ||||
| }) | ||||
| -- Item | ||||
| 
 | ||||
| minetest.register_craftitem("animalia:lasso", { | ||||
|     description = "Lasso", | ||||
|     inventory_image = "animalia_lasso.png", | ||||
|     on_secondary_use = function(_, 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.catch_with_lasso then return end | ||||
|             if not ent.caught_with_lasso | ||||
|             and not is_lasso_in_use(placer) then | ||||
|                 ent.caught_with_lasso = true | ||||
|                 ent.lasso_origin = placer | ||||
|             elseif ent.lasso_origin | ||||
|             and ent.lasso_origin == placer then | ||||
|                 ent.caught_with_lasso = nil | ||||
|                 ent.lasso_origin = 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_origin | ||||
|                     and obj:get_luaentity().lasso_visual | ||||
|                     and type(obj:get_luaentity().lasso_origin) == "userdata" | ||||
|                     and obj:get_luaentity().lasso_origin == placer then | ||||
|                         obj:get_luaentity().lasso_visual:get_luaentity().lasso_origin = pos | ||||
|                         obj:get_luaentity().lasso_origin = pos | ||||
|                         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 | ||||
| 	description = "Lasso", | ||||
| 	inventory_image = "animalia_lasso.png", | ||||
| 	on_secondary_use = function(_, placer, pointed) | ||||
| 		local ent = pointed.ref and pointed.ref:get_luaentity() | ||||
| 		if ent | ||||
| 		and (ent.name:match("^animalia:") | ||||
| 		or ent.name:match("^monstrum:")) then | ||||
| 			if not ent.catch_with_lasso then return end | ||||
| 			local name = placer:get_player_name() | ||||
| 			if not ent._lassod_to | ||||
| 			and not using_lasso[name] then | ||||
| 				using_lasso[name] = ent | ||||
| 				ent._lassod_to = name | ||||
| 				ent:memorize("_lassod_to", name) | ||||
| 			elseif ent._lassod_to | ||||
| 			and ent._lassod_to == name then | ||||
| 				using_lasso[name] = nil | ||||
| 				ent._lassod_to = nil | ||||
| 				ent:forget("_lassod_to") | ||||
| 			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 name = placer:get_player_name() | ||||
| 				local ent = using_lasso[name] | ||||
| 				if ent | ||||
| 				and ent._lassod_to | ||||
| 				and ent._lassod_to == name then | ||||
| 					using_lasso[name] = nil | ||||
| 					ent._lasso_ent:set_detach() | ||||
| 					ent._lasso_ent:set_pos(pos) | ||||
| 					ent._lasso_ent:get_luaentity()._attached = pos | ||||
| 					ent._lassod_to = pos | ||||
| 					ent:memorize("_lassod_to", pos) | ||||
| 					local fence_obj = minetest.add_entity(pos, "animalia:tied_lasso_entity") | ||||
| 					fence_obj:get_luaentity()._mob = ent.object | ||||
| 					fence_obj:get_luaentity()._lasso_obj = ent._lasso_ent | ||||
| 					itemstack:take_item(1) | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		return itemstack | ||||
| 	end | ||||
| }) | ||||
							
								
								
									
										137
									
								
								api/spawning.lua
									
									
									
									
									
								
							
							
						
						| @ -56,20 +56,12 @@ creatura.register_mob_spawn("animalia:cow", { | ||||
| 	biomes = animalia.registered_biome_groups["grassland"].biomes | ||||
| }) | ||||
| 
 | ||||
| creatura.register_mob_spawn("animalia:frog", { | ||||
| 	chance = 2, | ||||
| 	min_radius = 4, | ||||
| 	max_radius = 16, | ||||
| 	min_light = 0, | ||||
| 	min_height = -32, | ||||
| 	max_height = 8, | ||||
| 	min_group = 2, | ||||
| 	max_group = 6, | ||||
| 	biomes = frog_biomes, | ||||
| 	spawn_cluster = true, | ||||
| 	spawn_in_nodes = true, | ||||
| creatura.register_mob_spawn("animalia:fox", { | ||||
| 	chance = 4, | ||||
| 	min_group = 1, | ||||
| 	max_group = 2, | ||||
| 	spawn_on_gen = true, | ||||
| 	nodes = {"default:water_source"}, | ||||
| 	biomes = animalia.registered_biome_groups["boreal"].biomes | ||||
| }) | ||||
| 
 | ||||
| creatura.register_mob_spawn("animalia:horse", { | ||||
| @ -80,6 +72,28 @@ creatura.register_mob_spawn("animalia:horse", { | ||||
| 	biomes = animalia.registered_biome_groups["grassland"].biomes | ||||
| }) | ||||
| 
 | ||||
| creatura.register_abm_spawn("animalia:rat", { | ||||
| 	chance = 2000, | ||||
| 	interval = 30, | ||||
| 	min_height = -1, | ||||
| 	max_height = 1024, | ||||
| 	min_group = 1, | ||||
| 	max_group = 3, | ||||
| 	spawn_in_nodes = true, | ||||
| 	nodes = {"group:crop"} | ||||
| }) | ||||
| 
 | ||||
| creatura.register_abm_spawn("animalia:owl", { | ||||
| 	chance = 30000, | ||||
| 	interval = 60, | ||||
| 	min_height = 3, | ||||
| 	max_height = 1024, | ||||
| 	min_group = 1, | ||||
| 	max_group = 1, | ||||
| 	spawn_cap = 1, | ||||
| 	nodes = {"group:leaves"} | ||||
| }) | ||||
| 
 | ||||
| creatura.register_mob_spawn("animalia:pig", { | ||||
| 	chance = 3, | ||||
| 	min_group = 2, | ||||
| @ -119,37 +133,6 @@ creatura.register_mob_spawn("animalia:wolf", { | ||||
| 	biomes = animalia.registered_biome_groups["boreal"].biomes | ||||
| }) | ||||
| 
 | ||||
| creatura.register_mob_spawn("animalia:bird", { | ||||
| 	chance = 1, | ||||
| 	min_light = 0, | ||||
| 	min_group = 12, | ||||
| 	max_group = 16, | ||||
| 	biomes = animalia.registered_biome_groups["common"].biomes, | ||||
| 	spawn_cluster = true, | ||||
| 	nodes = {"group:leaves"} | ||||
| }) | ||||
| 
 | ||||
| creatura.register_on_spawn("animalia:bird", function(self, pos) | ||||
| 	local node = minetest.get_node(pos) | ||||
| 	if node.name == "air" then | ||||
| 		minetest.set_node(pos, {name = "animalia:nest_song_bird"}) | ||||
| 		self.home_position = self:memorize("home_position", pos) | ||||
| 		self.despawn_after = self:memorize("despawn_after", nil) | ||||
| 	else | ||||
| 		local nodes = minetest.find_nodes_in_area_under_air( | ||||
| 			{x = pos.x - 3, y = pos.y - 3, z = pos.z - 3}, | ||||
| 			{x = pos.x + 3, y = pos.y + 7, z = pos.z + 3}, | ||||
| 			"group:leaves" | ||||
| 		) | ||||
| 		if nodes[1] then | ||||
| 			pos = nodes[1] | ||||
| 			minetest.set_node({x = pos.x, y = pos.y + 1, z = pos.z}, {name = "animalia:nest_song_bird"}) | ||||
| 			self.home_position = self:memorize("home_position", {x = pos.x, y = pos.y + 1, z = pos.z}) | ||||
| 			self.despawn_after = self:memorize("despawn_after", nil) | ||||
| 		end | ||||
| 	end | ||||
| end) | ||||
| 
 | ||||
| creatura.register_mob_spawn("animalia:tropical_fish", { | ||||
| 	chance = 3, | ||||
| 	min_height = -128, | ||||
| @ -161,3 +144,69 @@ creatura.register_mob_spawn("animalia:tropical_fish", { | ||||
| 	spawn_on_gen = true, | ||||
| 	nodes = {"default:water_source"} | ||||
| }) | ||||
| 
 | ||||
| -- Ambient Spawning | ||||
| 
 | ||||
| creatura.register_abm_spawn("animalia:bat", { | ||||
| 	chance = 10000, | ||||
| 	interval = 30, | ||||
| 	min_light = 0, | ||||
| 	min_height = -31000, | ||||
| 	max_height = 1, | ||||
| 	min_group = 3, | ||||
| 	max_group = 5, | ||||
| 	spawn_cap = 6, | ||||
| 	nodes = {"group:stone"} | ||||
| }) | ||||
| 
 | ||||
| creatura.register_abm_spawn("animalia:bird", { | ||||
| 	chance = 15000, | ||||
| 	interval = 60, | ||||
| 	min_light = 0, | ||||
| 	min_height = 1, | ||||
| 	max_height = 1024, | ||||
| 	min_group = 12, | ||||
| 	max_group = 16, | ||||
| 	spawn_cap = 12, | ||||
| 	nodes = {"group:leaves"} | ||||
| }) | ||||
| 
 | ||||
| creatura.register_on_spawn("animalia:bird", function(self, pos) | ||||
| 	local nests = minetest.find_nodes_in_area_under_air( | ||||
| 		{x = pos.x - 12, y = pos.y - 12, z = pos.z - 12}, | ||||
| 		{x = pos.x + 12, y = pos.y + 12, z = pos.z + 12}, | ||||
| 		"animalia:nest_song_bird" | ||||
| 	) | ||||
| 	if nests[1] then | ||||
| 		self.home_position = self:memorize("home_position", nests[1]) | ||||
| 		return | ||||
| 	end | ||||
| 	local node = minetest.get_node(pos) | ||||
| 	if node.name == "air" then | ||||
| 		minetest.set_node(pos, {name = "animalia:nest_song_bird"}) | ||||
| 		self.home_position = self:memorize("home_position", pos) | ||||
| 	else | ||||
| 		local nodes = minetest.find_nodes_in_area_under_air( | ||||
| 			{x = pos.x - 3, y = pos.y - 3, z = pos.z - 3}, | ||||
| 			{x = pos.x + 3, y = pos.y + 7, z = pos.z + 3}, | ||||
| 			"group:leaves" | ||||
| 		) | ||||
| 		if nodes[1] then | ||||
| 			pos = nodes[1] | ||||
| 			minetest.set_node({x = pos.x, y = pos.y + 1, z = pos.z}, {name = "animalia:nest_song_bird"}) | ||||
| 			self.home_position = self:memorize("home_position", {x = pos.x, y = pos.y + 1, z = pos.z}) | ||||
| 		end | ||||
| 	end | ||||
| end) | ||||
| 
 | ||||
| creatura.register_abm_spawn("animalia:frog", { | ||||
| 	chance = 7500, | ||||
| 	interval = 60, | ||||
| 	min_light = 0, | ||||
| 	min_height = -1, | ||||
| 	max_height = 8, | ||||
| 	min_group = 2, | ||||
| 	max_group = 4, | ||||
| 	neighbors = {"group:water"}, | ||||
| 	nodes = {"group:soil"} | ||||
| }) | ||||
							
								
								
									
										297
									
								
								craftitems.lua
									
									
									
									
									
								
							
							
						
						| @ -27,7 +27,7 @@ end | ||||
| 
 | ||||
| local function register_egg(name, def) | ||||
| 
 | ||||
| 	minetest.register_entity(def.mob .. "_egg_sprite", { | ||||
| 	minetest.register_entity(def.mob .. "_egg_entity", { | ||||
| 		hp_max = 1, | ||||
| 		physical = true, | ||||
| 		collisionbox = {0, 0, 0, 0, 0, 0}, | ||||
| @ -36,94 +36,75 @@ local function register_egg(name, def) | ||||
| 		textures = {"animalia_egg.png"}, | ||||
| 		initial_sprite_basepos = {x = 0, y = 0}, | ||||
| 		is_visible = true, | ||||
| 		on_step = function(self) | ||||
| 		on_step = function(self, _, moveresult) | ||||
| 			local pos = self.object:get_pos() | ||||
| 			local objects = minetest.get_objects_inside_radius(pos, 1.5) | ||||
| 			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), | ||||
| 				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) | ||||
| 			if not pos then return end | ||||
| 			if moveresult.collides then | ||||
| 				for _, collision in ipairs(moveresult.collision) do | ||||
| 					if collision.type == "nodes" then | ||||
| 						minetest.add_particlespawner({ | ||||
| 							amount = 6, | ||||
| 							time = 0.1, | ||||
| 							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 = {x = -1, y = 2, z = -1}, | ||||
| 							maxvel = {x = 1, y = 5, z = 1}, | ||||
| 							minacc = {x = 0, y = -9.8, z = 0}, | ||||
| 							maxacc = {x = 0, y = -9.8, z = 0}, | ||||
| 							collisiondetection = true, | ||||
| 							collision_removal = true, | ||||
| 							texture = "animalia_egg_fragment.png" | ||||
| 						}) | ||||
| 						break | ||||
| 					elseif collision.type == "object" then | ||||
| 						collision.object:punch(self.object, 2.0, {full_punch_interval = 0.1, damage_groups = {fleshy = 1}}, nil) | ||||
| 						break | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
| 			if #cube >= 1 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", | ||||
| 				}) | ||||
| 				if random(1, 3) < 2 then | ||||
| 					local object = minetest.add_entity(pos, def.mob) | ||||
| 					local ent = object:get_luaentity() | ||||
| 					local ent = object and object:get_luaentity() | ||||
| 					ent.growth_scale = 0.7 | ||||
| 					animalia.initialize_api(ent) | ||||
| 					animalia.protect_from_despawn(ent) | ||||
| 					self.object:remove() | ||||
| 				else | ||||
| 					self.object:remove() | ||||
| 				end | ||||
| 				self.object:remove() | ||||
| 			end | ||||
| 		end | ||||
| 	}) | ||||
| 
 | ||||
| 	local function mobs_shoot_egg(item, player) | ||||
| 		local pos = player:get_pos() | ||||
| 
 | ||||
| 		minetest.sound_play("default_place_node_hard", { | ||||
| 			pos = pos, | ||||
| 			gain = 1.0, | ||||
| 			max_hear_distance = 5, | ||||
| 		}) | ||||
| 
 | ||||
| 		local vel = 19 | ||||
| 		local gravity = 9 | ||||
| 
 | ||||
| 		local obj = minetest.add_entity({ | ||||
| 			x = pos.x, | ||||
| 			y = pos.y +1.5, | ||||
| 			z = pos.z | ||||
| 		}, def.mob .. "_egg_sprite") | ||||
| 
 | ||||
| 		local ent = obj:get_luaentity() | ||||
| 		local dir = player:get_look_dir() | ||||
| 
 | ||||
| 		ent.velocity = vel -- needed for api internal timing | ||||
| 		ent.switch = 1 -- needed so that egg doesn't despawn straight away | ||||
| 
 | ||||
| 		obj:set_velocity({ | ||||
| 			x = dir.x * vel, | ||||
| 			y = dir.y * vel, | ||||
| 			z = dir.z * vel | ||||
| 		}) | ||||
| 
 | ||||
| 		obj:set_acceleration({ | ||||
| 			x = dir.x * -3, | ||||
| 			y = -gravity, | ||||
| 			z = dir.z * -3 | ||||
| 		}) | ||||
| 
 | ||||
| 		-- pass player name to egg for chick ownership | ||||
| 		local ent2 = obj:get_luaentity() | ||||
| 		ent2.playername = player:get_player_name() | ||||
| 
 | ||||
| 		item:take_item() | ||||
| 
 | ||||
| 		return item | ||||
| 	end | ||||
| 
 | ||||
| 	minetest.register_craftitem(name, { | ||||
| 		description = def.description, | ||||
| 		inventory_image = def.inventory_image .. ".png", | ||||
| 		on_use = mobs_shoot_egg, | ||||
| 		on_use = function(itemstack, player) | ||||
| 			local pos = player:get_pos() | ||||
| 			minetest.sound_play("default_place_node_hard", { | ||||
| 				pos = pos, | ||||
| 				gain = 1.0, | ||||
| 				max_hear_distance = 5, | ||||
| 			}) | ||||
| 			local vel = 19 | ||||
| 			local gravity = 9 | ||||
| 			local object = minetest.add_entity({ | ||||
| 				x = pos.x, | ||||
| 				y = pos.y + 1.5, | ||||
| 				z = pos.z | ||||
| 			}, def.mob .. "_egg_entity") | ||||
| 			local ent = object and object:get_luaentity() | ||||
| 			local dir = player:get_look_dir() | ||||
| 			obj:set_velocity({ | ||||
| 				x = dir.x * vel, | ||||
| 				y = dir.y * vel, | ||||
| 				z = dir.z * vel | ||||
| 			}) | ||||
| 			obj:set_acceleration({ | ||||
| 				x = dir.x * -3, | ||||
| 				y = -gravity, | ||||
| 				z = dir.z * -3 | ||||
| 			}) | ||||
| 			itemstack:take_item() | ||||
| 			return itemstack | ||||
| 		end, | ||||
| 		groups = {food_egg = 1, flammable = 2}, | ||||
| 	}) | ||||
| 
 | ||||
| @ -141,6 +122,47 @@ local function register_egg(name, def) | ||||
| 	}) | ||||
| end | ||||
| 
 | ||||
| local function mob_storage_use(itemstack, player, pointed) | ||||
| 	local ent = pointed.ref and pointed.ref:get_luaentity() | ||||
| 	if ent | ||||
| 	and (ent.name:match("^animalia:") | ||||
| 	or ent.name:match("^monstrum:")) then | ||||
| 		local desc = itemstack:get_short_description() | ||||
| 		if itemstack:get_count() > 1 then | ||||
| 			local name = itemstack:get_name() | ||||
| 			local inv = player:get_inventory() | ||||
| 			if inv:room_for_item("main", {name = name}) then | ||||
| 				itemstack:take_item(1) | ||||
| 				inv:add_item("main", name) | ||||
| 			end | ||||
| 			return itemstack | ||||
| 		end | ||||
| 		local plyr_name = player:get_player_name() | ||||
| 		local meta = itemstack:get_meta() | ||||
| 		local mob = meta:get_string("mob") or "" | ||||
| 		local staticdata = meta:get_string("staticdata") or "" | ||||
| 		if mob == "" then | ||||
| 			animalia.protect_from_despawn(ent) | ||||
| 			meta:set_string("mob", ent.name) | ||||
| 			meta:set_string("staticdata", ent:get_staticdata()) | ||||
| 			local ent_name = correct_name(ent.name) | ||||
| 			local ent_gender = correct_name(ent.gender) | ||||
| 			desc = desc .. " \n" .. color("#a9a9a9", ent_name) .. "\n" .. color("#a9a9a9", ent_gender) | ||||
| 			if ent.trust | ||||
| 			and ent.trust[plyr_name] then | ||||
| 				desc = desc .. "\n Trust: " .. color("#a9a9a9", ent.trust[plyr_name]) | ||||
| 			end | ||||
| 			meta:set_string("description", desc) | ||||
| 			player:set_wielded_item(itemstack) | ||||
| 			ent.object:remove() | ||||
| 			return itemstack | ||||
| 		else | ||||
| 			minetest.chat_send_player(plyr_name, | ||||
| 				"This " .. desc .. " already contains a " .. correct_name(mob)) | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| ----------- | ||||
| -- Drops -- | ||||
| ----------- | ||||
| @ -199,6 +221,26 @@ minetest.register_craft({ | ||||
| 	output = "animalia:mutton_cooked", | ||||
| }) | ||||
| 
 | ||||
| minetest.register_craftitem("animalia:rat_raw", { | ||||
| 	description = "Raw Rat", | ||||
| 	inventory_image = "animalia_rat_raw.png", | ||||
| 	on_use = minetest.item_eat(1), | ||||
| 	groups = {flammable = 2, meat = 1, food_meat = 1}, | ||||
| }) | ||||
| 
 | ||||
| minetest.register_craftitem("animalia:rat_cooked", { | ||||
| 	description = "Cooked Rat", | ||||
| 	inventory_image = "animalia_rat_cooked.png", | ||||
| 	on_use = minetest.item_eat(2), | ||||
| 	groups = {flammable = 2, meat = 1, food_meat = 1}, | ||||
| }) | ||||
| 
 | ||||
| minetest.register_craft({ | ||||
| 	type  =  "cooking", | ||||
| 	recipe  = "animalia:rat_raw", | ||||
| 	output = "animalia:rat_cooked", | ||||
| }) | ||||
| 
 | ||||
| minetest.register_craftitem("animalia:porkchop_raw", { | ||||
| 	description = "Raw Porkchop", | ||||
| 	inventory_image = "animalia_porkchop_raw.png", | ||||
| @ -480,48 +522,7 @@ minetest.register_craftitem("animalia:net", { | ||||
| 	description = "Animal Net", | ||||
| 	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 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_name = correct_name(ent.name) | ||||
| 			local ent_gender = correct_name(ent.gender) | ||||
| 			local meta = itemstack:get_meta() | ||||
| 			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" .. color("#a9a9a9", ent_name) .. "\n" .. color("#a9a9a9", ent_gender) | ||||
| 				if ent.name == "animalia:cat" | ||||
| 				and ent.trust | ||||
| 				and ent.trust[placer:get_player_name()] then | ||||
| 					desc = desc .. "\n Trust: " .. color("#a9a9a9", ent.trust[placer:get_player_name()]) | ||||
| 				end | ||||
| 				meta:set_string("description", desc) | ||||
| 				placer:set_wielded_item(itemstack) | ||||
| 				animalia.protect_from_despawn(ent) | ||||
| 				ent.object:remove() | ||||
| 				return itemstack | ||||
| 			else | ||||
| 				minetest.chat_send_player(placer:get_player_name(), | ||||
| 										  "This Net already contains a " .. | ||||
| 											  correct_name( | ||||
| 												  meta:get_string("mob"))) | ||||
| 				return | ||||
| 			end | ||||
| 		end | ||||
| 	end, | ||||
| 	on_secondary_use = mob_storage_use, | ||||
| 	on_place = function(itemstack, placer, pointed_thing) | ||||
| 		local pos = pointed_thing.above | ||||
| 		if pos then | ||||
| @ -580,6 +581,61 @@ minetest.register_node("animalia:guano", { | ||||
| 		end | ||||
| 	end | ||||
| }) | ||||
| 
 | ||||
| minetest.register_node("animalia:crate", { | ||||
| 	description = "Animal Crate", | ||||
| 	tiles = {"animalia_crate.png", "animalia_crate.png", "animalia_crate_side.png"}, | ||||
| 	groups = {choppy = 2}, | ||||
| 	stack_max = 1, | ||||
| 	on_secondary_use = mob_storage_use, | ||||
| 	preserve_metadata = function(_, _, oldmeta, drops) | ||||
| 		for _, stack in pairs(drops) do | ||||
| 			if stack:get_name() == "animalia:crate" then | ||||
| 				local meta = stack:get_meta() | ||||
| 				meta:set_string("mob", oldmeta["mob"]) | ||||
| 				meta:set_string("staticdata", oldmeta["staticdata"]) | ||||
| 				meta:set_string("description", oldmeta["description"]) | ||||
| 			end | ||||
| 		end | ||||
| 	end, | ||||
| 	after_place_node = function(pos, placer, itemstack) | ||||
| 		local meta = itemstack:get_meta() | ||||
| 		local mob = meta:get_string("mob") | ||||
| 		if mob ~= "" then | ||||
| 			local nmeta = minetest.get_meta(pos) | ||||
| 			nmeta:set_string("mob", mob) | ||||
| 			nmeta:set_string("infotext", "Contains a " .. correct_name((mob))) | ||||
| 			nmeta:set_string("staticdata", meta:get_string("staticdata")) | ||||
| 			nmeta:set_string("description", meta:get_string("description")) | ||||
| 			itemstack:take_item() | ||||
| 			placer:set_wielded_item(itemstack) | ||||
| 		end | ||||
| 	end, | ||||
| 	on_rightclick = function(pos, _, clicker) | ||||
| 		if minetest.is_protected(pos, clicker:get_player_name()) then | ||||
| 			return | ||||
| 		end | ||||
| 		local meta = minetest.get_meta(pos) | ||||
| 		local mob = meta:get_string("mob") | ||||
| 		local staticdata = meta:get_string("staticdata") | ||||
| 		if mob ~= "" then | ||||
| 			local above = { | ||||
| 				x = pos.x, | ||||
| 				y = pos.y + 1, | ||||
| 				z = pos.z | ||||
| 			} | ||||
| 			if creatura.get_node_def(above).walkable then | ||||
| 				return | ||||
| 			end | ||||
| 			minetest.add_entity(above, mob, staticdata) | ||||
| 			meta:set_string("mob", nil) | ||||
| 			meta:set_string("infotext", nil) | ||||
| 			meta:set_string("staticdata", nil) | ||||
| 			meta:set_string("description", "Animal Crate") | ||||
| 		end | ||||
| 	end | ||||
| }) | ||||
| 
 | ||||
| -------------- | ||||
| -- Crafting -- | ||||
| -------------- | ||||
| @ -652,6 +708,15 @@ minetest.register_craft({ | ||||
| 	} | ||||
| }) | ||||
| 
 | ||||
| minetest.register_craft({ | ||||
| 	output = "animalia:crate", | ||||
| 	recipe = { | ||||
| 		{"group:wood", "group:wood", "group:wood"}, | ||||
| 		{"group:wood", "animalia:net", "group:wood"}, | ||||
| 		{"group:wood", "group:wood", "group:wood"} | ||||
| 	} | ||||
| }) | ||||
| 
 | ||||
| minetest.register_craft({ | ||||
| 	output = "animalia:saddle", | ||||
| 	recipe = { | ||||
|  | ||||
							
								
								
									
										5
									
								
								init.lua
									
									
									
									
									
								
							
							
						
						| @ -42,13 +42,16 @@ animalia.animals = { | ||||
| 	"animalia:cat", | ||||
| 	"animalia:chicken", | ||||
| 	"animalia:cow", | ||||
| 	"animalia:tropical_fish", | ||||
| 	"animalia:fox", | ||||
| 	"animalia:frog", | ||||
| 	"animalia:horse", | ||||
| 	"animalia:owl", | ||||
| 	"animalia:pig", | ||||
| 	"animalia:rat", | ||||
| 	"animalia:reindeer", | ||||
| 	"animalia:sheep", | ||||
| 	"animalia:turkey", | ||||
| 	"animalia:tropical_fish", | ||||
| 	"animalia:wolf", | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										277
									
								
								mobs/bat.lua
									
									
									
									
									
								
							
							
						
						| @ -2,119 +2,76 @@ | ||||
| -- Bat -- | ||||
| --------- | ||||
| 
 | ||||
| local guano_accumulation = minetest.settings:get_bool("guano_accumulation") | ||||
| 
 | ||||
| -- Math -- | ||||
| 
 | ||||
| local function clamp(val, min, max) | ||||
| 	if val < min then | ||||
| 		val = min | ||||
| 	elseif max < val then | ||||
| 		val = max | ||||
| 	end | ||||
| 	return val | ||||
| end | ||||
| 
 | ||||
| local random = math.random | ||||
| local floor = math.floor | ||||
| 
 | ||||
| -- Vector Math -- | ||||
| 
 | ||||
| local vec_dist = vector.distance | ||||
| local vec_add = vector.add | ||||
| 
 | ||||
| local function vec_raise(v, n) | ||||
| 	return {x = v.x, y = v.y + n, z = v.z} | ||||
| end | ||||
| 
 | ||||
| --------------- | ||||
| -- Utilities -- | ||||
| --------------- | ||||
| 
 | ||||
| local function get_roost(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 | ||||
| local function get_home_pos(self) | ||||
| 	local pos = self.object:get_pos() | ||||
| 	if not pos then return end | ||||
| 	local nodes = minetest.find_nodes_in_area( | ||||
| 		vector.subtract(pos, 16), | ||||
| 		vector.add(pos, 16), | ||||
| 		{"group:leaves", "group:stone"} | ||||
| 	) | ||||
| 	if #walkable < 1 then return end | ||||
| 	local roosts = {} | ||||
| 	for i = 1, #walkable do | ||||
| 		local i_pos = walkable[i] | ||||
| 		local n_pos = { | ||||
| 			x = i_pos.x, | ||||
| 			y = i_pos.y - 1, | ||||
| 			z = i_pos.z | ||||
| 		} | ||||
| 		if creatura.get_node_def(n_pos).name == "air" | ||||
| 		and minetest.line_of_sight(pos, n_pos) then | ||||
| 			table.insert(roosts, n_pos) | ||||
| 	local home_dist | ||||
| 	local new_home | ||||
| 	for _, n_pos in ipairs(nodes or {}) do | ||||
| 		local dist = vec_dist(pos, n_pos) | ||||
| 		if not home_dist | ||||
| 		or dist < home_dist then | ||||
| 			n_pos.y = n_pos.y - 1 | ||||
| 			if creatura.get_node_def(n_pos).name == "air" then | ||||
| 				home_dist = dist | ||||
| 				new_home = n_pos | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	return roosts[random(#roosts)] | ||||
| end | ||||
| 
 | ||||
| local function is_node_walkable(name) | ||||
| 	local def = minetest.registered_nodes[name] | ||||
| 	return def and def.walkable | ||||
| 	if new_home then | ||||
| 		self.home_position = self:memorize("home_position", new_home) | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| creatura.register_mob("animalia:bat", { | ||||
| 	-- Stats | ||||
| 	max_health = 5, | ||||
| 	armor_groups = {fleshy = 200}, | ||||
| 	damage = 0, | ||||
| 	speed = 4, | ||||
| 	tracking_range = 16, | ||||
| 	despawn_after = 2500, | ||||
| 	-- Entity Physics | ||||
| 	stepheight = 1.1, | ||||
| 	max_fall = 0, | ||||
| 	turn_rate = 12, | ||||
| 	-- Visuals | ||||
| 	-- Engine Props | ||||
| 	visual = "mesh", | ||||
| 	visual_size = {x = 10, y = 10}, | ||||
| 	mesh = "animalia_bat.b3d", | ||||
| 	textures = { | ||||
| 		"animalia_bat_1.png", | ||||
| 		"animalia_bat_2.png", | ||||
| 		"animalia_bat_3.png", | ||||
| 	}, | ||||
| 	makes_footstep_sound = false, | ||||
| 	stepheight = 1.1, | ||||
| 	-- Creatura Props | ||||
| 	max_health = 5, | ||||
| 	armor_groups = {fleshy = 100}, | ||||
| 	damage = 0, | ||||
| 	speed = 6, | ||||
| 	tracking_range = 8, | ||||
| 	max_boids = 2, | ||||
| 	despawn_after = 500, | ||||
| 	max_fall = 0, | ||||
| 	hitbox = { | ||||
| 		width = 0.15, | ||||
| 		height = 0.3 | ||||
| 	}, | ||||
| 	visual_size = {x = 7, y = 7}, | ||||
| 	textures = { | ||||
| 		"animalia_bat_1.png", | ||||
| 		"animalia_bat_2.png", | ||||
| 		"animalia_bat_3.png" | ||||
| 	}, | ||||
| 	animations = { | ||||
| 		stand = {range = {x = 1, y = 40}, speed = 10, frame_blend = 0.3, loop = true}, | ||||
| 		walk = {range = {x = 50, y = 90}, speed = 30, frame_blend = 0.3, loop = true}, | ||||
| 		fly = {range = {x = 100, y = 140}, speed = 80, frame_blend = 0.3, loop = true}, | ||||
| 		cling = {range = {x = 150, y = 150}, speed = 1, frame_blend = 0, loop = false} | ||||
| 		walk = {range = {x = 51, y = 69}, speed = 30, frame_blend = 0.3, loop = true}, | ||||
| 		fly = {range = {x = 81, y = 99}, speed = 80, frame_blend = 0.3, loop = true}, | ||||
| 		cling = {range = {x = 110, y = 110}, speed = 1, frame_blend = 0, loop = false} | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	sounds = { | ||||
| 		random = { | ||||
| 			name = "animalia_bat", | ||||
| 			gain = 0.5, | ||||
| 			distance = 16, | ||||
| 			variations = 2 | ||||
| 		}, | ||||
| 	}, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = false, | ||||
| 	follow = { | ||||
| 		"butterflies:butterfly_red", | ||||
| 		"butterflies:butterfly_white", | ||||
| 		"butterflies:butterfly_violet" | ||||
| 	}, | ||||
| 	-- Function | ||||
| 	fancy_collide = false, | ||||
| 	bouyancy_multiplier = 1, | ||||
| 	hydrodynamics_multiplier = 1, | ||||
| 
 | ||||
| 	roost_action = animalia.action_cling, | ||||
| 	utility_stack = { | ||||
| 		{ | ||||
| 			utility = "animalia:wander", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				return 0.1, {self} | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:aerial_wander", | ||||
| 			step_delay = 0.25, | ||||
| @ -128,119 +85,101 @@ creatura.register_mob("animalia:bat", { | ||||
| 					local dist = vec_dist(pos, plyr_pos) | ||||
| 					self._target = player | ||||
| 					self.is_landed = false | ||||
| 					return (12 - (dist + trust)) * 0.1, {self} | ||||
| 					return (self.tracking_range - dist) / self.tracking_range, {self} | ||||
| 				end | ||||
| 				if self.in_liquid | ||||
| 				or not self.is_landed then | ||||
| 					return 0.2, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 				return 0.1, {self} | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:fly_to_land", | ||||
| 			utility = "animalia:swim_to_land", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				if self.is_landed | ||||
| 				and not self.touching_ground | ||||
| 				and not self.in_liquid | ||||
| 				and creatura.sensor_floor(self, 3, true) > 2 then | ||||
| 				if self.in_liquid then | ||||
| 					return 0.3, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		[4] = { | ||||
| 		{ | ||||
| 			utility = "animalia:fly_to_roost", | ||||
| 			get_score = function(self) | ||||
| 				local pos = self.object:get_pos() | ||||
| 				if not pos then return end | ||||
| 				local home = animalia.is_day and self.home_position | ||||
| 				if home | ||||
| 				if (home | ||||
| 				and home.x | ||||
| 				and vec_dist(pos, home) < 8 then | ||||
| 				and vec_dist(pos, home) < 8) | ||||
| 				or self.is_landed then | ||||
| 					return 0.6, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 	}, | ||||
| 	-- Animalia Props | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = false, | ||||
| 	-- Functions | ||||
| 	is_home = function(pos, home_pos) | ||||
| 		local dist = vec_dist(pos, home_pos) | ||||
| 		if dist < 4 then | ||||
| 			local above = {x = pos.x, y = pos.y + 1, z = pos.z} | ||||
| 			if creatura.get_node_def(above).walkable | ||||
| 			or dist < 1 then | ||||
| 				return true | ||||
| 			end | ||||
| 		end | ||||
| 		return false | ||||
| 	end, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| 		animalia.initialize_lasso(self) | ||||
| 		self.home_position = self:recall("home_position") or nil | ||||
| 		local home_pos = self.home_position | ||||
| 		self.is_landed = self:recall("is_landed") or false | ||||
| 		self.trust = self:recall("trust") or {} | ||||
| 		if not self.home_position then | ||||
| 			local roost = get_roost(self.object:get_pos(), 8) | ||||
| 			if roost then | ||||
| 				self.home_position = self:memorize("home_position", roost) | ||||
| 			end | ||||
| 		if not home_pos | ||||
| 		or not creatura.get_node_def(home_pos).walkable then | ||||
| 			get_home_pos(self) | ||||
| 		end | ||||
| 	end, | ||||
| 	step_func = function(self) | ||||
| 		animalia.step_timers(self) | ||||
| 		--animalia.head_tracking(self, 0.75, 0.75) | ||||
| 		animalia.do_growth(self, 60) | ||||
| 		animalia.update_lasso_effects(self) | ||||
| 		animalia.rotate_to_pitch(self) | ||||
| 		local pos = self.object:get_pos() | ||||
| 		if not pos then return end | ||||
| 		if self:timer(random(10,15)) then | ||||
| 			if random(4) < 2 then | ||||
| 				self.is_landed = not self.is_landed | ||||
| 			end | ||||
| 			if not self.home_position | ||||
| 			or creatura.get_node_def(self.home_position).walkable then | ||||
| 				local roost = get_roost(pos, 8) | ||||
| 				if roost then | ||||
| 					self.home_position = self:memorize("home_position", roost) | ||||
| 				end | ||||
| 			end | ||||
| 		animalia.random_sound(self) | ||||
| 		if not self.is_landed | ||||
| 		or not self.touching_ground then | ||||
| 			self.speed = 4 | ||||
| 		else | ||||
| 			self.speed = 1 | ||||
| 		end | ||||
| 		if self._anim == "fly" then | ||||
| 			local vel_y = vector.normalize(self.object:get_velocity()).y | ||||
| 			local rot = self.object:get_rotation() | ||||
| 			local n_rot = rot.x + (vel_y - rot.x) * 0.2 | ||||
| 			self.object:set_rotation({ | ||||
| 				x = clamp(n_rot, -0.75, 0.75), | ||||
| 				y = rot.y, | ||||
| 				z = rot.z | ||||
| 			}) | ||||
| 		end | ||||
| 		if self:timer(random(3, 4)) then | ||||
| 			self:play_sound("random") | ||||
| 			if guano_accumulation | ||||
| 			and random(16) < 2 | ||||
| 			and self:get_utility() == "animalia:fly_to_roost" then | ||||
| 				pos = { | ||||
| 					x = floor(pos.x + 0.5), | ||||
| 					y = floor(pos.y + 0.5), | ||||
| 					z = floor(pos.z + 0.5) | ||||
| 				} | ||||
| 				if not is_node_walkable(minetest.get_node(vec_raise(pos, 1)).name) then | ||||
| 					return | ||||
| 				end | ||||
| 				local fail_safe = 1 | ||||
| 				while not is_node_walkable(minetest.get_node(pos).name) | ||||
| 				and fail_safe < 16 do | ||||
| 					pos.y = pos.y - 1 | ||||
| 				end | ||||
| 				if is_node_walkable(minetest.get_node(pos).name) then | ||||
| 					if minetest.get_node(vec_raise(pos, 1)).name ~= "animalia:guano" then | ||||
| 						minetest.set_node(vec_raise(pos, 1), {name = "animalia:guano"}) | ||||
| 					else | ||||
| 						local nodes = minetest.find_nodes_in_area_under_air( | ||||
| 							vector.subtract(pos, 3), | ||||
| 							vec_add(pos, 3), | ||||
| 							animalia.walkable_nodes | ||||
| 						) | ||||
| 						if #nodes > 0 then | ||||
| 							pos = nodes[random(#nodes)] | ||||
| 							if minetest.get_node(vec_raise(pos, 1)).name ~= "animalia:guano" then | ||||
| 								minetest.set_node(vec_raise(pos, 1), {name = "animalia:guano"}) | ||||
| 							end | ||||
| 		if self:timer(10) | ||||
| 		and math.random(10) < 2 then | ||||
| 			local anim = self._anim or "" | ||||
| 			if anim == "cling" then | ||||
| 				local colony = creatura.get_nearby_objects(self, self.name) | ||||
| 				local pos = self.object:get_pos() | ||||
| 				if not pos then return end | ||||
| 				local center = pos | ||||
| 				if #colony > 0 then | ||||
| 					local pos_sum = center | ||||
| 					local pos_ttl = 1 | ||||
| 					for _, object in ipairs(colony) do | ||||
| 						local obj_pos = object and object:get_pos() | ||||
| 						if obj_pos then | ||||
| 							pos_sum = vector.add(pos_sum, obj_pos) | ||||
| 							pos_ttl = pos_ttl + 1 | ||||
| 						end | ||||
| 					end | ||||
| 					center = vector.divide(pos_sum, pos_ttl) | ||||
| 				end | ||||
| 				center = creatura.get_ground_level(center, 8) | ||||
| 				if center.y < pos.y then | ||||
| 					local under = {x = center.x, y = center.y - 1, z = center.z} | ||||
| 					if creatura.get_node_def(under).walkable | ||||
| 					and not minetest.is_protected(center, "") then | ||||
| 						minetest.set_node(center, {name = "animalia:guano"}) | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| @ -259,9 +198,7 @@ creatura.register_mob("animalia:bat", { | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:bat", "392517", "321b0b") | ||||
| @ -24,7 +24,7 @@ creatura.register_mob("animalia:bird", { | ||||
| 	damage = 0, | ||||
| 	speed = 4, | ||||
| 	tracking_range = 16, | ||||
| 	despawn_after = 100, | ||||
| 	despawn_after = 750, | ||||
| 	-- Entity Physics | ||||
| 	stepheight = 1.1, | ||||
| 	max_fall = 0, | ||||
| @ -48,6 +48,7 @@ creatura.register_mob("animalia:bird", { | ||||
| 		fly = {range = {x = 120, y = 140}, speed = 80, frame_blend = 0.3, loop = true} | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	max_boids = 12, | ||||
| 	makes_footstep_sound = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = false, | ||||
| @ -73,7 +74,7 @@ creatura.register_mob("animalia:bird", { | ||||
| 	}, | ||||
| 	follow = follows, | ||||
| 	-- Function | ||||
| 	wander_action = animalia.action_move_flock, | ||||
| 	wander_action = animalia.action_move_boid, | ||||
| 	utility_stack = { | ||||
| 		{ | ||||
| 			utility = "animalia:wander_group", | ||||
| @ -155,7 +156,7 @@ creatura.register_mob("animalia:bird", { | ||||
| 		animalia.do_growth(self, 60) | ||||
| 		animalia.update_lasso_effects(self) | ||||
| 		animalia.rotate_to_pitch(self) | ||||
| 		if self:timer(random(10,15)) then | ||||
| 		if self:timer(random(6,12)) then | ||||
| 			if animalia.is_day then | ||||
| 				if self.texture_no == 1 then | ||||
| 					self:play_sound("cardinal") | ||||
| @ -213,9 +214,7 @@ creatura.register_mob("animalia:bird", { | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:bird", "ae2f2f", "f3ac1c") | ||||
|  | ||||
							
								
								
									
										20
									
								
								mobs/cat.lua
									
									
									
									
									
								
							
							
						
						| @ -34,7 +34,7 @@ creatura.register_mob("animalia:cat", { | ||||
| 		width = 0.2, | ||||
| 		height = 0.4 | ||||
| 	}, | ||||
| 	visual_size = {x = 6, y = 6}, | ||||
| 	visual_size = {x = 10, y = 10}, | ||||
| 	textures = { | ||||
| 		"animalia_cat_1.png", | ||||
| 		"animalia_cat_2.png", | ||||
| @ -58,6 +58,7 @@ creatura.register_mob("animalia:cat", { | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	sounds = { | ||||
| @ -179,7 +180,8 @@ creatura.register_mob("animalia:cat", { | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso = type(self.lasso_origin or {}) == "userdata" and self.lasso_origin | ||||
| 				local lasso_tgt = self._lassod_to | ||||
| 				local lasso = type(lasso_tgt) == "string" and minetest.get_player_by_name(lasso_tgt) | ||||
| 				local trust = (self.owner and self.trust[self.owner]) or 0 | ||||
| 				local owner = self.owner and self.order == "follow" and trust > 4 and minetest.get_player_by_name(self.owner) | ||||
| 				local force = (lasso and lasso ~= false) or (owner and owner ~= false) | ||||
| @ -191,13 +193,25 @@ creatura.register_mob("animalia:cat", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:attack_target", | ||||
| 			get_score = function(self) | ||||
| 				local target = self._target or creatura.get_nearby_object(self, "animalia:rat") | ||||
| 				local tgt_pos = target and target:get_pos() | ||||
| 				if tgt_pos | ||||
| 				and self:is_pos_safe(tgt_pos) then | ||||
| 					return 0.7, {self, target} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				if self.breeding | ||||
| 				and animalia.get_nearby_mate(self, self.name) then | ||||
| 					return 0.7, {self} | ||||
| 					return 0.8, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
|  | ||||
| @ -52,6 +52,7 @@ creatura.register_mob("animalia:chicken", { | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	sounds = { | ||||
| @ -124,19 +125,7 @@ creatura.register_mob("animalia:chicken", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso = type(self.lasso_origin or {}) == "userdata" and self.lasso_origin | ||||
| 				local force = lasso and lasso ~= false | ||||
| 				local player = (force and lasso) or creatura.get_nearby_player(self) | ||||
| 				if player | ||||
| 				and self:follow_wielded_item(player) then | ||||
| 					return 0.3, {self, player} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		animalia.global_utils.basic_follow, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			step_delay = 0.25, | ||||
| @ -148,18 +137,7 @@ creatura.register_mob("animalia:chicken", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:flee_from_target", | ||||
| 			get_score = function(self) | ||||
| 				local puncher = self._target | ||||
| 				if puncher | ||||
| 				and puncher:get_pos() then | ||||
| 					return 0.6, {self, puncher} | ||||
| 				end | ||||
| 				self._target = nil | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 		animalia.global_utils.basic_flee | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| @ -194,10 +172,7 @@ creatura.register_mob("animalia:chicken", { | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		self._target = puncher | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:chicken", "c6c6c6", "d22222") | ||||
							
								
								
									
										34
									
								
								mobs/cow.lua
									
									
									
									
									
								
							
							
						
						| @ -59,7 +59,7 @@ creatura.register_mob("animalia:cow", { | ||||
| 		run = {range = {x = 61, y = 79}, speed = 30, frame_blend = 0.3, loop = true}, | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	step_delay = 0.25, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	sounds = { | ||||
| @ -123,19 +123,7 @@ creatura.register_mob("animalia:cow", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso = type(self.lasso_origin or {}) == "userdata" and self.lasso_origin | ||||
| 				local force = lasso and lasso ~= false | ||||
| 				local player = (force and lasso) or creatura.get_nearby_player(self) | ||||
| 				if player | ||||
| 				and self:follow_wielded_item(player) then | ||||
| 					return 0.4, {self, player} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		animalia.global_utils.basic_follow, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			step_delay = 0.25, | ||||
| @ -147,18 +135,7 @@ creatura.register_mob("animalia:cow", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:flee_from_target", | ||||
| 			get_score = function(self) | ||||
| 				local puncher = self._target | ||||
| 				if puncher | ||||
| 				and puncher:get_pos() then | ||||
| 					return 0.6, {self, puncher} | ||||
| 				end | ||||
| 				self._target = nil | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 		animalia.global_utils.basic_flee | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| @ -215,10 +192,7 @@ creatura.register_mob("animalia:cow", { | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		self._target = puncher | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:cow", "cac3a1" ,"464438") | ||||
							
								
								
									
										169
									
								
								mobs/fox.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,169 @@ | ||||
| ---------- | ||||
| -- Wolf -- | ||||
| ---------- | ||||
| 
 | ||||
| creatura.register_mob("animalia:fox", { | ||||
| 	-- Stats | ||||
| 	max_health = 15, | ||||
| 	armor_groups = {fleshy = 100}, | ||||
| 	damage = 4, | ||||
| 	speed = 5, | ||||
| 	tracking_range = 24, | ||||
| 	despawn_after = 2000, | ||||
| 	-- Entity Physics | ||||
| 	stepheight = 1.1, | ||||
| 	max_fall = 3, | ||||
| 	-- Visuals | ||||
| 	mesh = "animalia_fox.b3d", | ||||
| 	hitbox = { | ||||
| 		width = 0.35, | ||||
| 		height = 0.7 | ||||
| 	}, | ||||
| 	visual_size = {x = 10, y = 10}, | ||||
| 	textures = { | ||||
| 		"animalia_fox_1.png", | ||||
| 	}, | ||||
| 	animations = { | ||||
| 		stand = {range = {x = 1, y = 39}, speed = 10, frame_blend = 0.3, loop = true}, | ||||
| 		walk = {range = {x = 41, y = 59}, speed = 30, frame_blend = 0.3, loop = true}, | ||||
| 		run = {range = {x = 41, y = 59}, speed = 45, frame_blend = 0.3, loop = true}, | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	follow = { | ||||
| 		"animalia:rat_raw", | ||||
| 		"animalia:mutton_raw", | ||||
| 		"animalia:beef_raw", | ||||
| 		"animalia:porkchop_raw", | ||||
| 		"animalia:poultry_raw" | ||||
| 	}, | ||||
| 	head_data = { | ||||
| 		offset = {x = 0, y = 0.18, z = 0}, | ||||
| 		pitch_correction = -67, | ||||
| 		pivot_h = 0.65, | ||||
| 		pivot_v = 0.65 | ||||
| 	}, | ||||
| 	-- Function | ||||
| 	on_eat_drop = function(self) | ||||
| 		animalia.protect_from_despawn(self) | ||||
| 	end, | ||||
| 	utility_stack = { | ||||
| 		{ | ||||
| 			utility = "animalia:wander_skittish", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				return 0.1, {self} | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:swim_to_land", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				if self.in_liquid then | ||||
| 					return 0.3, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:attack_target", | ||||
| 			get_score = function(self) | ||||
| 				local target = self._target or creatura.get_nearby_object(self, {"animalia:rat", "animalia:chicken"}) | ||||
| 				local tgt_pos = target and target:get_pos() | ||||
| 				if tgt_pos | ||||
| 				and self:is_pos_safe(tgt_pos) then | ||||
| 					return 0.4, {self, target} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:flee_from_target", | ||||
| 			get_score = function(self) | ||||
| 				local target = self._puncher or creatura.get_nearby_player(self) | ||||
| 				local pos, tgt_pos = self.object:get_pos(), target and target:get_pos() | ||||
| 				if not pos then return end | ||||
| 				if not tgt_pos then self._puncher = nil return 0 end | ||||
| 				local sneaking = target:get_player_control().sneak | ||||
| 				if not sneaking then | ||||
| 					local dist = vector.distance(pos, tgt_pos) | ||||
| 					local score = (self.tracking_range - dist) / self.tracking_range | ||||
| 					self._puncher = target | ||||
| 					return score / 2, {self, target} | ||||
| 				end | ||||
| 				self._puncher = nil | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:walk_to_food", | ||||
| 			get_score = function(self) | ||||
| 				local cooldown = self.eat_cooldown or 0 | ||||
| 				if cooldown > 0 then | ||||
| 					self.eat_cooldown = cooldown - 1 | ||||
| 					return 0 | ||||
| 				end | ||||
| 				local food_item = animalia.get_dropped_food(self) | ||||
| 				if food_item then | ||||
| 					return 0.7, {self, food_item} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso_tgt = self._lassod_to | ||||
| 				local lasso = type(lasso_tgt) == "string" and minetest.get_player_by_name(lasso_tgt) | ||||
| 				if lasso | ||||
| 				and lasso:get_pos() then | ||||
| 					return 0.6, {self, lasso, true} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				if self.breeding | ||||
| 				and animalia.get_nearby_mate(self, self.name) then | ||||
| 					return 0.7, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| 		animalia.initialize_lasso(self) | ||||
| 	end, | ||||
| 	step_func = function(self) | ||||
| 		animalia.step_timers(self) | ||||
| 		animalia.head_tracking(self, 0.5, 0.75) | ||||
| 		animalia.do_growth(self, 60) | ||||
| 		animalia.update_lasso_effects(self) | ||||
| 	end, | ||||
| 	death_func = function(self) | ||||
| 		if self:get_utility() ~= "animalia:die" then | ||||
| 			self:initiate_utility("animalia:die", self) | ||||
| 		end | ||||
| 	end, | ||||
| 	on_rightclick = function(self, clicker) | ||||
| 		if not clicker:is_player() then return end | ||||
| 		local name = clicker:get_player_name() | ||||
| 		local passive = true | ||||
| 		if animalia.feed(self, clicker, passive, passive) then | ||||
| 			return | ||||
| 		end | ||||
| 		if animalia.set_nametag(self, clicker) then | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:fox", "d0602d" ,"c9c9c9") | ||||
| @ -45,6 +45,7 @@ creatura.register_mob("animalia:frog", { | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	sounds = { | ||||
| @ -128,7 +129,7 @@ creatura.register_mob("animalia:frog", { | ||||
| 				if self.in_liquid then return 0 end | ||||
| 				local pos = self.object:get_pos() | ||||
| 				if not pos then return end | ||||
| 				local target = self._target or creatura.get_nearby_player(self) | ||||
| 				local target = self._puncher or self._target or creatura.get_nearby_player(self) | ||||
| 				local tgt_pos = target and target:get_pos() | ||||
| 				local plyr_name = (target and target:is_player() and target:get_player_name()) or "" | ||||
| 				if tgt_pos then | ||||
|  | ||||
| @ -100,15 +100,15 @@ creatura.register_mob("animalia:horse", { | ||||
| 	}, | ||||
| 	animations = { | ||||
| 		stand = {range = {x = 1, y = 59}, speed = 10, frame_blend = 0.3, loop = true}, | ||||
| 		walk = {range = {x = 61, y = 79}, speed = 20, frame_blend = 0.3, loop = true}, | ||||
| 		run = {range = {x = 81, y = 99}, speed = 30, frame_blend = 0.3, loop = true}, | ||||
| 		punch_aoe = {range = {x = 101, y = 119}, speed = 30, frame_blend = 0.2, loop = false}, | ||||
| 		rear = {range = {x = 121, y = 140}, speed = 20, frame_blend = 0.2, loop = false}, | ||||
| 		rear_constant = {range = {x = 121, y = 140}, speed = 20, frame_blend = 0.3, loop = false}, | ||||
| 		eat = {range = {x = 141, y = 160}, speed = 20, frame_blend = 0.3, loop = false} | ||||
| 		walk = {range = {x = 71, y = 89}, speed = 25, frame_blend = 0.3, loop = true}, | ||||
| 		run = {range = {x = 101, y = 119}, speed = 40, frame_blend = 0.3, loop = true}, | ||||
| 		punch_aoe = {range = {x = 161, y = 180}, speed = 30, frame_blend = 0.2, loop = false}, | ||||
| 		rear = {range = {x = 131, y = 150}, speed = 20, frame_blend = 0.2, loop = false}, | ||||
| 		eat = {range = {x = 191, y = 220}, speed = 30, frame_blend = 0.3, loop = false} | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	sounds = { | ||||
| @ -116,8 +116,7 @@ creatura.register_mob("animalia:horse", { | ||||
| 		random = { | ||||
| 			name = "animalia_horse_idle", | ||||
| 			gain = 1.0, | ||||
| 			distance = 8, | ||||
| 			variations = 3, | ||||
| 			distance = 8 | ||||
| 		}, | ||||
| 		hurt = { | ||||
| 			name = "animalia_horse_hurt", | ||||
| @ -140,10 +139,10 @@ creatura.register_mob("animalia:horse", { | ||||
| 	}, | ||||
| 	head_data = { | ||||
| 		bone = "Neck.CTRL", | ||||
| 		offset = {x = 0, y = 1.45, z = 0.0}, | ||||
| 		pitch_correction = 25, | ||||
| 		offset = {x = 0, y = 1.05, z = 0.0}, | ||||
| 		pitch_correction = 35, | ||||
| 		pivot_h = 1, | ||||
| 		pivot_v = 1.5 | ||||
| 		pivot_v = 1.75 | ||||
| 	}, | ||||
| 	-- Function | ||||
| 	add_child = function(self, mate) | ||||
| @ -198,19 +197,7 @@ creatura.register_mob("animalia:horse", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso = type(self.lasso_origin or {}) == "userdata" and self.lasso_origin | ||||
| 				local force = lasso and lasso ~= false | ||||
| 				local player = (force and lasso) or creatura.get_nearby_player(self) | ||||
| 				if player | ||||
| 				and self:follow_wielded_item(player) then | ||||
| 					return 0.4, {self, player} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		animalia.global_utils.basic_follow, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			step_delay = 0.25, | ||||
| @ -263,10 +250,6 @@ creatura.register_mob("animalia:horse", { | ||||
| 		animalia.initialize_lasso(self) | ||||
| 		set_pattern(self) | ||||
| 		self.owner = self:recall("owner") or nil | ||||
| 		if self.owner then | ||||
| 			self._despawn = nil | ||||
| 			self.despawn_after = nil | ||||
| 		end | ||||
| 		self.rider = nil | ||||
| 		self.saddled = self:recall("saddled") or false | ||||
| 		self.max_health = self:recall("max_health") or random(30, 45) | ||||
| @ -333,9 +316,7 @@ creatura.register_mob("animalia:horse", { | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, ...) | ||||
| 		if self.rider and puncher == self.rider then return end | ||||
| 		creatura.basic_punch_func(self, puncher, ...) | ||||
| 		if self.hp < 0 then return end | ||||
| 		self._puncher = puncher | ||||
| 		animalia.punch(self, puncher, ...) | ||||
| 	end | ||||
| }) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										217
									
								
								mobs/owl.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,217 @@ | ||||
| --------- | ||||
| -- Owl -- | ||||
| --------- | ||||
| 
 | ||||
| local follows = {} | ||||
| 
 | ||||
| minetest.register_on_mods_loaded(function() | ||||
| 	for name in pairs(minetest.registered_items) do | ||||
| 		if name:match(":seed_") | ||||
| 		or name:match("_seed") then | ||||
| 			table.insert(follows, name) | ||||
| 		end | ||||
| 	end | ||||
| end) | ||||
| 
 | ||||
| local random = math.random | ||||
| 
 | ||||
| local vec_dist = vector.distance | ||||
| 
 | ||||
| local function get_home_pos(self) | ||||
| 	local pos = self.object:get_pos() | ||||
| 	if not pos then return end | ||||
| 	local leaves = minetest.find_nodes_in_area_under_air( | ||||
| 		vector.subtract(pos, 16), | ||||
| 		vector.add(pos, 16), | ||||
| 		"group:leaves" | ||||
| 	) | ||||
| 	local home_dist | ||||
| 	local new_home | ||||
| 	for _, leaf_pos in ipairs(leaves or {}) do | ||||
| 		local dist = vec_dist(pos, leaf_pos) | ||||
| 		if not home_dist | ||||
| 		or dist < home_dist then | ||||
| 			home_dist = dist | ||||
| 			new_home = leaf_pos | ||||
| 		end | ||||
| 	end | ||||
| 	if new_home then | ||||
| 		new_home.y = new_home.y + 1 | ||||
| 		self.home_position = self:memorize("home_position", new_home) | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| creatura.register_mob("animalia:owl", { | ||||
| 	-- Stats | ||||
| 	max_health = 10, | ||||
| 	armor_groups = {fleshy = 100}, | ||||
| 	damage = 2, | ||||
| 	speed = 5, | ||||
| 	tracking_range = 32, | ||||
| 	despawn_after = 1000, | ||||
| 	-- Entity Physics | ||||
| 	stepheight = 1.1, | ||||
| 	max_fall = 0, | ||||
| 	turn_rate = 4, | ||||
| 	-- Visuals | ||||
| 	mesh = "animalia_owl.b3d", | ||||
| 	hitbox = { | ||||
| 		width = 0.15, | ||||
| 		height = 0.3 | ||||
| 	}, | ||||
| 	visual_size = {x = 10, y = 10}, | ||||
| 	textures = { | ||||
| 		"animalia_owl.png" | ||||
| 	}, | ||||
| 	animations = { | ||||
| 		stand = {range = {x = 1, y = 60}, speed = 20, frame_blend = 0.3, loop = true}, | ||||
| 		fly = {range = {x = 71, y = 89}, speed = 30, frame_blend = 0.3, loop = true}, | ||||
| 		glide = {range = {x = 101, y = 119}, speed = 20, frame_blend = 0.2, loop = true}, | ||||
| 		fly_punch = {range = {x = 131, y = 149}, speed = 20, frame_blend = 0.1, loop = false}, | ||||
| 		eat = {range = {x = 161, y = 179}, speed = 20, frame_blend = 0.1, loop = false} | ||||
| 
 | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = false, | ||||
| 	--sounds = {}, | ||||
| 	follow = {"animalia:rat_raw"}, | ||||
| 	-- Function | ||||
| 	on_eat_drop = function(self) | ||||
| 		animalia.protect_from_despawn(self) | ||||
| 		get_home_pos(self) | ||||
| 	end, | ||||
| 	is_home = function(pos, home_pos) | ||||
| 		if abs(pos.x - home_pos.x) < 0.5 | ||||
| 		and abs(pos.z - home_pos.z) < 0.5 then | ||||
| 			if abs(pos.y - home_pos.y) < 0.75 then | ||||
| 				return true | ||||
| 			else | ||||
| 				local under = {x = home_pos.x, y = home_pos.y, z = home_pos.z} | ||||
| 				local name = minetest.get_node(under).name | ||||
| 				if minetest.get_node_group(name, "leaves") > 0 then | ||||
| 					return true | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		return false | ||||
| 	end, | ||||
| 	wander_action = creatura.action_move, | ||||
| 	utility_stack = { | ||||
| 		{ | ||||
| 			utility = "animalia:aerial_wander", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				if not self.is_landed | ||||
| 				or self.in_liquid then | ||||
| 					return 0.1, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:fly_to_roost", | ||||
| 			get_score = function(self) | ||||
| 				local pos = self.object:get_pos() | ||||
| 				if not pos then return end | ||||
| 				local player = creatura.get_nearby_player(self) | ||||
| 				local plyr_pos = player and player:get_pos() | ||||
| 				if plyr_pos then | ||||
| 					local dist = vector.distance(pos, plyr_pos) | ||||
| 					if dist < 3 then | ||||
| 						return 0 | ||||
| 					end | ||||
| 				end | ||||
| 				local home = animalia.is_day and self.home_position | ||||
| 				if home | ||||
| 				and vec_dist(pos, home) < 8 then | ||||
| 					return 0.6, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:fly_to_food", | ||||
| 			get_score = function(self) | ||||
| 				local cooldown = self.eat_cooldown or 0 | ||||
| 				if cooldown > 0 then | ||||
| 					self.eat_cooldown = cooldown - 1 | ||||
| 					return 0 | ||||
| 				end | ||||
| 				local food_item = animalia.get_dropped_food(self, "animalia:rat_raw") | ||||
| 				if food_item then | ||||
| 					return 0.7, {self, food_item} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:glide_attack_target", | ||||
| 			get_score = function(self) | ||||
| 				local target = self._target or creatura.get_nearby_object(self, {"animalia:rat", "animalia:bird"}) | ||||
| 				local tgt_pos = target and target:get_pos() | ||||
| 				if tgt_pos then | ||||
| 					return 0.4, {self, target} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| 		animalia.initialize_lasso(self) | ||||
| 		self._tp2home = self:recall("_tp2home") or nil | ||||
| 		self.home_position = self:recall("home_position") or nil | ||||
| 		local home_pos = self.home_position | ||||
| 		if self._tp2home | ||||
| 		and home_pos then | ||||
| 			self.object:set_pos(home_pos) | ||||
| 		end | ||||
| 		self.is_landed = self:recall("is_landed") or false | ||||
| 		if not home_pos | ||||
| 		or creatura.get_node_def(home_pos).walkable then | ||||
| 			get_home_pos(self) | ||||
| 		end | ||||
| 	end, | ||||
| 	step_func = function(self) | ||||
| 		animalia.step_timers(self) | ||||
| 		animalia.do_growth(self, 60) | ||||
| 		animalia.update_lasso_effects(self) | ||||
| 		animalia.rotate_to_pitch(self) | ||||
| 		if not self.is_landed | ||||
| 		or not self.touching_ground then | ||||
| 			self.speed = 5 | ||||
| 		else | ||||
| 			self.speed = 1 | ||||
| 		end | ||||
| 	end, | ||||
| 	death_func = function(self) | ||||
| 		if self:get_utility() ~= "animalia:die" then | ||||
| 			self:initiate_utility("animalia:die", self) | ||||
| 		end | ||||
| 	end, | ||||
| 	deactivate_func = function(self) | ||||
| 		if self:get_utility() | ||||
| 		and self:get_utility() == "animalia:fly_to_roost" then | ||||
| 			local pos = self.home_position | ||||
| 			local node = minetest.get_node_or_nil(pos) | ||||
| 			if node | ||||
| 			and not creatura.get_node_def(node.name).walkable | ||||
| 			and minetest.get_natural_light(pos) > 0 then | ||||
| 				self:memorize("_tp2home", true) | ||||
| 			end | ||||
| 		end | ||||
| 	end, | ||||
| 	on_rightclick = function(self, clicker) | ||||
| 		if animalia.feed(self, clicker, false, false) then | ||||
| 			return | ||||
| 		end | ||||
| 		if animalia.set_nametag(self, clicker) then | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:owl", "412918", "735b46") | ||||
							
								
								
									
										33
									
								
								mobs/pig.lua
									
									
									
									
									
								
							
							
						
						| @ -67,11 +67,12 @@ creatura.register_mob("animalia:pig", { | ||||
| 	makes_footstep_sound = true, | ||||
| 	consumable_nodes = destroyable_crops, | ||||
| 	birth_count = 2, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	sounds = { | ||||
| 		random = { | ||||
| 			name = "animalia_pig_idle", | ||||
| 			name = "animalia_pig_random", | ||||
| 			gain = 1.0, | ||||
| 			distance = 8 | ||||
| 		}, | ||||
| @ -92,14 +93,14 @@ creatura.register_mob("animalia:pig", { | ||||
| 	follow = follows, | ||||
| 	-- Function | ||||
| 	utility_stack = { | ||||
| 		[1] = { | ||||
| 		{ | ||||
| 			utility = "animalia:wander", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				return 0.1, {self, true} | ||||
| 			end | ||||
| 		}, | ||||
| 		[2] = { | ||||
| 		{ | ||||
| 			utility = "animalia:eat_from_turf", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| @ -109,7 +110,7 @@ creatura.register_mob("animalia:pig", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		[3] = { | ||||
| 		{ | ||||
| 			utility = "animalia:swim_to_land", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| @ -119,20 +120,8 @@ creatura.register_mob("animalia:pig", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		[4] = { | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso = type(self.lasso_origin or {}) == "userdata" and self.lasso_origin | ||||
| 				local force = lasso and lasso ~= false | ||||
| 				local player = (force and lasso) or creatura.get_nearby_player(self) | ||||
| 				if player | ||||
| 				and self:follow_wielded_item(player) then | ||||
| 					return 0.4, {self, player} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		[5] = { | ||||
| 		animalia.global_utils.basic_follow, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| @ -142,7 +131,8 @@ creatura.register_mob("animalia:pig", { | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 		}, | ||||
| 		animalia.global_utils.basic_flee | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| @ -167,10 +157,7 @@ creatura.register_mob("animalia:pig", { | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		self._target = puncher | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:pig", "e0b1a7" ,"cc9485") | ||||
							
								
								
									
										120
									
								
								mobs/rat.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,120 @@ | ||||
| ---------- | ||||
| -- Mice -- | ||||
| ---------- | ||||
| 
 | ||||
| creatura.register_mob("animalia:rat", { | ||||
| 	-- Stats | ||||
| 	max_health = 5, | ||||
| 	armor_groups = {fleshy = 100}, | ||||
| 	damage = 0, | ||||
| 	speed = 3, | ||||
| 	tracking_range = 12, | ||||
| 	despawn_after = 2500, | ||||
| 	-- Entity Physics | ||||
| 	stepheight = 1.1, | ||||
| 	max_fall = 0, | ||||
| 	turn_rate = 12, | ||||
| 	bouyancy_multiplier = 0.5, | ||||
| 	-- Visuals | ||||
| 	mesh = "animalia_rat.b3d", | ||||
| 	hitbox = { | ||||
| 		width = 0.15, | ||||
| 		height = 0.3 | ||||
| 	}, | ||||
| 	visual_size = {x = 10, y = 10}, | ||||
| 	textures = { | ||||
| 		"animalia_rat_1.png", | ||||
| 		"animalia_rat_2.png", | ||||
| 		"animalia_rat_3.png" | ||||
| 	}, | ||||
| 	animations = { | ||||
| 		stand = {range = {x = 1, y = 39}, speed = 20, frame_blend = 0.3, loop = true}, | ||||
| 		walk = {range = {x = 51, y = 69}, speed = 20, frame_blend = 0.3, loop = true}, | ||||
| 		run = {range = {x = 81, y = 99}, speed = 45, frame_blend = 0.3, loop = true}, | ||||
| 		eat = {range = {x = 111, y = 119}, speed = 20, frame_blend = 0.1, loop = false} | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = false, | ||||
| 	makes_footstep_sound = false, | ||||
| 	drops = { | ||||
| 		{name = "animalia:rat_raw", min = 1, max = 1, chance = 1} | ||||
| 	}, | ||||
| 	-- Function | ||||
| 	utility_stack = { | ||||
| 		{ | ||||
| 			utility = "animalia:wander", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				return 0.1, {self} | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:swim_to_land", | ||||
| 			step_delay = 0.25, | ||||
| 			get_score = function(self) | ||||
| 				if self.in_liquid then | ||||
| 					return 0.3, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:eat_crop", | ||||
| 			get_score = function(self) | ||||
| 				if math.random(6) < 2 | ||||
| 				or self:get_utility() == "animalia:eat_crop" then | ||||
| 					return 0.2, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:steal_from_chest", | ||||
| 			get_score = function(self) | ||||
| 				if math.random(12) < 2 | ||||
| 				or self:get_utility() == "animalia:steal_from_chest" then | ||||
| 					return 0.2, {self} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:flee_from_target", | ||||
| 			get_score = function(self) | ||||
| 				local target = creatura.get_nearby_object(self, {"animalia:fox", "animalia:cat"}) | ||||
| 				if not target then | ||||
| 					target = creatura.get_nearby_player(self) | ||||
| 				end | ||||
| 				if target | ||||
| 				and target:get_pos() then | ||||
| 					return 0.6, {self, target} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| 		animalia.initialize_lasso(self) | ||||
| 	end, | ||||
| 	step_func = function(self) | ||||
| 		animalia.step_timers(self) | ||||
| 		animalia.do_growth(self, 60) | ||||
| 		animalia.update_lasso_effects(self) | ||||
| 	end, | ||||
| 	death_func = function(self) | ||||
| 		if self:get_utility() ~= "animalia:die" then | ||||
| 			self:initiate_utility("animalia:die", self) | ||||
| 		end | ||||
| 	end, | ||||
| 	on_rightclick = function(self, clicker) | ||||
| 		if animalia.set_nametag(self, clicker) then | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:rat", "605a55", "ff936f") | ||||
| @ -44,6 +44,7 @@ creatura.register_mob("animalia:reindeer", { | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	drops = { | ||||
| @ -98,19 +99,7 @@ creatura.register_mob("animalia:reindeer", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso = type(self.lasso_origin or {}) == "userdata" and self.lasso_origin | ||||
| 				local force = lasso and lasso ~= false | ||||
| 				local player = (force and lasso) or creatura.get_nearby_player(self) | ||||
| 				if player | ||||
| 				and self:follow_wielded_item(player) then | ||||
| 					return 0.4, {self, player} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		animalia.global_utils.basic_follow, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			step_delay = 0.25, | ||||
| @ -122,18 +111,7 @@ creatura.register_mob("animalia:reindeer", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:flee_from_target", | ||||
| 			get_score = function(self) | ||||
| 				local puncher = self._target | ||||
| 				if puncher | ||||
| 				and puncher:get_pos() then | ||||
| 					return 0.6, {self, puncher} | ||||
| 				end | ||||
| 				self._target = nil | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 		animalia.global_utils.basic_flee | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| @ -158,10 +136,7 @@ creatura.register_mob("animalia:reindeer", { | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		self._target = puncher | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:reindeer", "cac3a1" ,"464438") | ||||
| creatura.register_spawn_egg("animalia:reindeer", "413022" ,"d5c0a3") | ||||
| @ -72,6 +72,7 @@ creatura.register_mob("animalia:sheep", { | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	sounds = { | ||||
| @ -135,21 +136,7 @@ creatura.register_mob("animalia:sheep", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				if self.lasso_origin | ||||
| 				and type(self.lasso_origin) == "userdata" then | ||||
| 					return 0.4, {self, self.lasso_origin, true} | ||||
| 				end | ||||
| 				local player = creatura.get_nearby_player(self) | ||||
| 				if player | ||||
| 				and self:follow_wielded_item(player) then | ||||
| 					return 0.4, {self, player} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		animalia.global_utils.basic_follow, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			step_delay = 0.25, | ||||
| @ -161,18 +148,7 @@ creatura.register_mob("animalia:sheep", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:flee_from_target", | ||||
| 			get_score = function(self) | ||||
| 				local puncher = self._target | ||||
| 				if puncher | ||||
| 				and puncher:get_pos() then | ||||
| 					return 0.6, {self, puncher} | ||||
| 				end | ||||
| 				self._target = nil | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 		animalia.global_utils.basic_flee | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		self.collected = self:recall("collected") or false | ||||
| @ -267,10 +243,7 @@ creatura.register_mob("animalia:sheep", { | ||||
| 			end | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		self._target = puncher | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:sheep", "f4e6cf", "e1ca9b") | ||||
| @ -5,6 +5,7 @@ | ||||
| creatura.register_mob("animalia:tropical_fish", { | ||||
| 	-- Stats | ||||
| 	max_health = 5, | ||||
| 	max_breath = 0, | ||||
| 	armor_groups = {fleshy = 150}, | ||||
| 	damage = 0, | ||||
| 	speed = 2, | ||||
| @ -33,7 +34,6 @@ creatura.register_mob("animalia:tropical_fish", { | ||||
| 		flop = {range = {x = 30, y = 40}, speed = 20, frame_blend = 0.3, loop = true}, | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	step_delay = 0.25, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = false, | ||||
| 	makes_footstep_sound = false, | ||||
| @ -82,9 +82,7 @@ creatura.register_mob("animalia:tropical_fish", { | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:tropical_fish", "e28821", "f6e5d2") | ||||
|  | ||||
| @ -42,6 +42,7 @@ creatura.register_mob("animalia:turkey", { | ||||
| 	}, | ||||
| 	-- Misc | ||||
| 	makes_footstep_sound = true, | ||||
| 	flee_puncher = true, | ||||
| 	catch_with_net = true, | ||||
| 	catch_with_lasso = true, | ||||
| 	sounds = { | ||||
| @ -113,19 +114,7 @@ creatura.register_mob("animalia:turkey", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso = type(self.lasso_origin or {}) == "userdata" and self.lasso_origin | ||||
| 				local force = lasso and lasso ~= false | ||||
| 				local player = (force and lasso) or creatura.get_nearby_player(self) | ||||
| 				if player | ||||
| 				and self:follow_wielded_item(player) then | ||||
| 					return 0.3, {self, player} | ||||
| 				end | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		animalia.global_utils.basic_follow, | ||||
| 		{ | ||||
| 			utility = "animalia:breed", | ||||
| 			get_score = function(self) | ||||
| @ -136,18 +125,7 @@ creatura.register_mob("animalia:turkey", { | ||||
| 				return 0 | ||||
| 			end | ||||
| 		}, | ||||
| 		{ | ||||
| 			utility = "animalia:flee_from_target", | ||||
| 			get_score = function(self) | ||||
| 				local puncher = self._target | ||||
| 				if puncher | ||||
| 				and puncher:get_pos() then | ||||
| 					return 0.6, {self, puncher} | ||||
| 				end | ||||
| 				self._target = nil | ||||
| 				return 0 | ||||
| 			end | ||||
| 		} | ||||
| 		animalia.global_utils.basic_flee | ||||
| 	}, | ||||
| 	activate_func = function(self) | ||||
| 		animalia.initialize_api(self) | ||||
| @ -182,10 +160,7 @@ creatura.register_mob("animalia:turkey", { | ||||
| 			return | ||||
| 		end | ||||
| 	end, | ||||
| 	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage) | ||||
| 		self._target = puncher | ||||
| 	end | ||||
| 	on_punch = animalia.punch | ||||
| }) | ||||
| 
 | ||||
| creatura.register_spawn_egg("animalia:turkey", "352b22", "2f2721") | ||||
| @ -45,7 +45,7 @@ end | ||||
| 
 | ||||
| creatura.register_mob("animalia:wolf", { | ||||
| 	-- Stats | ||||
| 	max_health = 15, | ||||
| 	max_health = 25, | ||||
| 	armor_groups = {fleshy = 100}, | ||||
| 	damage = 4, | ||||
| 	speed = 5, | ||||
| @ -60,7 +60,7 @@ creatura.register_mob("animalia:wolf", { | ||||
| 		width = 0.35, | ||||
| 		height = 0.7 | ||||
| 	}, | ||||
| 	visual_size = {x = 9, y = 9}, | ||||
| 	visual_size = {x = 10, y = 10}, | ||||
| 	textures = { | ||||
| 		"animalia_wolf_1.png", | ||||
| 		"animalia_wolf_2.png", | ||||
| @ -131,7 +131,8 @@ creatura.register_mob("animalia:wolf", { | ||||
| 		{ | ||||
| 			utility = "animalia:follow_player", | ||||
| 			get_score = function(self) | ||||
| 				local lasso = type(self.lasso_origin or {}) == "userdata" and self.lasso_origin | ||||
| 				local lasso_tgt = self._lassod_to | ||||
| 				local lasso = type(lasso_tgt) == "string" and minetest.get_player_by_name(lasso_tgt) | ||||
| 				local owner = self.owner and self.order == "follow" and minetest.get_player_by_name(self.owner) | ||||
| 				local force = (lasso and lasso ~= false) or owner | ||||
| 				local player = (force and (owner or lasso)) or creatura.get_nearby_player(self) | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								models/animalia_fox.b3d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								models/animalia_lasso_entity.b3d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								models/animalia_owl.b3d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								models/animalia_rat.b3d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								sounds/pig/animalia_pig_hurt.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								sounds/pig/animalia_pig_random.1.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								sounds/pig/animalia_pig_random.2.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 5.0 KiB | 
| Before Width: | Height: | Size: 1.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/animalia_lasso_entity.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.7 KiB | 
| Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.5 KiB | 
| Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.3 KiB | 
| Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.4 KiB | 
| Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/fox/animalia_fox_1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/items/animalia_crate.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/items/animalia_crate_side.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 851 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/items/animalia_guano.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.4 KiB | 
| Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/items/animalia_rat_cooked.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/items/animalia_rat_raw.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/owl/animalia_owl.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 9.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/rat/animalia_rat_1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/rat/animalia_rat_2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/rat/animalia_rat_3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.5 KiB |