------------------------------------------------------------------------------- -- Mob Framework Mod by Sapier -- -- You may copy, use, modify or do nearly anything except removing this -- copyright notice. -- And of course you are NOT allow to pretend you have written it. -- --! @file movement_generic.lua --! @brief generic movement related functions --! @copyright Sapier --! @author Sapier --! @date 2012-08-09 -- --! @defgroup generic_movement Generic movement functions --! @brief Movement related functions used by different movement generators --! @ingroup framework_int --! @{ -- Contact sapier a t gmx net ------------------------------------------------------------------------------- mobf_assert_backtrace(movement_generic == nil) movement_generic = {} --!@} ------------------------------------------------------------------------------- -- @function [parent=#movement_generic] get_accel_to(new_pos,entity,ymovement) -- --! @brief calculate a random speed directed to new_pos -- --! @param new_pos position to go to --! @param entity mob to move --! @param ymovement current movement in y direction --! @return { x,y,z } random speed directed to new_pos ------------------------------------------------------------------------------- -- function movement_generic.get_accel_to(new_pos,entity,ymovement) if new_pos == nil or entity == nil then minetest.log(LOGLEVEL_CRITICAL, "MOBF: movement_generic.get_accel_to : Invalid parameters") end local old_pos = entity.object:getpos() local node = minetest.get_node(old_pos) local maxaccel = entity.data.movement.max_accel local minaccel = entity.data.movement.min_accel local yaccel = environment.get_default_gravity(old_pos, entity.environment.media, entity.data.movement.canfly) mobf_assert_backtrace(yaccel ~= nil) --calculate plane direction to target local xz_direction = mobf_calc_yaw(new_pos.x-old_pos.x,new_pos.z-old_pos.z) --find a new speed local absolute_accel = minaccel + (maxaccel - minaccel) * math.random() local new_accel_vector = nil --flying mob calculate accel towards target if entity.data.movement.canfly and yaccel == 0 then local xz_direction,xy_direction = mobf_calc_direction(old_pos,new_pos) new_accel_vector = mobf_calc_vector_components_3d(xz_direction, xy_direction, absolute_accel) if (new_pos.y > old_pos.y) then mobf_assert_backtrace(new_accel_vector.y >= 0) end if (new_pos.y < old_pos.y) then mobf_assert_backtrace(new_accel_vector.y <= 0) end else new_accel_vector = mobf_calc_vector_components(xz_direction,absolute_accel) new_accel_vector.y = yaccel end return new_accel_vector end ------------------------------------------------------------------------------- -- @function [parent=#movement_generic] calc_new_pos(pos,acceleration,prediction_time) -- --! @brief calc the position a mob would be after a specified time -- this doesn't handle velocity changes due to colisions -- --! @param pos position --! @param acceleration acceleration to predict pos --! @param prediction_time time to predict pos --! @param current_velocity current velocity of mob --! @return { x,y,z } position after specified time ------------------------------------------------------------------------------- function movement_generic.calc_new_pos(pos,acceleration,prediction_time,current_velocity) local predicted_pos = {x=pos.x,y=pos.y,z=pos.z} predicted_pos.x = predicted_pos.x + current_velocity.x * prediction_time + (acceleration.x/2)*math.pow(prediction_time,2) predicted_pos.z = predicted_pos.z + current_velocity.z * prediction_time + (acceleration.z/2)*math.pow(prediction_time,2) return predicted_pos end ------------------------------------------------------------------------------- -- @function [parent=#movement_generic] predict_next_block(pos,velocity,acceleration) -- --! @brief predict next block based on pos velocity and acceleration -- --! @param pos current position --! @param velocity current velocity --! @param acceleration current acceleration --! @return { x,y,z } position of next block ------------------------------------------------------------------------------- function movement_generic.predict_next_block(pos,velocity,acceleration) local prediction_time = 2 local pos_predicted = movement_generic.calc_new_pos(pos, acceleration, prediction_time, velocity ) local count = 1 --check if after prediction time we would have traveled more than one block and adjust to not predict to far while mobf_calc_distance(pos,pos_predicted) > 1 do pos_predicted = movement_generic.calc_new_pos(pos, acceleration, prediction_time - (count*0.1), velocity ) if (prediction_time - (count*0.1)) < 0 then minetest.log(LOGLEVEL_WARNING,"MOBF: Bug!!!! didn't find a suitable prediction time. Mob will move more than one block within prediction period") break end count = count +1 end return pos_predicted end ------------------------------------------------------------------------------- -- @function [parent=#movement_generic] predict_enter_next_block(entity,pos,velocity,acceleration) -- --! @brief predict next block based on pos velocity and acceleration -- --! @param entity entitiy to do prediction for --! @param pos current position --! @param velocity current velocity --! @param acceleration current acceleration --! @return { x,y,z } position of next block ------------------------------------------------------------------------------- function movement_generic.predict_enter_next_block(entity,pos,velocity,acceleration) local cornerpositions = {} table.insert(cornerpositions,{x=pos.x + entity.collisionbox[4] -0.01,y=pos.y,z=pos.z + entity.collisionbox[6] -0.01}) table.insert(cornerpositions,{x=pos.x + entity.collisionbox[4] -0.01,y=pos.y,z=pos.z + entity.collisionbox[3] +0.01}) table.insert(cornerpositions,{x=pos.x + entity.collisionbox[1] +0.01,y=pos.y,z=pos.z + entity.collisionbox[6] -0.01}) table.insert(cornerpositions,{x=pos.x + entity.collisionbox[1] +0.01,y=pos.y,z=pos.z + entity.collisionbox[3] +0.01}) local sameblock = function(a,b) for i=1,#a,1 do if not mobf_pos_is_same( mobf_round_pos(a[i]), mobf_round_pos(b[i])) then return false end end return true end local prediction_time = 0.1 local predicted_corners = {} for i=1,#cornerpositions,1 do predicted_corners[i] = movement_generic.calc_new_pos(cornerpositions[i], acceleration, prediction_time, velocity ) end --check if any of the corners is in different block after prediction time while sameblock(cornerpositions,predicted_corners) and prediction_time < 2 do prediction_time = prediction_time + 0.1 for i=1,#cornerpositions,1 do predicted_corners[i] = movement_generic.calc_new_pos(cornerpositions[i], acceleration, prediction_time, velocity ) end end local pos_predicted = movement_generic.calc_new_pos(pos, acceleration, prediction_time, velocity ) return pos_predicted end