Refactor mobs code by generalizing boar code
This commit is contained in:
parent
b92a780a7d
commit
51e42fdfbb
@ -496,3 +496,14 @@ rp_mobs.microtasks.sleep = function(time)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Instantly set mob acceleration to the given parameter
|
||||||
|
rp_mobs.microtasks.set_acceleration = function(acceleration)
|
||||||
|
return rp_mobs.create_microtask({
|
||||||
|
label = "set acceleration",
|
||||||
|
singlestep = true,
|
||||||
|
on_step = function(self, mob, dtime)
|
||||||
|
mob.object:set_acceleration(acceleration)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
@ -41,20 +41,12 @@ local IDLE_DURATION_MAX = 2000
|
|||||||
local RANDOM_SOUND_TIMER_MIN = 10000
|
local RANDOM_SOUND_TIMER_MIN = 10000
|
||||||
local RANDOM_SOUND_TIMER_MAX = 60000
|
local RANDOM_SOUND_TIMER_MAX = 60000
|
||||||
|
|
||||||
-- When trying to find a safe spot, the mob makes multiple raycasts
|
|
||||||
-- from the mob all around the mob horizontally. This number is
|
|
||||||
-- the angle difference in degrees between each ray.
|
|
||||||
local FIND_LAND_ANGLE_STEP = 15
|
|
||||||
|
|
||||||
-- How far the mob looks away for safe land (raycast length)
|
-- How far the mob looks away for safe land (raycast length)
|
||||||
local FIND_LAND_LENGTH = 20
|
local FIND_LAND_LENGTH = 20
|
||||||
|
|
||||||
-- Range the mob can 'see' players and mods for following/attacking
|
-- Range the mob can 'see' players and mods for following/attacking
|
||||||
local VIEW_RANGE = 10
|
local VIEW_RANGE = 10
|
||||||
|
|
||||||
-- Scan for players/mobs and update the following state every this many seconds
|
|
||||||
local FOLLOW_CHECK_TIME = 1.0
|
|
||||||
|
|
||||||
-- When the mob is this far away from a follow target, or closer, the target
|
-- When the mob is this far away from a follow target, or closer, the target
|
||||||
-- is supposed to be "reached" and the mob stops walking.
|
-- is supposed to be "reached" and the mob stops walking.
|
||||||
local FOLLOW_REACH_DISTANCE = 2
|
local FOLLOW_REACH_DISTANCE = 2
|
||||||
@ -70,188 +62,6 @@ local NO_FOLLOW_TIME = 6.0
|
|||||||
-- activate Love Mode.
|
-- activate Love Mode.
|
||||||
local FOOD = { "rp_default:apple", "rp_default:acorn" }
|
local FOOD = { "rp_default:apple", "rp_default:acorn" }
|
||||||
|
|
||||||
local mt_set_acceleration = function(acceleration)
|
|
||||||
return rp_mobs.create_microtask({
|
|
||||||
label = "set acceleration",
|
|
||||||
singlestep = true,
|
|
||||||
on_step = function(self, mob, dtime)
|
|
||||||
mob.object:set_acceleration(acceleration)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_liquid(nodename)
|
|
||||||
local ndef = minetest.registered_nodes[nodename]
|
|
||||||
return ndef and (ndef.liquid_move_physics == true or (ndef.liquid_move_physics == nil and ndef.liquidtype ~= "none"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_damaging(nodename)
|
|
||||||
local ndef = minetest.registered_nodes[nodename]
|
|
||||||
return ndef and ndef.damage_per_second > 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_walkable(nodename)
|
|
||||||
local ndef = minetest.registered_nodes[nodename]
|
|
||||||
return ndef and ndef.walkable
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_front_safe(mob, check_liquids, cliff_depth)
|
|
||||||
local vel = mob.object:get_velocity()
|
|
||||||
vel.y = 0
|
|
||||||
local yaw = mob.object:get_yaw()
|
|
||||||
local dir = vector.normalize(vel)
|
|
||||||
if vector.length(dir) > 0.5 then
|
|
||||||
yaw = minetest.dir_to_yaw(dir)
|
|
||||||
else
|
|
||||||
yaw = mob.object:get_yaw()
|
|
||||||
dir = minetest.yaw_to_dir(yaw)
|
|
||||||
end
|
|
||||||
local pos = mob.object:get_pos()
|
|
||||||
if mob._front_body_point then
|
|
||||||
local fbp = table.copy(mob._front_body_point)
|
|
||||||
fbp = vector.rotate_around_axis(fbp, vector.new(0, 1, 0), yaw)
|
|
||||||
pos = vector.add(pos, fbp)
|
|
||||||
end
|
|
||||||
local pos_front = vector.add(pos, dir)
|
|
||||||
local node_front = minetest.get_node(pos_front)
|
|
||||||
local def_front = minetest.registered_nodes[node_front.name]
|
|
||||||
if def_front and (def_front.drowning > 0 or def_front.damage_per_second > 0) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if def_front and not def_front.walkable then
|
|
||||||
local safe_drop = false
|
|
||||||
for c=1, cliff_depth do
|
|
||||||
local cpos = vector.add(pos_front, vector.new(0, -c, 0))
|
|
||||||
local cnode = minetest.get_node(cpos)
|
|
||||||
local cdef = minetest.registered_nodes[cnode.name]
|
|
||||||
if check_liquids and cdef.drowning > 0 then
|
|
||||||
return false
|
|
||||||
elseif cdef.damage_per_second > 0 then
|
|
||||||
return false
|
|
||||||
elseif cdef.walkable then
|
|
||||||
-- Mob doesn't like to land on node with high fall damage addition
|
|
||||||
if c > 1 and minetest.get_item_group(cnode.name, "fall_damage_add_percent") >= MAX_FALL_DAMAGE_ADD_PERCENT_DROP_ON then
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
safe_drop = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not safe_drop then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- This function helps the mob find safe land from a lake or ocean.
|
|
||||||
--
|
|
||||||
-- Assuming that pos is a position above a large body of
|
|
||||||
-- liquid (like a lake or ocean), this function can return
|
|
||||||
-- the (approximately) closest position of walkable land
|
|
||||||
-- from that position, up to a hardcoded maximum range.
|
|
||||||
--
|
|
||||||
--
|
|
||||||
-- Argument:
|
|
||||||
-- * pos: Start position
|
|
||||||
--
|
|
||||||
-- returns: <position>, <angle from position>
|
|
||||||
-- or nil, nil if no position found
|
|
||||||
local find_land_from_liquid = function(pos)
|
|
||||||
local startpos = table.copy(pos)
|
|
||||||
startpos.y = startpos.y - 1
|
|
||||||
local startnode = minetest.get_node(startpos)
|
|
||||||
if not is_liquid(startnode.name) then
|
|
||||||
startpos.y = startpos.y - 1
|
|
||||||
end
|
|
||||||
local vec_y = vector.new(0, 1, 0)
|
|
||||||
local best_pos
|
|
||||||
local best_dist
|
|
||||||
local best_angle
|
|
||||||
for angle=0, 359, FIND_LAND_ANGLE_STEP do
|
|
||||||
local angle_rad = (angle/360) * (math.pi*2)
|
|
||||||
local vec = vector.new(0, 0, 1)
|
|
||||||
vec = vector.rotate_around_axis(vec, vec_y, angle_rad)
|
|
||||||
vec = vector.multiply(vec, FIND_LAND_LENGTH)
|
|
||||||
local rc = minetest.raycast(startpos, vector.add(startpos, vec), false, false)
|
|
||||||
for pt in rc do
|
|
||||||
if pt.type == "node" then
|
|
||||||
local dist = vector.distance(startpos, pt.under)
|
|
||||||
local up = vector.add(pt.under, vector.new(0, 1, 0))
|
|
||||||
local upnode = minetest.get_node(up)
|
|
||||||
if not best_dist or dist < best_dist then
|
|
||||||
-- Ignore if ray collided with overhigh selection boxes (kelp, seagrass, etc.)
|
|
||||||
if pt.intersection_point.y - 0.5 < pt.under.y and
|
|
||||||
-- Node above must be non-walkable
|
|
||||||
not is_walkable(upnode.name) then
|
|
||||||
best_pos = up
|
|
||||||
best_dist = dist
|
|
||||||
local pos1 = vector.copy(startpos)
|
|
||||||
local pos2 = vector.copy(up)
|
|
||||||
pos1.y = 0
|
|
||||||
pos2.y = 0
|
|
||||||
best_angle = minetest.dir_to_yaw(vector.direction(pos1, pos2))
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if is_walkable(upnode.name) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return best_pos, best_angle
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Argument:
|
|
||||||
-- * pos: Start position
|
|
||||||
--
|
|
||||||
-- returns: <position>, <angle from position>
|
|
||||||
-- or nil, nil if no position found
|
|
||||||
local find_safe_node_from_pos = function(pos)
|
|
||||||
local startpos = table.copy(pos)
|
|
||||||
startpos.y = math.floor(startpos.y)
|
|
||||||
startpos.y = startpos.y - 1
|
|
||||||
local startnode = minetest.get_node(startpos)
|
|
||||||
local best_pos
|
|
||||||
local best_dist
|
|
||||||
local best_angle
|
|
||||||
local vec_y = vector.new(0, 1, 0)
|
|
||||||
for angle=0, 359, FIND_LAND_ANGLE_STEP do
|
|
||||||
local angle_rad = (angle/360) * (math.pi*2)
|
|
||||||
local vec = vector.new(0, 0, 1)
|
|
||||||
vec = vector.rotate_around_axis(vec, vec_y, angle_rad)
|
|
||||||
vec = vector.multiply(vec, FIND_LAND_LENGTH)
|
|
||||||
local rc = minetest.raycast(startpos, vector.add(startpos, vec), false, false)
|
|
||||||
for pt in rc do
|
|
||||||
if pt.type == "node" then
|
|
||||||
local floor = pt.under
|
|
||||||
local floornode = minetest.get_node(floor)
|
|
||||||
local up = vector.add(floor, vector.new(0, 1, 0))
|
|
||||||
local upnode = minetest.get_node(up)
|
|
||||||
if is_walkable(floornode.name) then
|
|
||||||
if is_walkable(upnode.name) then
|
|
||||||
break
|
|
||||||
elseif not is_walkable(upnode.name) and not is_damaging(upnode.name) then
|
|
||||||
local dist = vector.distance(startpos, floor)
|
|
||||||
if not best_dist or dist < best_dist then
|
|
||||||
best_pos = up
|
|
||||||
best_dist = dist
|
|
||||||
local pos1 = vector.copy(startpos)
|
|
||||||
local pos2 = vector.copy(up)
|
|
||||||
pos1.y = 0
|
|
||||||
pos2.y = 0
|
|
||||||
best_angle = minetest.dir_to_yaw(vector.direction(pos1, pos2))
|
|
||||||
end
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return best_pos, best_angle
|
|
||||||
end
|
|
||||||
|
|
||||||
local roam_decider = function(task_queue, mob)
|
local roam_decider = function(task_queue, mob)
|
||||||
local task_roam
|
local task_roam
|
||||||
@ -261,12 +71,12 @@ local roam_decider = function(task_queue, mob)
|
|||||||
if mob._env_node.name == "ignore" then
|
if mob._env_node.name == "ignore" then
|
||||||
task_roam = rp_mobs.create_task({label="stand still"})
|
task_roam = rp_mobs.create_task({label="stand still"})
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
|
||||||
elseif is_damaging(mob._env_node.name) then
|
elseif rp_mobs_mobs.is_damaging(mob._env_node.name) then
|
||||||
task_roam = rp_mobs.create_task({label="escape from damaging node"})
|
task_roam = rp_mobs.create_task({label="escape from damaging node"})
|
||||||
|
|
||||||
local yaw
|
local yaw
|
||||||
-- Find direction to walk to
|
-- Find direction to walk to
|
||||||
local safepos, safeangle = find_safe_node_from_pos(mob.object:get_pos())
|
local safepos, safeangle = rp_mobs_mobs.find_safe_node_from_pos(mob.object:get_pos())
|
||||||
local walk_duration
|
local walk_duration
|
||||||
-- Prefer walking towards safe place
|
-- Prefer walking towards safe place
|
||||||
if safepos and safeangle then
|
if safepos and safeangle then
|
||||||
@ -278,28 +88,30 @@ local roam_decider = function(task_queue, mob)
|
|||||||
walk_duration = math.random(WALK_DURATION_MIN, WALK_DURATION_MAX)/1000
|
walk_duration = math.random(WALK_DURATION_MIN, WALK_DURATION_MAX)/1000
|
||||||
end
|
end
|
||||||
local mt_walk = rp_mobs.microtasks.walk_straight(WALK_SPEED, yaw, nil, walk_duration)
|
local mt_walk = rp_mobs.microtasks.walk_straight(WALK_SPEED, yaw, nil, walk_duration)
|
||||||
|
local mt_acceleration = rp_mobs.microtasks.set_acceleration(rp_mobs.GRAVITY_VECTOR)
|
||||||
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
||||||
mt_walk.start_animation = "walk"
|
mt_walk.start_animation = "walk"
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_yaw, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_yaw, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_set_acceleration(rp_mobs.GRAVITY_VECTOR), task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_acceleration, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_walk, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_walk, task_roam)
|
||||||
|
|
||||||
elseif is_liquid(mob._env_node.name) then
|
elseif rp_mobs_mobs.is_liquid(mob._env_node.name) then
|
||||||
task_roam = rp_mobs.create_task({label="swim upwards"})
|
task_roam = rp_mobs.create_task({label="swim upwards"})
|
||||||
local yaw = math.random(0, 360) / 360 * (math.pi*2)
|
local yaw = math.random(0, 360) / 360 * (math.pi*2)
|
||||||
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
||||||
local move_vector = vector.new(0, LIQUID_RISE_SPEED, 0)
|
local move_vector = vector.new(0, LIQUID_RISE_SPEED, 0)
|
||||||
local mt_swim_up = rp_mobs.microtasks.move_straight(move_vector, yaw, vector.new(2, 0.3, 2))
|
local mt_swim_up = rp_mobs.microtasks.move_straight(move_vector, yaw, vector.new(2, 0.3, 2))
|
||||||
|
local mt_acceleration = rp_mobs.microtasks.set_acceleration(vector.zero())
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_yaw, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_yaw, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_set_acceleration(vector.zero()), task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_acceleration, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_swim_up, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_swim_up, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
|
||||||
elseif is_liquid(mob._env_node_floor.name) then
|
elseif rp_mobs_mobs.is_liquid(mob._env_node_floor.name) then
|
||||||
task_roam = rp_mobs.create_task({label="swim on liquid surface"})
|
task_roam = rp_mobs.create_task({label="swim on liquid surface"})
|
||||||
|
|
||||||
local yaw
|
local yaw
|
||||||
-- Find direction to walk to
|
-- Find direction to walk to
|
||||||
local landpos, landangle = find_land_from_liquid(mob.object:get_pos())
|
local landpos, landangle = rp_mobs_mobs.find_land_from_liquid(mob.object:get_pos())
|
||||||
local walk_duration
|
local walk_duration
|
||||||
-- Prefer walking towards land. Boar wants to stay dry. ;-)
|
-- Prefer walking towards land. Boar wants to stay dry. ;-)
|
||||||
if landpos and landangle then
|
if landpos and landangle then
|
||||||
@ -313,9 +125,10 @@ local roam_decider = function(task_queue, mob)
|
|||||||
end
|
end
|
||||||
local mt_walk = rp_mobs.microtasks.walk_straight(WALK_SPEED, yaw, JUMP_STRENGTH, walk_duration)
|
local mt_walk = rp_mobs.microtasks.walk_straight(WALK_SPEED, yaw, JUMP_STRENGTH, walk_duration)
|
||||||
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
||||||
|
local mt_acceleration = rp_mobs.microtasks.set_acceleration(vector.zero())
|
||||||
mt_walk.start_animation = "walk"
|
mt_walk.start_animation = "walk"
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_yaw, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_yaw, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_set_acceleration(vector.zero()), task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_acceleration, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_walk, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_walk, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
|
||||||
else
|
else
|
||||||
@ -325,8 +138,9 @@ local roam_decider = function(task_queue, mob)
|
|||||||
local walk_duration = math.random(WALK_DURATION_MIN, WALK_DURATION_MAX)/1000
|
local walk_duration = math.random(WALK_DURATION_MIN, WALK_DURATION_MAX)/1000
|
||||||
local mt_walk = rp_mobs.microtasks.walk_straight(WALK_SPEED, yaw, JUMP_STRENGTH, walk_duration)
|
local mt_walk = rp_mobs.microtasks.walk_straight(WALK_SPEED, yaw, JUMP_STRENGTH, walk_duration)
|
||||||
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
||||||
|
local mt_acceleration = rp_mobs.microtasks.set_acceleration(rp_mobs.GRAVITY_VECTOR)
|
||||||
mt_walk.start_animation = "walk"
|
mt_walk.start_animation = "walk"
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_set_acceleration(rp_mobs.GRAVITY_VECTOR), task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_acceleration, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_yaw, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_yaw, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_walk, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_walk, task_roam)
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
|
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
|
||||||
@ -335,32 +149,6 @@ local roam_decider = function(task_queue, mob)
|
|||||||
rp_mobs.add_task_to_task_queue(task_queue, task_roam)
|
rp_mobs.add_task_to_task_queue(task_queue, task_roam)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add a "stand still" task to the mob's task queue with
|
|
||||||
-- an optional yaw
|
|
||||||
local halt = function(task_queue, mob, set_yaw)
|
|
||||||
local mt_sleep = rp_mobs.microtasks.sleep(math.random(IDLE_DURATION_MIN, IDLE_DURATION_MAX)/1000)
|
|
||||||
mt_sleep.start_animation = "idle"
|
|
||||||
local task = rp_mobs.create_task({label="stand still"})
|
|
||||||
local vel = mob.object:get_velocity()
|
|
||||||
vel.x = 0
|
|
||||||
vel.z = 0
|
|
||||||
local yaw
|
|
||||||
if not set_yaw then
|
|
||||||
yaw = mob.object:get_yaw()
|
|
||||||
else
|
|
||||||
yaw = set_yaw
|
|
||||||
end
|
|
||||||
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
|
||||||
|
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_set_acceleration(rp_mobs.GRAVITY_VECTOR), task)
|
|
||||||
if set_yaw then
|
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_yaw, task)
|
|
||||||
end
|
|
||||||
rp_mobs.add_microtask_to_task(mob, rp_mobs.microtasks.move_straight(vel, yaw, vector.new(0.5,0,0.5), 1), task)
|
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_sleep, task)
|
|
||||||
rp_mobs.add_task_to_task_queue(task_queue, task)
|
|
||||||
end
|
|
||||||
|
|
||||||
local roam_decider_step = function(task_queue, mob, dtime)
|
local roam_decider_step = function(task_queue, mob, dtime)
|
||||||
-- Re-enable following after a few seconds
|
-- Re-enable following after a few seconds
|
||||||
if mob._temp_custom_state.no_follow then
|
if mob._temp_custom_state.no_follow then
|
||||||
@ -376,24 +164,24 @@ local roam_decider_step = function(task_queue, mob, dtime)
|
|||||||
if current and current.data then
|
if current and current.data then
|
||||||
-- Escape from damaging node has reached safety
|
-- Escape from damaging node has reached safety
|
||||||
if current.data.label == "escape from damaging node" then
|
if current.data.label == "escape from damaging node" then
|
||||||
if not is_damaging(mob._env_node.name) or is_liquid(mob._env_node.name) then
|
if not rp_mobs_mobs.is_damaging(mob._env_node.name) or rp_mobs_mobs.is_liquid(mob._env_node.name) then
|
||||||
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
||||||
end
|
end
|
||||||
-- Stop following player or partner if gone
|
-- Stop following player or partner if gone
|
||||||
elseif (current.data.label == "follow player holding food" and not mob._temp_custom_state.follow_player) or
|
elseif (current.data.label == "follow player holding food" and not mob._temp_custom_state.follow_player) or
|
||||||
(current.data.label == "follow mating partner" and not mob._temp_custom_state.follow_partner) then
|
(current.data.label == "follow mating partner" and not mob._temp_custom_state.follow_partner) then
|
||||||
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
||||||
halt(task_queue, mob)
|
rp_mobs_mobs.add_halt_to_task_queue(task_queue, mob, nil, IDLE_DURATION_MIN, IDLE_DURATION_MAX)
|
||||||
-- Update land movement (roam, standing, following)
|
-- Update land movement (roam, standing, following)
|
||||||
-- Note: The follow tasks are all considered to be land movement.
|
-- Note: The follow tasks are all considered to be land movement.
|
||||||
-- There is no following while swimming!
|
-- There is no following while swimming!
|
||||||
elseif current.data.label == "roam land" or current.data.label == "stand still" or
|
elseif current.data.label == "roam land" or current.data.label == "stand still" or
|
||||||
current.data.label == "follow player holding food" or current.data.label == "follow mating partner" then
|
current.data.label == "follow player holding food" or current.data.label == "follow mating partner" then
|
||||||
-- Abort when in damaging or liquid node
|
-- Abort when in damaging or liquid node
|
||||||
if is_damaging(mob._env_node.name) or is_liquid(mob._env_node.name) then
|
if rp_mobs_mobs.is_damaging(mob._env_node.name) or rp_mobs_mobs.is_liquid(mob._env_node.name) then
|
||||||
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
||||||
-- Abort and stop movement when walking towards of a cliff or other dangerous node
|
-- Abort and stop movement when walking towards of a cliff or other dangerous node
|
||||||
elseif not is_front_safe(mob, true, FALL_HEIGHT) and current.data.label ~= "stand still" then
|
elseif not rp_mobs_mobs.is_front_safe(mob, FALL_HEIGHT, MAX_FALL_DAMAGE_ADD_PERCENT_DROP_ON) and current.data.label ~= "stand still" then
|
||||||
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
||||||
|
|
||||||
-- Rotate by 70° to 180° left or right
|
-- Rotate by 70° to 180° left or right
|
||||||
@ -403,7 +191,9 @@ local roam_decider_step = function(task_queue, mob, dtime)
|
|||||||
if sign == 1 then
|
if sign == 1 then
|
||||||
yaw = -yaw
|
yaw = -yaw
|
||||||
end
|
end
|
||||||
halt(task_queue, mob, yaw)
|
rp_mobs_mobs.add_halt_to_task_queue(task_queue, mob, yaw, IDLE_DURATION_MIN, IDLE_DURATION_MAX)
|
||||||
|
|
||||||
|
|
||||||
-- Disable following for a few seconds if mob just avoided a danger
|
-- Disable following for a few seconds if mob just avoided a danger
|
||||||
if current.data.label == "follow player holding food" or current.data.label == "follow mating partner" then
|
if current.data.label == "follow player holding food" or current.data.label == "follow mating partner" then
|
||||||
mob._temp_custom_state.no_follow = true
|
mob._temp_custom_state.no_follow = true
|
||||||
@ -434,7 +224,8 @@ local roam_decider_step = function(task_queue, mob, dtime)
|
|||||||
end
|
end
|
||||||
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
||||||
local task = rp_mobs.create_task({label=task_label})
|
local task = rp_mobs.create_task({label=task_label})
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_set_acceleration(rp_mobs.GRAVITY_VECTOR), task)
|
local mt_acceleration = rp_mobs.microtasks.set_acceleration(rp_mobs.GRAVITY_VECTOR)
|
||||||
|
rp_mobs.add_microtask_to_task(mob, mt_acceleration, task)
|
||||||
local mt_follow = rp_mobs.microtasks.walk_straight_towards(WALK_SPEED, "object", target, true, FOLLOW_REACH_DISTANCE, JUMP_STRENGTH, FOLLOW_GIVE_UP_TIME)
|
local mt_follow = rp_mobs.microtasks.walk_straight_towards(WALK_SPEED, "object", target, true, FOLLOW_REACH_DISTANCE, JUMP_STRENGTH, FOLLOW_GIVE_UP_TIME)
|
||||||
mt_follow.start_animation = "walk"
|
mt_follow.start_animation = "walk"
|
||||||
rp_mobs.add_microtask_to_task(mob, mt_follow, task)
|
rp_mobs.add_microtask_to_task(mob, mt_follow, task)
|
||||||
@ -446,7 +237,7 @@ local roam_decider_step = function(task_queue, mob, dtime)
|
|||||||
end
|
end
|
||||||
-- Surface from liquid
|
-- Surface from liquid
|
||||||
elseif current.data.label == "swim upwards" then
|
elseif current.data.label == "swim upwards" then
|
||||||
if not is_liquid(mob._env_node.name) then
|
if not rp_mobs_mobs.is_liquid(mob._env_node.name) then
|
||||||
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
||||||
local vel = vector.zero()
|
local vel = vector.zero()
|
||||||
-- Transitionary task to reset velocity.
|
-- Transitionary task to reset velocity.
|
||||||
@ -456,7 +247,7 @@ local roam_decider_step = function(task_queue, mob, dtime)
|
|||||||
end
|
end
|
||||||
-- Reaching land or air when swimming on liquid
|
-- Reaching land or air when swimming on liquid
|
||||||
elseif current.data.label == "swim on liquid surface" then
|
elseif current.data.label == "swim on liquid surface" then
|
||||||
if not is_liquid(mob._env_node.name) and not is_liquid(mob._env_node_floor.name) then
|
if not rp_mobs_mobs.is_liquid(mob._env_node.name) and not rp_mobs_mobs.is_liquid(mob._env_node_floor.name) then
|
||||||
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -464,134 +255,7 @@ local roam_decider_step = function(task_queue, mob, dtime)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This microtasks scans the mob's surroundings within
|
mt_find_follow = rp_mobs_mobs.microtask_find_follow(VIEW_RANGE, FOOD)
|
||||||
-- VIEW_RANGE for other interesting entities:
|
|
||||||
-- 1) Players holding food
|
|
||||||
-- 2) Mobs of same species to mate with
|
|
||||||
-- The result is stored in mob._temp_custom_state.follow_partner
|
|
||||||
-- and mob._temp_custom_state.follow_player.
|
|
||||||
-- This microtask only *searches* for suitable targets to follow,
|
|
||||||
-- it does *NOT* actually follow them. Other microtasks
|
|
||||||
-- are supposed to decide what do do with this information.
|
|
||||||
local mt_find_follow = rp_mobs.create_microtask({
|
|
||||||
label = "find entities to follow",
|
|
||||||
on_start = function(self, mob)
|
|
||||||
self.statedata.timer = 0
|
|
||||||
end,
|
|
||||||
on_step = function(self, mob, dtime)
|
|
||||||
-- Perform the follow check periodically
|
|
||||||
self.statedata.timer = self.statedata.timer + dtime
|
|
||||||
if self.statedata.timer < FOLLOW_CHECK_TIME then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self.statedata.timer = 0
|
|
||||||
|
|
||||||
local s = mob.object:get_pos()
|
|
||||||
local objs = minetest.get_objects_inside_radius(s, VIEW_RANGE)
|
|
||||||
|
|
||||||
-- Look for other horny mob nearby
|
|
||||||
if mob._horny then
|
|
||||||
if mob._temp_custom_state.follow_partner == nil then
|
|
||||||
local min_dist, closest_partner
|
|
||||||
local min_dist_h, closest_partner_h
|
|
||||||
for o=1, #objs do
|
|
||||||
local obj = objs[o]
|
|
||||||
local ent = obj:get_luaentity()
|
|
||||||
-- Find other mob of same species
|
|
||||||
if obj ~= mob.object and ent and ent._cmi_is_mob and ent.name == "rp_mobs_mobs:boar" and not ent._child then
|
|
||||||
local p = obj:get_pos()
|
|
||||||
local dist = vector.distance(s, p)
|
|
||||||
-- Find closest one
|
|
||||||
if dist <= VIEW_RANGE then
|
|
||||||
-- Closest partner
|
|
||||||
if ((not min_dist) or dist < min_dist) then
|
|
||||||
min_dist = dist
|
|
||||||
closest_partner = obj
|
|
||||||
end
|
|
||||||
-- Closest horny partner
|
|
||||||
if ent._horny and ((not min_dist_h) or dist < min_dist_h) then
|
|
||||||
min_dist_h = dist
|
|
||||||
closest_partner_h = obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- Set new partner to follow (prefer horny)
|
|
||||||
if closest_partner_h then
|
|
||||||
mob._temp_custom_state.follow_partner = closest_partner_h
|
|
||||||
elseif closest_partner then
|
|
||||||
mob._temp_custom_state.follow_partner = closest_partner
|
|
||||||
end
|
|
||||||
-- Unfollow partner if out of range
|
|
||||||
elseif mob._temp_custom_state.follow_partner:get_luaentity() then
|
|
||||||
local p = mob._temp_custom_state.follow_partner:get_pos()
|
|
||||||
local dist = vector.distance(s, p)
|
|
||||||
-- Out of range
|
|
||||||
if dist > VIEW_RANGE then
|
|
||||||
mob._temp_custom_state.follow_partner = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Partner object is gone
|
|
||||||
mob._temp_custom_state.follow_partner = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Unfollow partner if no longer horny
|
|
||||||
mob._temp_custom_state.follow_partner = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if (mob._temp_custom_state.follow_player == nil) then
|
|
||||||
-- Mark closest player holding food within view range as player to follow
|
|
||||||
local p, dist
|
|
||||||
local min_dist, closest_player
|
|
||||||
for o=1, #objs do
|
|
||||||
local obj = objs[o]
|
|
||||||
if obj:is_player() then
|
|
||||||
local player = obj
|
|
||||||
p = player:get_pos()
|
|
||||||
dist = vector.distance(s, p)
|
|
||||||
if dist <= VIEW_RANGE and ((not min_dist) or dist < min_dist) then
|
|
||||||
local wield = player:get_wielded_item()
|
|
||||||
-- Is holding food?
|
|
||||||
for f=1, #FOOD do
|
|
||||||
if wield:get_name() == FOOD[f] then
|
|
||||||
min_dist = dist
|
|
||||||
closest_player = player
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if closest_player then
|
|
||||||
mob._temp_custom_state.follow_player = closest_player:get_player_name()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Unfollow player if out of view range or not holding food
|
|
||||||
local player = minetest.get_player_by_name(mob._temp_custom_state.follow_player)
|
|
||||||
if player then
|
|
||||||
local p = player:get_pos()
|
|
||||||
local dist = vector.distance(s, p)
|
|
||||||
-- Out of range
|
|
||||||
if dist > VIEW_RANGE then
|
|
||||||
mob._temp_custom_state.follow_player = nil
|
|
||||||
else
|
|
||||||
local wield = player:get_wielded_item()
|
|
||||||
for f=1, #FOOD do
|
|
||||||
if wield:get_name() == FOOD[f] then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- Not holding food
|
|
||||||
mob._temp_custom_state.follow_player = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
is_finished = function()
|
|
||||||
return false
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
local follow_decider = function(task_queue, mob)
|
local follow_decider = function(task_queue, mob)
|
||||||
local task = rp_mobs.create_task({label="scan for entities to follow"})
|
local task = rp_mobs.create_task({label="scan for entities to follow"})
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
rp_mobs_mobs = {}
|
rp_mobs_mobs = {}
|
||||||
|
|
||||||
|
-- Shared functions
|
||||||
|
dofile(minetest.get_modpath("rp_mobs_mobs").."/shared.lua")
|
||||||
|
|
||||||
-- Crafting stuff
|
-- Crafting stuff
|
||||||
dofile(minetest.get_modpath("rp_mobs_mobs").."/crafts.lua")
|
dofile(minetest.get_modpath("rp_mobs_mobs").."/crafts.lua")
|
||||||
|
|
||||||
|
358
mods/rp_mobs_mobs/shared.lua
Normal file
358
mods/rp_mobs_mobs/shared.lua
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
-- Scan for players/mobs and update the following state every this many seconds
|
||||||
|
local FOLLOW_CHECK_TIME = 1.0
|
||||||
|
|
||||||
|
-- When trying to find a safe spot, the mob makes multiple raycasts
|
||||||
|
-- from the mob all around the mob horizontally. This number is
|
||||||
|
-- the angle difference in degrees between each ray.
|
||||||
|
local FIND_LAND_ANGLE_STEP = 15
|
||||||
|
|
||||||
|
-- Returns true if the given node (by node name) is a liquid
|
||||||
|
rp_mobs_mobs.is_liquid = function(nodename)
|
||||||
|
local ndef = minetest.registered_nodes[nodename]
|
||||||
|
return ndef and (ndef.liquid_move_physics == true or (ndef.liquid_move_physics == nil and ndef.liquidtype ~= "none"))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns true if node deals damage
|
||||||
|
rp_mobs_mobs.is_damaging = function(nodename)
|
||||||
|
local ndef = minetest.registered_nodes[nodename]
|
||||||
|
return ndef and ndef.damage_per_second > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns true if node is walkable
|
||||||
|
rp_mobs_mobs.is_walkable = function(nodename)
|
||||||
|
local ndef = minetest.registered_nodes[nodename]
|
||||||
|
return ndef and ndef.walkable
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns true if the node(s) in front of the mob are safe.
|
||||||
|
-- This is considered unsafe:
|
||||||
|
-- * damage_per_second > 0
|
||||||
|
-- * drowning > 0
|
||||||
|
-- * a drop, if greater than cliff_depth
|
||||||
|
-- * a drop on a node with high fall_damage_add_percent
|
||||||
|
--
|
||||||
|
-- Parameters:
|
||||||
|
-- * mob: Mob object to check
|
||||||
|
-- * cliff_depth: How deep the mob is allowed to fall
|
||||||
|
-- * max_fall_damage_add_percent_drop_on: (optional): If set, mob can
|
||||||
|
-- not fall on a node with a fall_damage_add_percent group that is higher or equal than this value
|
||||||
|
rp_mobs_mobs.is_front_safe = function(mob, cliff_depth, max_fall_damage_add_percent_drop_on)
|
||||||
|
local vel = mob.object:get_velocity()
|
||||||
|
vel.y = 0
|
||||||
|
local yaw = mob.object:get_yaw()
|
||||||
|
local dir = vector.normalize(vel)
|
||||||
|
if vector.length(dir) > 0.5 then
|
||||||
|
yaw = minetest.dir_to_yaw(dir)
|
||||||
|
else
|
||||||
|
yaw = mob.object:get_yaw()
|
||||||
|
dir = minetest.yaw_to_dir(yaw)
|
||||||
|
end
|
||||||
|
local pos = mob.object:get_pos()
|
||||||
|
if mob._front_body_point then
|
||||||
|
local fbp = table.copy(mob._front_body_point)
|
||||||
|
fbp = vector.rotate_around_axis(fbp, vector.new(0, 1, 0), yaw)
|
||||||
|
pos = vector.add(pos, fbp)
|
||||||
|
end
|
||||||
|
local pos_front = vector.add(pos, dir)
|
||||||
|
local node_front = minetest.get_node(pos_front)
|
||||||
|
local def_front = minetest.registered_nodes[node_front.name]
|
||||||
|
if def_front and (def_front.drowning > 0 or def_front.damage_per_second > 0) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if def_front and not def_front.walkable then
|
||||||
|
local safe_drop = false
|
||||||
|
for c=1, cliff_depth do
|
||||||
|
local cpos = vector.add(pos_front, vector.new(0, -c, 0))
|
||||||
|
local cnode = minetest.get_node(cpos)
|
||||||
|
local cdef = minetest.registered_nodes[cnode.name]
|
||||||
|
if cdef.drowning > 0 then
|
||||||
|
return false
|
||||||
|
elseif cdef.damage_per_second > 0 then
|
||||||
|
return false
|
||||||
|
elseif cdef.walkable then
|
||||||
|
-- Mob doesn't like to land on node with high fall damage addition
|
||||||
|
if max_fall_damage_add_percent_drop_on and c > 1 and minetest.get_item_group(cnode.name, "fall_damage_add_percent") >= max_fall_damage_add_percent_drop_on then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
safe_drop = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not safe_drop then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This function helps the mob find safe land from a lake or ocean.
|
||||||
|
--
|
||||||
|
-- Assuming that pos is a position above a large body of
|
||||||
|
-- liquid (like a lake or ocean), this function can return
|
||||||
|
-- the (approximately) closest position of walkable land
|
||||||
|
-- from that position, up to a hardcoded maximum range.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- Argument:
|
||||||
|
-- * pos: Start position
|
||||||
|
-- * find_land_length: How far the mob looks away for safe land (raycast length)
|
||||||
|
--
|
||||||
|
-- returns: <position>, <angle from position>
|
||||||
|
-- or nil, nil if no position found
|
||||||
|
rp_mobs_mobs.find_land_from_liquid = function(pos, find_land_length)
|
||||||
|
local startpos = table.copy(pos)
|
||||||
|
startpos.y = startpos.y - 1
|
||||||
|
local startnode = minetest.get_node(startpos)
|
||||||
|
if not is_liquid(startnode.name) then
|
||||||
|
startpos.y = startpos.y - 1
|
||||||
|
end
|
||||||
|
local vec_y = vector.new(0, 1, 0)
|
||||||
|
local best_pos
|
||||||
|
local best_dist
|
||||||
|
local best_angle
|
||||||
|
for angle=0, 359, FIND_LAND_ANGLE_STEP do
|
||||||
|
local angle_rad = (angle/360) * (math.pi*2)
|
||||||
|
local vec = vector.new(0, 0, 1)
|
||||||
|
vec = vector.rotate_around_axis(vec, vec_y, angle_rad)
|
||||||
|
vec = vector.multiply(vec, find_land_length)
|
||||||
|
local rc = minetest.raycast(startpos, vector.add(startpos, vec), false, false)
|
||||||
|
for pt in rc do
|
||||||
|
if pt.type == "node" then
|
||||||
|
local dist = vector.distance(startpos, pt.under)
|
||||||
|
local up = vector.add(pt.under, vector.new(0, 1, 0))
|
||||||
|
local upnode = minetest.get_node(up)
|
||||||
|
if not best_dist or dist < best_dist then
|
||||||
|
-- Ignore if ray collided with overhigh selection boxes (kelp, seagrass, etc.)
|
||||||
|
if pt.intersection_point.y - 0.5 < pt.under.y and
|
||||||
|
-- Node above must be non-walkable
|
||||||
|
not is_walkable(upnode.name) then
|
||||||
|
best_pos = up
|
||||||
|
best_dist = dist
|
||||||
|
local pos1 = vector.copy(startpos)
|
||||||
|
local pos2 = vector.copy(up)
|
||||||
|
pos1.y = 0
|
||||||
|
pos2.y = 0
|
||||||
|
best_angle = minetest.dir_to_yaw(vector.direction(pos1, pos2))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if is_walkable(upnode.name) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return best_pos, best_angle
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Arguments:
|
||||||
|
-- * pos: Start position
|
||||||
|
-- * find_land_length: How far the mob looks away for safe land (raycast length)
|
||||||
|
--
|
||||||
|
-- returns: <position>, <angle from position>
|
||||||
|
-- or nil, nil if no position found
|
||||||
|
rp_mobs_mobs.find_safe_node_from_pos = function(pos)
|
||||||
|
local startpos = table.copy(pos)
|
||||||
|
startpos.y = math.floor(startpos.y)
|
||||||
|
startpos.y = startpos.y - 1
|
||||||
|
local startnode = minetest.get_node(startpos)
|
||||||
|
local best_pos
|
||||||
|
local best_dist
|
||||||
|
local best_angle
|
||||||
|
local vec_y = vector.new(0, 1, 0)
|
||||||
|
for angle=0, 359, FIND_LAND_ANGLE_STEP do
|
||||||
|
local angle_rad = (angle/360) * (math.pi*2)
|
||||||
|
local vec = vector.new(0, 0, 1)
|
||||||
|
vec = vector.rotate_around_axis(vec, vec_y, angle_rad)
|
||||||
|
vec = vector.multiply(vec, find_land_length)
|
||||||
|
local rc = minetest.raycast(startpos, vector.add(startpos, vec), false, false)
|
||||||
|
for pt in rc do
|
||||||
|
if pt.type == "node" then
|
||||||
|
local floor = pt.under
|
||||||
|
local floornode = minetest.get_node(floor)
|
||||||
|
local up = vector.add(floor, vector.new(0, 1, 0))
|
||||||
|
local upnode = minetest.get_node(up)
|
||||||
|
if is_walkable(floornode.name) then
|
||||||
|
if is_walkable(upnode.name) then
|
||||||
|
break
|
||||||
|
elseif not is_walkable(upnode.name) and not is_damaging(upnode.name) then
|
||||||
|
local dist = vector.distance(startpos, floor)
|
||||||
|
if not best_dist or dist < best_dist then
|
||||||
|
best_pos = up
|
||||||
|
best_dist = dist
|
||||||
|
local pos1 = vector.copy(startpos)
|
||||||
|
local pos2 = vector.copy(up)
|
||||||
|
pos1.y = 0
|
||||||
|
pos2.y = 0
|
||||||
|
best_angle = minetest.dir_to_yaw(vector.direction(pos1, pos2))
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return best_pos, best_angle
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add a "stand still" task to the mob's task queue with
|
||||||
|
-- an optional yaw
|
||||||
|
rp_mobs_mobs.add_halt_to_task_queue = function(task_queue, mob, set_yaw, idle_min, idle_max)
|
||||||
|
local mt_sleep = rp_mobs.microtasks.sleep(math.random(idle_min, idle_max)/1000)
|
||||||
|
mt_sleep.start_animation = "idle"
|
||||||
|
local task = rp_mobs.create_task({label="stand still"})
|
||||||
|
local vel = mob.object:get_velocity()
|
||||||
|
vel.x = 0
|
||||||
|
vel.z = 0
|
||||||
|
local yaw
|
||||||
|
if not set_yaw then
|
||||||
|
yaw = mob.object:get_yaw()
|
||||||
|
else
|
||||||
|
yaw = set_yaw
|
||||||
|
end
|
||||||
|
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
|
||||||
|
local mt_acceleration = rp_mobs.microtasks.set_acceleration(rp_mobs.GRAVITY_VECTOR)
|
||||||
|
|
||||||
|
rp_mobs.add_microtask_to_task(mob, mt_acceleration, task)
|
||||||
|
if set_yaw then
|
||||||
|
rp_mobs.add_microtask_to_task(mob, mt_yaw, task)
|
||||||
|
end
|
||||||
|
rp_mobs.add_microtask_to_task(mob, rp_mobs.microtasks.move_straight(vel, yaw, vector.new(0.5,0,0.5), 1), task)
|
||||||
|
rp_mobs.add_microtask_to_task(mob, mt_sleep, task)
|
||||||
|
rp_mobs.add_task_to_task_queue(task_queue, task)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This function creates and returns a microtask thta scans the
|
||||||
|
-- mob's surroundings within view_range for other interesting entities:
|
||||||
|
-- 1) Players holding food
|
||||||
|
-- 2) Mobs of same species to mate with
|
||||||
|
-- The result is stored in mob._temp_custom_state.follow_partner
|
||||||
|
-- and mob._temp_custom_state.follow_player.
|
||||||
|
-- This microtask only *searches* for suitable targets to follow,
|
||||||
|
-- it does *NOT* actually follow them. Other microtasks
|
||||||
|
-- are supposed to decide what do do with this information.
|
||||||
|
-- Parameters:
|
||||||
|
-- * view_range: Range in which mob can detect other objects
|
||||||
|
-- * foodlist: List of food items the mob likes to follow (itemstrings)
|
||||||
|
rp_mobs_mobs.microtask_find_follow = function(view_range, foodlist)
|
||||||
|
return rp_mobs.create_microtask({
|
||||||
|
label = "find entities to follow",
|
||||||
|
on_start = function(self, mob)
|
||||||
|
self.statedata.timer = 0
|
||||||
|
end,
|
||||||
|
on_step = function(self, mob, dtime)
|
||||||
|
-- Perform the follow check periodically
|
||||||
|
self.statedata.timer = self.statedata.timer + dtime
|
||||||
|
if self.statedata.timer < FOLLOW_CHECK_TIME then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.statedata.timer = 0
|
||||||
|
|
||||||
|
local s = mob.object:get_pos()
|
||||||
|
local objs = minetest.get_objects_inside_radius(s, view_range)
|
||||||
|
|
||||||
|
-- Look for other horny mob nearby
|
||||||
|
if mob._horny then
|
||||||
|
if mob._temp_custom_state.follow_partner == nil then
|
||||||
|
local min_dist, closest_partner
|
||||||
|
local min_dist_h, closest_partner_h
|
||||||
|
for o=1, #objs do
|
||||||
|
local obj = objs[o]
|
||||||
|
local ent = obj:get_luaentity()
|
||||||
|
-- Find other mob of same species
|
||||||
|
if obj ~= mob.object and ent and ent._cmi_is_mob and ent.name == mob.name and not ent._child then
|
||||||
|
local p = obj:get_pos()
|
||||||
|
local dist = vector.distance(s, p)
|
||||||
|
-- Find closest one
|
||||||
|
if dist <= view_range then
|
||||||
|
-- Closest partner
|
||||||
|
if ((not min_dist) or dist < min_dist) then
|
||||||
|
min_dist = dist
|
||||||
|
closest_partner = obj
|
||||||
|
end
|
||||||
|
-- Closest horny partner
|
||||||
|
if ent._horny and ((not min_dist_h) or dist < min_dist_h) then
|
||||||
|
min_dist_h = dist
|
||||||
|
closest_partner_h = obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Set new partner to follow (prefer horny)
|
||||||
|
if closest_partner_h then
|
||||||
|
mob._temp_custom_state.follow_partner = closest_partner_h
|
||||||
|
elseif closest_partner then
|
||||||
|
mob._temp_custom_state.follow_partner = closest_partner
|
||||||
|
end
|
||||||
|
-- Unfollow partner if out of range
|
||||||
|
elseif mob._temp_custom_state.follow_partner:get_luaentity() then
|
||||||
|
local p = mob._temp_custom_state.follow_partner:get_pos()
|
||||||
|
local dist = vector.distance(s, p)
|
||||||
|
-- Out of range
|
||||||
|
if dist > view_range then
|
||||||
|
mob._temp_custom_state.follow_partner = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Partner object is gone
|
||||||
|
mob._temp_custom_state.follow_partner = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Unfollow partner if no longer horny
|
||||||
|
mob._temp_custom_state.follow_partner = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if (mob._temp_custom_state.follow_player == nil) then
|
||||||
|
-- Mark closest player holding food within view range as player to follow
|
||||||
|
local p, dist
|
||||||
|
local min_dist, closest_player
|
||||||
|
for o=1, #objs do
|
||||||
|
local obj = objs[o]
|
||||||
|
if obj:is_player() then
|
||||||
|
local player = obj
|
||||||
|
p = player:get_pos()
|
||||||
|
dist = vector.distance(s, p)
|
||||||
|
if dist <= view_range and ((not min_dist) or dist < min_dist) then
|
||||||
|
local wield = player:get_wielded_item()
|
||||||
|
-- Is holding food?
|
||||||
|
for f=1, #foodlist do
|
||||||
|
if wield:get_name() == foodlist[f] then
|
||||||
|
min_dist = dist
|
||||||
|
closest_player = player
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if closest_player then
|
||||||
|
mob._temp_custom_state.follow_player = closest_player:get_player_name()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Unfollow player if out of view range or not holding food
|
||||||
|
local player = minetest.get_player_by_name(mob._temp_custom_state.follow_player)
|
||||||
|
if player then
|
||||||
|
local p = player:get_pos()
|
||||||
|
local dist = vector.distance(s, p)
|
||||||
|
-- Out of range
|
||||||
|
if dist > view_range then
|
||||||
|
mob._temp_custom_state.follow_player = nil
|
||||||
|
else
|
||||||
|
local wield = player:get_wielded_item()
|
||||||
|
for f=1, #foodlist do
|
||||||
|
if wield:get_name() == foodlist[f] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Not holding food
|
||||||
|
mob._temp_custom_state.follow_player = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
is_finished = function()
|
||||||
|
return false
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
@ -17,6 +17,9 @@ else
|
|||||||
setting_spawn_protected = true
|
setting_spawn_protected = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- If true, then only peaceful mobs may spawn
|
||||||
|
local setting_peaceful_only = minetest.settings:get_bool("only_peaceful_mobs", false)
|
||||||
|
|
||||||
-- END OF SETTINGS
|
-- END OF SETTINGS
|
||||||
|
|
||||||
local mg_corner_1, mg_corner_2 = minetest.get_mapgen_edges()
|
local mg_corner_1, mg_corner_2 = minetest.get_mapgen_edges()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user