Teach boar how to escape damaging nodes

This commit is contained in:
Wuzzy 2024-03-01 02:26:46 +01:00
parent 9de7867e7e
commit 428b4a88c6
3 changed files with 117 additions and 10 deletions

View File

@ -450,11 +450,6 @@ rp_mobs.handle_tasks = function(self, dtime, moveresult)
local activeTaskQueue = active_task_queue_entry.data
-- Run step decider
if activeTaskQueue.step_decider then
activeTaskQueue:step_decider(self)
end
-- Run empty decider if active task queue is empty
local activeTaskEntry
if activeTaskQueue.tasks:isEmpty() then
@ -463,6 +458,11 @@ rp_mobs.handle_tasks = function(self, dtime, moveresult)
end
end
-- Run step decider
if activeTaskQueue.step_decider then
activeTaskQueue:step_decider(self)
end
activeTaskEntry = activeTaskQueue.tasks:getFirst()
if not activeTaskEntry then

View File

@ -8,6 +8,11 @@ local JUMP_REPEAT_TIME = 1
-- the mob will reset the walk speed
local WALK_SPEED_RESET_THRESHOLD = 0.9
-- if the ratio of the current walk angle to the
-- target walk angle is lower than this number,
-- the mob will reset the walk angle
local WALK_ANGLE_RESET_THRESHOLD = 0.99
local MOVE_SPEED_MAX_DIFFERENCE = 0.01
local show_pathfinder_path = function(path)
@ -209,6 +214,7 @@ rp_mobs.microtasks.walk_straight = function(walk_speed, yaw, jump, max_timer)
return rp_mobs.create_microtask({
label = label,
on_start = function(self, mob)
minetest.log("error", "walk start")
self.statedata.jumping = false -- is true when mob is currently jumpin
self.statedata.jump_timer = 0 -- timer counting the time of the current jump, in seconds
self.statedata.stop = false -- is set to true if microtask is supposed to be finished after the current step finishes
@ -271,7 +277,8 @@ rp_mobs.microtasks.walk_straight = function(walk_speed, yaw, jump, max_timer)
realvel_hor.y = 0
local targetvel_hor = table.copy(vel)
targetvel_hor.y = 0
if vector.length(realvel_hor) < WALK_SPEED_RESET_THRESHOLD * vector.length(targetvel_hor) then
if (vector.length(realvel_hor) < WALK_SPEED_RESET_THRESHOLD * vector.length(targetvel_hor)) or
(0.01 > math.abs(vector.angle(vector.zero(), realvel_hor) - vector.angle(vector.zero(), targetvel_hor))) then
mob.object:set_velocity(vel)
end
end,

View File

@ -5,6 +5,7 @@ local WALK_DURATION_MIN = 3000
local WALK_DURATION_MAX = 4000
local FIND_LAND_DURATION_MIN = 7000
local FIND_LAND_DURATION_MAX = 10000
local FIND_SAFE_LAND_DURATION = 1000
local IDLE_DURATION_MIN = 500
local IDLE_DURATION_MAX = 2000
local RANDOM_SOUND_TIMER_MIN = 10000
@ -44,6 +45,11 @@ local function is_liquid(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
@ -105,6 +111,73 @@ local find_land_from_liquid = function(pos)
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
minetest.add_particlespawner({
amount = 1,
time = 0.01,
pos = up,
exptime = 1,
size = 2,
texture = {
name = "heart.png^[brighten",
alpha_tween = { 1, 0, start = 0.75 }
}
})
local dist = vector.distance(startpos, floor)
if not best_dist or dist < best_dist then
best_pos = up
best_dist = dist
best_angle = angle_rad
end
break
end
end
end
end
end
minetest.add_particlespawner({
amount = 1,
time = 0.01,
pos = best_pos,
exptime = 1,
size = 3,
texture = {
name = "heart.png",
alpha_tween = { 1, 0, start = 0.75 }
}
})
return best_pos, best_angle
end
local roam_decider = function(task_queue, mob)
local task_roam
local mt_sleep = rp_mobs.microtasks.sleep(math.random(IDLE_DURATION_MIN, IDLE_DURATION_MAX)/1000)
@ -113,6 +186,29 @@ local roam_decider = function(task_queue, mob)
if mob._env_node.name == "ignore" then
task_roam = rp_mobs.create_task({label="stand still"})
rp_mobs.add_microtask_to_task(mob, mt_sleep, task_roam)
elseif is_damaging(mob._env_node.name) then
task_roam = rp_mobs.create_task({label="escape from damaging node"})
local yaw
-- Find direction to walk to
local safepos, safeangle = find_safe_node_from_pos(mob.object:get_pos())
local walk_duration
-- Prefer walking towards safe place
if safepos and safeangle then
yaw = safeangle
walk_duration = math.random(FIND_LAND_DURATION_MIN, FIND_LAND_DURATION_MAX)/1000
else
-- If no safe place found, walk randomly (panic!)
yaw = math.random(0, 360) / 360 * (math.pi*2)
walk_duration = math.random(WALK_DURATION_MIN, WALK_DURATION_MAX)/1000
end
local mt_walk = rp_mobs.microtasks.walk_straight(WALK_SPEED, yaw, nil, walk_duration)
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
mt_walk.start_animation = "walk"
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_walk, task_roam)
elseif is_liquid(mob._env_node.name) then
task_roam = rp_mobs.create_task({label="swim upwards"})
local yaw = math.random(0, 360) / 360 * (math.pi*2)
@ -134,7 +230,7 @@ local roam_decider = function(task_queue, mob)
if landpos and landangle then
-- towards land
yaw = landangle
walk_duration = math.random(FIND_LAND_DURATION_MIN, FIND_LAND_DURATION_MAX)/1000
walk_duration = FIND_SAFE_LAND_DURATION
else
-- If no land found, go randomly on water
yaw = math.random(0, 360) / 360 * (math.pi*2)
@ -156,9 +252,9 @@ local roam_decider = function(task_queue, mob)
local mt_yaw = rp_mobs.microtasks.set_yaw(yaw)
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_sleep, 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_sleep, task_roam)
end
rp_mobs.add_task_to_task_queue(task_queue, task_roam)
@ -168,8 +264,12 @@ local roam_decider_step = function(task_queue, mob)
if mob._env_node then
local current = task_queue.tasks:getFirst()
if current and current.data then
if current.data.label == "roam land" then
if is_liquid(mob._env_node.name) 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
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
end
elseif current.data.label == "roam land" then
if is_damaging(mob._env_node.name) or is_liquid(mob._env_node.name) then
rp_mobs.end_current_task_in_task_queue(mob, task_queue)
end
elseif current.data.label == "swim upwards" then