From 73fb95bfb42c5a1d38b23b5c642c967924659d90 Mon Sep 17 00:00:00 2001 From: sapier Date: Mon, 16 Dec 2013 18:32:40 +0100 Subject: [PATCH] Use better algorithm for correcting too fast mob movement --- mobf/mgen_follow/main_follow.lua | 198 ++++++++++++++++--------------- 1 file changed, 101 insertions(+), 97 deletions(-) diff --git a/mobf/mgen_follow/main_follow.lua b/mobf/mgen_follow/main_follow.lua index 50da367..efdb566 100644 --- a/mobf/mgen_follow/main_follow.lua +++ b/mobf/mgen_follow/main_follow.lua @@ -1,8 +1,8 @@ ------------------------------------------------------------------------------- -- Mob Framework Mod by Sapier --- +-- -- You may copy, use, modify or do nearly anything except removing this --- copyright notice. +-- copyright notice. -- And of course you are NOT allow to pretend you have written it. -- --! @file main_follow.lua @@ -12,10 +12,10 @@ --! @date 2012-08-09 -- --! @defgroup mgen_follow MGEN: follow movement generator ---! @brief A movement generator creating movement that trys to follow a moving +--! @brief A movement generator creating movement that trys to follow a moving --! target or reach a given point on map --! @ingroup framework_int ---! @{ +--! @{ -- Contact sapier a t gmx net ------------------------------------------------------------------------------- @@ -51,21 +51,21 @@ mgen_follow.name = "follow_mov_gen" function mgen_follow.identify_movement_state(ownpos,targetpos) mobf_assert_backtrace(ownpos ~= nil) mobf_assert_backtrace(targetpos ~= nil) - + local same_height_delta = 0.1 - + local los = mobf_line_of_sight(ownpos,targetpos) - + if ownpos.y > targetpos.y - same_height_delta and ownpos.y < targetpos.y + same_height_delta then - + if los then return "same_height_los" else return "same_height_no_los" end end - + if ownpos.y < targetpos.y then if los then return "below_los" @@ -81,7 +81,7 @@ function mgen_follow.identify_movement_state(ownpos,targetpos) return "above_no_los" end end - + return "unknown" end @@ -103,14 +103,14 @@ function mgen_follow.handleteleport(entity,now,targetpos) if (entity.dynamic_data.movement.last_next_to_target ~= nil ) then local time_since_next_to_target = now - entity.dynamic_data.movement.last_next_to_target - - dbg_mobf.fmovement_lvl3("MOBF: time since next to target: " .. time_since_next_to_target .. + + dbg_mobf.fmovement_lvl3("MOBF: time since next to target: " .. time_since_next_to_target .. " delay: " .. dump(entity.data.movement.teleportdelay) .. " teleportsupport: " .. dump(entity.dynamic_data.movement.teleportsupport)) if (entity.dynamic_data.movement.teleportsupport) and time_since_next_to_target > entity.data.movement.teleportdelay then - + --check targetpos try to playe above if not valid local maxoffset = 5 local current_offset = 0 @@ -123,9 +123,9 @@ function mgen_follow.handleteleport(entity,now,targetpos) print("MOBF: teleport target within block trying above: " .. current_offset) current_offset = current_offset +1 end - + targetpos.y = targetpos.y + current_offset - + --adjust to collisionbox of mob if entity.collisionbox[2] < -0.5 then targetpos.y = targetpos.y - (entity.collisionbox[2] + 0.49) @@ -153,20 +153,20 @@ end function mgen_follow.callback(entity,now) dbg_mobf.fmovement_lvl3("MOBF: Follow mgen callback called") - + if entity == nil then mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!!: called movement gen without entity!") return end - + if entity.dynamic_data == nil or entity.dynamic_data.movement == nil then mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!!: >" ..entity.data.name .. "< removed=" .. dump(entity.removed) .. " entity=" .. tostring(entity) .. " probab movement callback") return end - + local follow_speedup = {x=10,y=2,z=10 } - + if entity.data.movement.follow_speedup ~= nil then if type(entity.data.movement.follow_speedup) == "table" then follow_speedup = entity.data.movement.follow_speedup @@ -175,17 +175,17 @@ function mgen_follow.callback(entity,now) follow_speedup.z= entity.data.movement.follow_speedup end end - + --check max speed limit mgen_follow.checkspeed(entity) - - + + --check environment local basepos = entity.getbasepos(entity) local pos_quality = environment.pos_quality(basepos,entity) - + if environment.evaluate_state(pos_quality, LT_GOOD_POS) or - (entity.data.movement.canfly and + (entity.data.movement.canfly and environment.evaluate_state(pos_quality,LT_GOOD_FLY_POS)) then local toset = { x= basepos.x, @@ -194,21 +194,21 @@ function mgen_follow.callback(entity,now) --save known good position entity.dynamic_data.movement.last_pos_in_env = toset end - + if pos_quality.media_quality == MQ_IN_AIR or -- wrong media pos_quality.media_quality == MQ_IN_WATER or -- wrong media pos_quality.geometry_quality == GQ_NONE or -- no ground contact (TODO this was drop above water before) pos_quality.surface_quality_min == SQ_WATER then -- above water - - + + if entity.dynamic_data.movement.invalid_env_count == nil then entity.dynamic_data.movement.invalid_env_count = 0 end - - entity.dynamic_data.movement.invalid_env_count = + + entity.dynamic_data.movement.invalid_env_count = entity.dynamic_data.movement.invalid_env_count + 1 - - + + --don't change at first invalid pos but give some steps to cleanup by --other less invasive mechanisms if entity.dynamic_data.movement.invalid_env_count > 10 then @@ -218,7 +218,7 @@ function mgen_follow.callback(entity,now) basepos = entity.getbasepos(entity) else local newpos = environment.get_suitable_pos_same_level(basepos,1,entity,true) - + if newpos == nil then newpos = environment.get_suitable_pos_same_level( { x=basepos.x, @@ -226,7 +226,7 @@ function mgen_follow.callback(entity,now) z=basepos.z } ,1,entity,true) end - + if newpos == nil then newpos = environment.get_suitable_pos_same_level( { x=basepos.x, @@ -234,7 +234,7 @@ function mgen_follow.callback(entity,now) z=basepos.z } ,1,entity,true) end - + if newpos == nil then dbg_mobf.fmovement_lvl1("MOBF: no way to fix it removing mob") spawning.remove(entity,"mgen_follow poscheck") @@ -248,116 +248,116 @@ function mgen_follow.callback(entity,now) else entity.dynamic_data.movement.invalid_env_count = 0 end - - if pos_quality.level_quality ~= LQ_OK and + + if pos_quality.level_quality ~= LQ_OK and entity.data.movement.canfly then local current_accel = entity.object:getacceleration() - + if pos_quality.level_quality == LQ_ABOVE then if current_accel.y >= 0 then current_accel.y = - entity.data.movement.max_accel end end - + if pos_quality.level_quality == LQ_BELOW then local current_accel = entity.object:getacceleration() if current_accel.y <= 0 then current_accel.y = entity.data.movement.max_accel end end - + entity.object:setacceleration(current_accel) return end - + --fixup height fixup if entity.data.movement.canfly then local current_accel = entity.object:getacceleration() - + if current_accel.y ~= 0 then current_accel.y = 0 entity.object:setacceleration(current_accel) end end - + if entity.dynamic_data.movement.target ~= nil or entity.dynamic_data.movement.guardspawnpoint then dbg_mobf.fmovement_lvl3("MOBF: Target available") --calculate distance to target local targetpos = nil - + if entity.dynamic_data.movement.target ~= nil then dbg_mobf.fmovement_lvl3("MOBF: have moving target") - + if not mobf_is_pos(entity.dynamic_data.movement.target) then targetpos = entity.dynamic_data.movement.target:getpos() else targetpos = entity.dynamic_data.movement.target end end - + if targetpos == nil and entity.dynamic_data.movement.guardspawnpoint == true then dbg_mobf.fmovement_lvl3("MOBF: non target selected") - targetpos = entity.dynamic_data.spawning.spawnpoint + targetpos = entity.dynamic_data.spawning.spawnpoint end - + if targetpos == nil then - mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: " .. entity.data.name - .. " don't have targetpos " + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: " .. entity.data.name + .. " don't have targetpos " .. "SP: " .. dump(entity.dynamic_data.spawning.spawnpoint) .. " TGT: " .. dump(entity.dynamic_data.movement.target)) return end - + local distance = mobf_calc_distance(basepos,targetpos) - + if mobf_line_of_sight({x=basepos.x,y=basepos.y+1,z=basepos.z}, {x=targetpos.x,y=targetpos.y+1,z=targetpos.z}) == false then dbg_mobf.fmovement_lvl3("MOBF: no line of sight") --TODO teleport support? --TODO other ways to handle this? - --return + --return end --dbg_mobf.fmovement_lvl3("MOBF: line of sight") - + local max_distance = entity.dynamic_data.movement.max_distance - + if max_distance == nil then max_distance = 1 end - + --check if mob needs to move towards target - dbg_mobf.fmovement_lvl3("MOBF: max distance is set to : " + dbg_mobf.fmovement_lvl3("MOBF: max distance is set to : " .. max_distance .. " dist: " .. distance) if distance > max_distance then entity.dynamic_data.movement.was_moving_last_step = true - + if mgen_follow.handleteleport(entity,now,targetpos) then return end dbg_mobf.fmovement_lvl3("MOBF: distance:" .. distance) - + local current_state = mgen_follow.identify_movement_state(basepos,targetpos) local handled = false - - if handled == false and + + if handled == false and (current_state == "same_height_los" or current_state == "above_los" or current_state == "above_no_los" ) then dbg_mobf.fmovement_lvl3("MOBF: \t Case 1: " .. current_state) local accel_to_set = movement_generic.get_accel_to(targetpos,entity,true) - - handled = + + handled = mgen_follow.set_acceleration(entity, accel_to_set, follow_speedup, basepos) end - + if handled == false and (current_state == "below_los" or current_state == "below_no_los" or @@ -365,23 +365,23 @@ function mgen_follow.callback(entity,now) dbg_mobf.fmovement_lvl3("MOBF: \t Case 2: " .. current_state) local accel_to_set = movement_generic.get_accel_to(targetpos,entity) - + --seems to be a flying mob if (accel_to_set.y >0) then - handled = + handled = mgen_follow.set_acceleration(entity, accel_to_set, follow_speedup, basepos) else local current_velocity = entity.object:getvelocity() - local predicted_pos = + local predicted_pos = movement_generic.predict_next_block(basepos, current_velocity, accel_to_set) - + --TODO replace by quality based mechanism!!!!!!!------------ - local pos_state = + local pos_state = environment.pos_is_ok(predicted_pos,entity) if pos_state == "collision_jumpable" then local pos_to_set = entity.object:getpos() @@ -390,11 +390,11 @@ function mgen_follow.callback(entity,now) basepos.y=basepos.y+1.1 end ------------------------------------------------------------ - - dbg_mobf.fmovement_lvl3("MOBF: setting acceleration to: " + + dbg_mobf.fmovement_lvl3("MOBF: setting acceleration to: " .. printpos(accel_to_set) .. " predicted_state: " .. pos_state); - handled = + handled = mgen_follow.set_acceleration(entity, accel_to_set, follow_speedup, @@ -418,16 +418,16 @@ function mgen_follow.callback(entity,now) local yaccel = environment.get_default_gravity(basepos, entity.environment.media, entity.data.movement.canfly) - + dbg_mobf.fmovement_lvl3("MOBF: next to target") entity.object:setvelocity({x=0,y=0,z=0}) entity.object:setacceleration({x=0,y=yaccel,z=0}) entity.dynamic_data.movement.last_next_to_target = now end end - + else - --TODO evaluate if this is an error case + --TODO evaluate if this is an error case end end @@ -440,17 +440,17 @@ end ------------------------------------------------------------------------------- function mgen_follow.next_block_ok(entity,pos,acceleration,velocity) local current_velocity = velocity - + if current_velocity == nil then current_velocity = entity.object:getvelocity() end - + local predicted_pos = movement_generic.predict_next_block(pos,current_velocity,acceleration) - + local quality = environment.pos_quality(predicted_pos,entity) - + return ( - (quality.media_quality == MQ_IN_MEDIA) and + (quality.media_quality == MQ_IN_MEDIA) and (quality.level_quality == LQ_OK) and ( (quality.surface_quality_min == Q_UNKNOWN) or @@ -491,18 +491,18 @@ function mgen_follow.init_dynamic_data(entity,now) max_distance = entity.data.movement.max_distance, invalid_env_count = 0, } - + if entity.data.movement.guardspawnpoint ~= nil and entity.data.movement.guardspawnpoint then dbg_mobf.fmovement_lvl3("MOBF: setting guard point to: " .. printpos(entity.dynamic_data.spawning.spawnpoint)) data.guardspawnpoint = true end - + if entity.data.movement.teleportdelay~= nil then data.last_next_to_target = now data.teleportsupport = true end - + entity.dynamic_data.movement = data end @@ -519,22 +519,26 @@ function mgen_follow.checkspeed(entity) local current_velocity = entity.object:getvelocity() - local xzspeed = + local xzspeed = mobf_calc_scalar_speed(current_velocity.x,current_velocity.z) if (xzspeed > entity.data.movement.max_speed) then - - --preserver orientation when correcting speed - local dir = mobf_calc_yaw(current_velocity.x,current_velocity.z) - local velocity_to_set = mobf_calc_vector_components(dir,entity.data.movement.max_speed * 0.25) - - velocity_to_set.y=current_velocity.y - - entity.object:setvelocity(velocity_to_set) - + + local direction = mobf_calc_yaw(current_velocity.x, + current_velocity.z) + + --reduce speed to 90% of current speed + local new_speed = mobf_calc_vector_components(direction,xzspeed*0.9) + + local current_accel = entity.object:getacceleration() + + new_speed.y = current_velocity.y + entity.object:setvelocity(new_speed) + entity.object:setacceleration({x=0,y=current_accel.y,z=0}) + return true end - + return false end @@ -554,7 +558,7 @@ function mgen_follow.set_acceleration(entity,accel,speedup,pos) accel.x = accel.x*speedup.x accel.z = accel.z*speedup.z - + if entity.data.movement.canfly then accel.y = accel.y*speedup.y end @@ -571,7 +575,7 @@ function mgen_follow.set_acceleration(entity,accel,speedup,pos) else local current_velocity = entity.object:getvelocity() current_velocity.y = 0 - + if mgen_follow.next_block_ok(entity,pos,{x=0,y=0,z=0},current_velocity) then accel_to_set = {x=0,y=0,z=0} entity.object:setvelocity(current_velocity) @@ -579,11 +583,11 @@ function mgen_follow.set_acceleration(entity,accel,speedup,pos) return true end end - + dbg_mobf.fmovement_lvl1( "MOBF: \t acceleration " .. printpos(accel) .. " would result in invalid position not applying!") - + return false end