Add advanced direction changeing mechanism

Fix slow mob causing random movement changes way to often
This commit is contained in:
sapier 2013-08-18 00:43:43 +02:00
parent eea475373d
commit 0e7a8c1d26
2 changed files with 214 additions and 128 deletions

View File

@ -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

View File

@ -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