Add advanced direction changeing mechanism
Fix slow mob causing random movement changes way to often
This commit is contained in:
parent
eea475373d
commit
0e7a8c1d26
@ -182,7 +182,7 @@ end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- name: precheck_movement(entity,movement_state)
|
||||
-- name: precheck_movement(entity,movement_state,pos_predicted,pos_predicted_quality)
|
||||
--
|
||||
--! @brief check if x/z movement results in invalid position and change
|
||||
-- movement if required
|
||||
@ -191,157 +191,234 @@ end
|
||||
--! @param entity mob to generate movement
|
||||
--! @param movement_state current state of movement
|
||||
--! @param pos_predicted position mob will be next
|
||||
--! @param pos_predicted_state suitability state of predicted position
|
||||
--! @param pos_predicted_quality quality of predicted position
|
||||
--! @return movement_state is changed!
|
||||
-------------------------------------------------------------------------------
|
||||
function direction_control.precheck_movement(entity,movement_state,pos_predicted,pos_predicted_state)
|
||||
function direction_control.precheck_movement(entity,movement_state,pos_predicted,pos_predicted_quality)
|
||||
|
||||
--next block mob is to be isn't a place where it can be so we need to change something
|
||||
if pos_predicted_state ~= "ok" and
|
||||
pos_predicted_state ~= "above_limit" and
|
||||
pos_predicted_state ~= "below_limit" then
|
||||
if movement_state.changed then
|
||||
--someone already changed something
|
||||
return
|
||||
end
|
||||
|
||||
-- mob would walk onto water or drop
|
||||
if movement_state.changed == false and
|
||||
( pos_predicted_state == "above_water" or
|
||||
pos_predicted_state == "drop" or
|
||||
pos_predicted_state == "drop_above_water")
|
||||
then
|
||||
dbg_mobf.pmovement_lvl1("MOBF: mob " .. entity.data.name .. " is going to walk on water or drop")
|
||||
local new_pos = environment.get_suitable_pos_same_level(movement_state.basepos,1,entity)
|
||||
local prefered_quality =
|
||||
environment.evaluate_state( pos_predicted_quality, LT_GOOD_POS)
|
||||
|
||||
-- ok predicted pos isn't as good as we'd wanted it to be let's find out why
|
||||
if not prefered_quality then
|
||||
|
||||
local mob_is_safe = environment.evaluate_state( pos_predicted_quality,LT_SAFE_POS)
|
||||
|
||||
if movement_state.current_quality == nil then
|
||||
movement_state.current_quality = environment.pos_quality(
|
||||
movement_state.basepos,
|
||||
entity
|
||||
)
|
||||
end
|
||||
|
||||
--try to find at least a possible position
|
||||
if environment.compare_state(movement_state.current_quality,pos_predicted_quality) > 0 and
|
||||
pos_predicted_quality.media_quality == MQ_IN_MEDIA then
|
||||
--movement state is better than old one so we're fine
|
||||
return
|
||||
end
|
||||
|
||||
local walking_at_edge =
|
||||
environment.evaluate_state( pos_predicted_quality,LT_SAFE_EDGE_POS)
|
||||
|
||||
if walking_at_edge then
|
||||
--mob center still on ground but at worst walking at edge, do nothing
|
||||
return
|
||||
end
|
||||
|
||||
local drop_pending =
|
||||
(pos_predicted_quality.geometry_quality <= GQ_PARTIAL and
|
||||
pos_predicted_quality.center_geometry_quality <= GQ_NONE) or
|
||||
pos_predicted_quality.surface_quality_min <= SQ_WATER
|
||||
|
||||
if drop_pending then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " .. entity.data.name .. " is going to walk on water or drop")
|
||||
|
||||
local new_pos = environment.get_pos_same_level(movement_state.basepos,1,entity,
|
||||
function(quality)
|
||||
return environment.evaluate_state(quality,LT_SAFE_EDGE_POS)
|
||||
end
|
||||
)
|
||||
|
||||
if new_pos == nil then
|
||||
dbg_mobf.pmovement_lvl1("MOBF: mob " .. entity.data.name .. " no prefect fitting position found")
|
||||
new_pos = environment.get_suitable_pos_same_level(movement_state.basepos,1,entity,true)
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " .. entity.data.name .. " trying edge pos")
|
||||
new_pos = environment.get_pos_same_level(movement_state.basepos,1,entity,
|
||||
function(quality)
|
||||
return environment.evaluate_state(quality,LT_EDGE_POS)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
if new_pos == nil then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " .. entity.data.name .. " trying relaxed surface")
|
||||
new_pos = environment.get_pos_same_level(movement_state.basepos,1,entity,
|
||||
function(quality)
|
||||
return environment.evaluate_state(quality,LT_EDGE_POS_GOOD_CENTER)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
if new_pos == nil then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " .. entity.data.name .. " trying even more relaxed surface")
|
||||
new_pos = environment.get_pos_same_level(movement_state.basepos,1,entity,
|
||||
function(quality)
|
||||
return environment.evaluate_state(quality,LT_EDGE_POS_POSSIBLE_CENTER)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
if new_pos ~= nil then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: redirecting to safe position .. " .. printpos(new_pos))
|
||||
movement_state.accel_to_set = movement_generic.get_accel_to(new_pos,entity)
|
||||
pos_predicted = movement_generic.predict_next_block( movement_state.basepos,
|
||||
movement_state.current_velocity,
|
||||
movement_state.accel_to_set)
|
||||
pos_predicted_state = environment.pos_is_ok({x=pos_predicted.x,y=pos_predicted.y+1,z=pos_predicted.z},entity)
|
||||
for i=0,10,1 do
|
||||
if pos_predicted_state == "ok" or
|
||||
pos_predicted_state == "possible_surface" then
|
||||
break
|
||||
end
|
||||
movement_state.accel_to_set = movement_generic.get_accel_to(new_pos,entity)
|
||||
pos_predicted = movement_generic.predict_next_block( movement_state.basepos,
|
||||
movement_state.current_velocity,
|
||||
movement_state.accel_to_set)
|
||||
pos_predicted_state = environment.pos_is_ok({x=pos_predicted.x,y=pos_predicted.y+1,z=pos_predicted.z},entity)
|
||||
end
|
||||
|
||||
if pos_predicted_state == "ok" or
|
||||
pos_predicted_state == "possible_surface" then
|
||||
dbg_mobf.pmovement_lvl1("MOBF: redirecting to safe position .. " .. printpos(new_pos) .. " failed!")
|
||||
end
|
||||
movement_state.changed = true
|
||||
return
|
||||
else
|
||||
local current_state = environment.pos_is_ok(movement_state.basepos,entity)
|
||||
|
||||
--animal is safe atm stop it to avoid doing silly things
|
||||
if current_state == "ok" or
|
||||
current_state == "possible_surface" then
|
||||
--no suitable pos found, if mob is safe atm just stop it
|
||||
if mob_is_safe then
|
||||
entity.object:setvelocity({x=0,y=0,z=0})
|
||||
movement_state.accel_to_set = {x=0,y=nil,z=0}
|
||||
movement_state.changed = true
|
||||
dbg_mobf.pmovement_lvl2("MOBF: couldn't find acceleration but mob is safe where it is")
|
||||
else
|
||||
--apply random acceleration to avoid permanent stuck mobs maybe mobs should be deleted instead
|
||||
movement_state.accel_to_set = direction_control.get_random_acceleration(entity.data.movement.min_accel,
|
||||
entity.data.movement.max_accel,entity.object:getyaw(),0)
|
||||
movement_state.changed = true
|
||||
dbg_mobf.pmovement_lvl2("MOBF: couldn't find acceleration mob ain't safe either, just move on with random movement")
|
||||
end
|
||||
end
|
||||
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " .. entity.data.name .. " didn't find a way to fix drop trying random")
|
||||
--make mgen change direction randomly
|
||||
movement_state.force_change = true
|
||||
return
|
||||
end
|
||||
|
||||
if movement_state.changed == false and pos_predicted_state == "collision_jumpable" then
|
||||
dbg_mobf.movement_lvl1("mob is about to collide")
|
||||
if environment.pos_is_ok({x=pos_predicted.x,y=pos_predicted.y+1,z=pos_predicted.z},entity) == "ok" then
|
||||
if math.random() < ( entity.dynamic_data.movement.mpattern.jump_up * PER_SECOND_CORRECTION_FACTOR) then
|
||||
local node_at_predicted_pos = minetest.get_node(pos_predicted)
|
||||
dbg_mobf.pmovement_lvl2("MOBF: velocity is:" .. printpos(movement_state.current_velocity) .. " position is: "..printpos(pos) )
|
||||
dbg_mobf.pmovement_lvl2("MOBF: estimated position was: "..printpos(pos_predicted))
|
||||
dbg_mobf.pmovement_lvl2("MOBF: predicted node state is: " .. environment.pos_is_ok(pos_predicted,entity))
|
||||
--if node_at_predicted_pos ~= nil then
|
||||
--dbg_mobf.movement_lvl1("MOBF: jumping onto: " .. node_at_predicted_pos.name)
|
||||
--end
|
||||
movement_state.accel_to_set = { x=0,y=nil,z=0 }
|
||||
movement_state.changed = true
|
||||
|
||||
--todo check if y pos is ok?!
|
||||
local jumppos = {x=pos_predicted.x,y=movement_state.centerpos.y+1,z=pos_predicted.z}
|
||||
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " ..entity.data.name .. " is jumping, moving to:" .. printpos(jumppos))
|
||||
dbg_mobf.pmovement_lvl2("MOBF: target pos node state is: " .. environment.pos_is_ok(jumppos,entity))
|
||||
|
||||
entity.object:moveto(jumppos)
|
||||
--TODO set movement state positions
|
||||
--movement_state.basepos=
|
||||
--movement_state.centerpos=
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--redirect mob to block thats not above its current level
|
||||
--or jump if possible
|
||||
if movement_state.changed == false and pos_predicted_state == "collision" then
|
||||
dbg_mobf.pmovement_lvl1("MOBF: mob is about to collide")
|
||||
|
||||
local new_pos = environment.get_suitable_pos_same_level(movement_state.basepos,1,entity)
|
||||
--check if mob is going to be somewhere where it can't be
|
||||
if pos_predicted_quality.media_quality ~= MQ_IN_MEDIA then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: collision pending "
|
||||
.. printpos(movement_state.basepos) .. "-->"
|
||||
.. printpos(pos_predicted))
|
||||
|
||||
--try to find a better position at same level
|
||||
local new_pos =
|
||||
environment.get_suitable_pos_same_level(movement_state.basepos,1,entity)
|
||||
|
||||
if new_pos == nil then
|
||||
new_pos = environment.get_suitable_pos_same_level(movement_state.basepos,1,entity,true)
|
||||
end
|
||||
|
||||
--there is at least one direction to go
|
||||
if new_pos ~= nil then
|
||||
if new_pos ~= nil then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " ..entity.data.name
|
||||
.. " redirecting to:" .. printpos(new_pos))
|
||||
local new_predicted_state = nil
|
||||
local new_predicted_pos = nil
|
||||
for i=1,5,1 do
|
||||
movement_state.accel_to_set = movement_generic.get_accel_to(new_pos,entity)
|
||||
--TODO check if acceleration is enough
|
||||
new_predicted_pos =
|
||||
movement_generic.predict_enter_next_block( entity,
|
||||
movement_state.basepos,
|
||||
movement_state.current_velocity,
|
||||
movement_state.accel_to_set)
|
||||
new_predicted_state = environment.pos_quality(
|
||||
new_predicted_pos,
|
||||
entity
|
||||
)
|
||||
if new_predicted_state.media_quality == MQ_IN_MEDIA then
|
||||
break
|
||||
end
|
||||
end
|
||||
if new_predicted_state.media_quality ~= MQ_IN_MEDIA then
|
||||
movement_state.accel_to_set = movement_state.current_acceleration
|
||||
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " ..entity.data.name
|
||||
.. " acceleration not enough to avoid collision try to jump")
|
||||
if math.random() <
|
||||
( entity.dynamic_data.movement.mpattern.jump_up * PER_SECOND_CORRECTION_FACTOR) then
|
||||
local upper_pos = {
|
||||
x= pos_predicted.x,
|
||||
y= pos_predicted.y +1,
|
||||
z= pos_predicted.z
|
||||
}
|
||||
|
||||
local upper_quality = environment.pos_quality(
|
||||
upper_pos,
|
||||
entity
|
||||
)
|
||||
|
||||
if environment.evaluate_state( upper_quality,LT_EDGE_POS) then
|
||||
|
||||
entity.object:setvelocity(
|
||||
{x=movement_state.current_velocity.x,
|
||||
y=5,
|
||||
z=movement_state.current_velocity.z})
|
||||
end
|
||||
end
|
||||
end
|
||||
movement_state.changed = true
|
||||
return
|
||||
end
|
||||
|
||||
--try to find a better position above
|
||||
new_pos = environment.get_suitable_pos_same_level({ x=movement_state.basepos.x,
|
||||
y=movement_state.basepos.y+1,
|
||||
z=movement_state.basepos.z},
|
||||
1,entity)
|
||||
|
||||
if new_pos == nil then
|
||||
new_pos = environment.get_suitable_pos_same_level({ x=movement_state.basepos.x,
|
||||
y=movement_state.basepos.y+1,
|
||||
z=movement_state.basepos.z},
|
||||
1,entity)
|
||||
end
|
||||
|
||||
if new_pos ~= nil then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " ..entity.data.name
|
||||
.. " seems to be locked in, jumping to:" .. printpos(new_pos))
|
||||
|
||||
entity.object:setvelocity({x=0,
|
||||
y=5.5,
|
||||
z=0})
|
||||
movement_state.accel_to_set = movement_generic.get_accel_to(new_pos,entity)
|
||||
movement_state.changed = true
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " ..entity.data.name
|
||||
.. " unable to fix collision try random")
|
||||
--a collision is going to happen force change of direction
|
||||
movement_state.force_change = true
|
||||
return
|
||||
end
|
||||
|
||||
local suboptimal_surface =
|
||||
environment.evaluate_state( pos_predicted_quality,
|
||||
{ old_state=nil,
|
||||
min_media=MQ_IN_MEDIA,
|
||||
min_geom=GQ_PARTIAL,
|
||||
min_geom_center=nil,
|
||||
min_min_surface=SQ_WRONG,
|
||||
min_max_surface=SQ_POSSIBLE,
|
||||
min_center_surface=nil })
|
||||
|
||||
if suboptimal_surface then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: suboptimal positiond detected trying to find better pos")
|
||||
--try to find a better position at same level
|
||||
local new_pos =
|
||||
environment.get_suitable_pos_same_level(movement_state.basepos,1,entity)
|
||||
|
||||
if new_pos ~= nil then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: redirecting to better position .. " .. printpos(new_pos))
|
||||
movement_state.accel_to_set = movement_generic.get_accel_to(new_pos,entity)
|
||||
movement_state.changed = true
|
||||
return
|
||||
else
|
||||
local jumppos = nil
|
||||
--there ain't any acceptable pos at same level try to find one above current position
|
||||
new_pos = environment.get_suitable_pos_same_level({ x=movement_state.basepos.x,
|
||||
y=movement_state.basepos.y+1,
|
||||
z=movement_state.basepos.z},
|
||||
1,entity)
|
||||
|
||||
if new_pos ~= nil then
|
||||
jumppos = {x=new_pos.x,y=movement_state.centerpos.y+1,z=new_pos.z}
|
||||
end
|
||||
|
||||
--there ain't any acceptable pos at same level try to find one below current position
|
||||
new_pos = environment.get_suitable_pos_same_level({ x=movement_state.basepos.x,
|
||||
y=movement_state.basepos.y-1,
|
||||
z=movement_state.basepos.z},
|
||||
1,entity)
|
||||
|
||||
if new_pos ~= nil then
|
||||
jumppos = {x=new_pos.x,y=movement_state.centerpos.y-1,z=new_pos.z}
|
||||
end
|
||||
|
||||
|
||||
if jumppos ~= nil then
|
||||
dbg_mobf.pmovement_lvl2("MOBF: mob " ..entity.data.name .. " seems to be locked in, moving to:" .. printpos(jumppos))
|
||||
entity.object:moveto(jumppos)
|
||||
|
||||
movement_state.accel_to_set = { x=0,y=nil,z=0 }
|
||||
movement_state.changed = true
|
||||
end
|
||||
-- pos isn't critical don't do anything
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
--generic try to solve situation eg wrong surface
|
||||
if movement_state.changed == false then
|
||||
dbg_mobf.pmovement_lvl1("MOBF: generic try to resolve state " .. pos_predicted_state .. " for mob " .. entity.data.name .. " "..printpos(movement_state.basepos))
|
||||
movement_state.accel_to_set = direction_control.changeaccel(movement_state.basepos,
|
||||
entity,movement_state.current_velocity)
|
||||
if movement_state.accel_to_set ~= nil then
|
||||
movement_state.changed = true
|
||||
end
|
||||
end
|
||||
mobf_print("Unhandled suboptimal state:" .. pos_predicted_quality.tostring(pos_predicted_quality))
|
||||
movement_state.force_change = true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -164,7 +164,13 @@ function movement_gen.callback(entity)
|
||||
entity.data.name .. " "..printpos(movement_state.basepos))
|
||||
|
||||
movement_gen.fix_runaway(entity,movement_state)
|
||||
movement_gen.fix_to_slow(entity,movement_state)
|
||||
|
||||
--don't do slowness check each callback
|
||||
if entity.dynamic_data.movement.ts_last_slowcheck == nil or
|
||||
entity.dynamic_data.movement.ts_last_slowcheck +2 < movement_state.now then
|
||||
movement_gen.fix_to_slow(entity,movement_state)
|
||||
entity.dynamic_data.movement.ts_last_slowcheck = movement_state.now
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------
|
||||
@ -193,16 +199,18 @@ function movement_gen.callback(entity)
|
||||
local pos_predicted_state = environment.pos_is_ok(pos_predicted,entity)
|
||||
dbg_mobf.pmovement_lvl3("MOBF: Pos predicted state ".. entity.data.name
|
||||
.. ": " .. pos_predicted_state)
|
||||
|
||||
-- Y-Movement
|
||||
if movement_state.changed == false then
|
||||
height_level_control.precheck_movement(entity,movement_state,
|
||||
pos_predicted,pos_predicted_state)
|
||||
end
|
||||
|
||||
local pos_predicted_quality = environment.pos_quality(pos_predicted,entity)
|
||||
-- X/Z-Movement
|
||||
if movement_state.changed == false then
|
||||
direction_control.precheck_movement(entity,movement_state,
|
||||
pos_predicted,pos_predicted_state)
|
||||
pos_predicted,pos_predicted_quality)
|
||||
end
|
||||
|
||||
end
|
||||
@ -312,6 +320,7 @@ function movement_gen.init_dynamic_data(entity,now)
|
||||
ts_orientation_upd = now,
|
||||
mpattern = mobf_rtd.movement_patterns[entity.data.movement.pattern],
|
||||
orientation_fix_needed = true,
|
||||
ts_last_slowcheck = now,
|
||||
}
|
||||
|
||||
entity.dynamic_data.movement = data
|
||||
@ -402,7 +411,7 @@ function movement_gen.fix_to_slow(entity,movement_state)
|
||||
xzspeed < entity.data.movement.min_speed) or
|
||||
xzspeed == nil then
|
||||
|
||||
dbg_mobf.pmovement_lvl3("MOBF: too slow! vxz=" .. xzspeed)
|
||||
dbg_mobf.pmovement_lvl2("MOBF: too slow! vxz=" .. xzspeed)
|
||||
--use normal speed change handling
|
||||
movement_state.force_change = true
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user