mobf_core/mobf/attention.lua
2014-08-07 19:41:06 +02:00

420 lines
13 KiB
Lua

-------------------------------------------------------------------------------
-- 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 attention.lua
--! @brief component for calculating attention of mobs
--! @copyright Sapier
--! @author Sapier
--! @date 2013-04-02
--
--! @defgroup attention Attention subcomponent
--! @brief Component handling attention of a mob. This incudes aggression as
--! well as initial attack handling.
--! @ingroup framework_int
--! @{
-- Contact: sapier a t gmx net
-------------------------------------------------------------------------------
--! @class attention
--!@}
mobf_assert_backtrace(attention == nil)
--! @brief attention handling class reference
--TODO rename to fix documentation issues
attention = {}
-------------------------------------------------------------------------------
-- @function [parent=#attention] aggression(entity)
--
--! @brief old agression handler to be used for legacy mobs
--! @memberof attention
--
--! @param entity mob to do action
--! @param now current time
-------------------------------------------------------------------------------
function attention.aggression(entity,now)
--if no combat data is specified don't do anything
if entity.data.combat == nil then
return
end
local current_state = entity.dynamic_data.state.current
--mob is specified as self attacking
if entity.data.combat.starts_attack and
entity.dynamic_data.combat.target == nil and
current_state.state_mode ~= "combat" then
dbg_mobf.fighting_lvl3("MOBF: ".. entity.data.name .. " " .. now
.. " aggressive mob, is it time to attack?")
if entity.dynamic_data.combat.ts_last_aggression_chance + 1 < now then
dbg_mobf.fighting_lvl3("MOBF: ".. entity.data.name .. " " .. now
.. " lazzy time over try to find an enemy")
entity.dynamic_data.combat.ts_last_aggression_chance = now
if entity.data.combat.angryness ~= nil and
math.random() < entity.data.combat.angryness then
dbg_mobf.fighting_lvl3("MOBF: ".. entity.data.name .. " " .. now
.. " really is angry")
local target = fighting.get_target(entity)
if target ~= nil then
if target ~= entity.dynamic_data.combat.target then
entity.dynamic_data.combat.target = target
fighting.switch_to_combat_state(entity,now,target)
local targetname = fighting.get_target_name(target)
dbg_mobf.fighting_lvl2("MOBF: ".. entity.data.name .. " "
.. now .. " starting attack at player: "
..targetname)
minetest.log(LOGLEVEL_INFO,
"MOBF: starting attack at player "..targetname)
end
end
end
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#attention] callback(entity)
--
--! @brief calculate attenntion level for mob mob
--! @memberof attention
--
--! @param entity mob to do action
--! @param now current time
-------------------------------------------------------------------------------
function attention.callback(entity,now)
--do legacy code
if entity.data.attention == nil then
attention.aggression(entity,now)
return
end
local top_attention_object = nil
local top_attention_value = 0
local top_attention_enemy = nil
local top_attention_enemy_value = 0
local current_attention_value = 0
mobf_assert_backtrace(entity.dynamic_data.attention ~= nil)
--set default values
local reduction_value = 0.1
local attention_distance = 5
local target_switch_offset = 0.5
if entity.data.attention.attention_distance ~= nil then
attention_distance = entity.data.attention.attention_distance
end
if entity.data.attention.target_switch_offset ~= nil then
target_switch_offset = entity.data.attention.target_switch_offset
end
--reduce attention level for all objects
for k,v in pairs(entity.dynamic_data.attention.watched_objects) do
if v.value > reduction_value then
v.value = v.value - reduction_value
dbg_mobf.attention_lvl3("MOBF: preserving " .. k ..
" for watchlist new value: " .. v.value)
else
entity.dynamic_data.attention.watched_objects[k] = nil
dbg_mobf.attention_lvl3("MOBF: removing " .. k .. " from watchlist")
end
end
local new_objecttable = entity.dynamic_data.attention.watched_objects
entity.dynamic_data.attention.watched_objects = new_objecttable
local own_pos = entity.object:getpos()
--get list of all objects in attention range
local objectlist =
minetest.get_objects_inside_radius(own_pos,attention_distance)
if #objectlist > 0 then
for i = 1 , #objectlist, 1 do
local continue = true
dbg_mobf.attention_lvl3("MOBF: checking " .. tostring(objectlist[i]))
if not objectlist[i]:is_player() then
local lua_entity = objectlist[i]:get_luaentity()
if lua_entity ~= nil and
not lua_entity.draws_attention then
continue = false
end
end
if continue then
local remote_pos = objectlist[i]:getpos()
local hear_addon = false
local own_view_addon = false
local remote_view_addon = false
--is in audible distance
if entity.data.attention.hear_distance ~= nil then
local distance = mobf_calc_distance(own_pos,remote_pos)
if distance < entity.data.attention.hear_distance then
hear_addon = true
end
end
--does own view angle matter
if entity.data.attention.view_angle ~= nil then
local own_view = entity.object:getyaw()
local min_yaw = own_view - entity.data.attention.view_angle/2
local max_yaw = own_view + entity.data.attention.view_angle/2
local direction = mobf_get_direction(own_pos,remote_pos)
local yaw_to_target = mobf_calc_yaw(direction.x,direction.z)
if yaw_to_target > min_yaw and
yaw_to_target < max_yaw then
own_view_addon = true
end
end
--does remote view angle matter
if entity.data.attention.remote_view == true then
local remote_view = objectlist[i]:getyaw()
if objectlist[i]:is_player() then
remote_view = objectlist[i]:get_look_yaw()
end
if remote_view ~= nil then
local direction = mobf_get_direction(own_pos,remote_pos)
local yaw_to_target = mobf_calc_yaw(direction.x,direction.z)
--TODO check for overflows
if remote_view > yaw_to_target - (math.pi/2) and
remote_view < yaw_to_target + (math.pi/2) then
remote_view_addon = true
end
else
dbg_mobf.attention_lvl2(
"MOBF: unable to get yaw for obeject: " ..table_id)
end
end
--calculate new value
local sum_values = 0;
table_id = tostring(objectlist[i])
if hear_addon then
dbg_mobf.attention_lvl3("MOBF: " .. table_id .. " within hear distance")
sum_values = sum_values + entity.data.attention.hear_distance_value
end
if own_view_addon then
dbg_mobf.attention_lvl3("MOBF: " .. table_id .. " in view")
sum_values = sum_values + entity.data.attention.own_view_value
end
if remote_view_addon then
dbg_mobf.attention_lvl3("MOBF: " .. table_id .. " looks towards mob")
sum_values = sum_values + entity.data.attention.remote_view_value
end
sum_values = sum_values + entity.data.attention.attention_distance_value
if new_objecttable[table_id] == nil then
dbg_mobf.attention_lvl3("MOBF: " .. table_id .. " unknown adding new entry")
new_objecttable[table_id] = { value = 0 }
end
new_objecttable[table_id].value =
new_objecttable[table_id].value + sum_values
if entity.data.attention.attention_max ~= nil and
new_objecttable[table_id].value > entity.data.attention.attention_max then
new_objecttable[table_id].value = entity.data.attention.attention_max
end
dbg_mobf.attention_lvl3("MOBF: adding " .. sum_values ..
" to " .. table_id ..
" new value " ..
new_objecttable[table_id].value)
--update overall atttention values
if new_objecttable[table_id].value > top_attention_value then
top_attention_value = new_objecttable[table_id].value
top_attention_object = objectlist[i]
end
--update value of old most relevant target only
if objectlist[i] == entity.dynamic_data.attention.most_relevant_target then
current_attention_value = new_objecttable[table_id].value
end
--update enemy attention values
if new_objecttable[table_id].value > top_attention_enemy_value and
attention.is_enemy(entity,objectlist[i]) then
top_attention_enemy_value = new_objecttable[table_id].value
top_attention_enemy = objectlist[i]
end
end
end
end
--check if top attention exceeds current + offset
if top_attention_value > current_attention_value + target_switch_offset then
--update top attention object
entity.dynamic_data.attention.most_relevant_target = top_attention_object
current_attention_value = top_attention_value
end
dbg_mobf.attention_lvl3("MOBF: value=" .. current_attention_value .. " attack_threshold=" ..
dump(entity.data.attention.attack_threshold) .. " watch_threshold=" ..
dump(entity.data.attention.watch_threshold))
local toattack = nil
local attack_attention_value = nil
if mobf_rtd.factions_available then
if top_attention_enemy ~= nil then
attack_attention_value = top_attention_enemy_value
toattack = top_attention_enemy
end
--don't attack anyone if factions mod is available and no enemy is found
else
attack_attention_value = top_attention_value
toattack = top_attention_object
end
if entity.data.attention.attack_threshold ~= nil and
attack_attention_value ~= nil and
attack_attention_value > entity.data.attention.attack_threshold then
local current_state = mob_state.get_state_by_name(entity,entity.dynamic_data.state.current)
if entity.data.combat.starts_attack then
dbg_mobf.attention_lvl3("MOBF: attack threshold exceeded starting attack of " ..
dump(entity.dynamic_data.attention.most_relevant_target))
entity.dynamic_data.attention.most_relevant_target = toattack
current_attention_value = attack_attention_value
fighting.set_target(entity,toattack)
end
else
if entity.data.attention.watch_threshold ~= nil and
current_attention_value > entity.data.attention.watch_threshold then
dbg_mobf.attention_lvl2("MOBF: watch threshold exceeded: value=" ..
current_attention_value .. " threshold=" ..
entity.data.attention.watch_threshold )
if entity.data.attention.watch_callback ~= nil and
type(entity.data.attention.watch_callback) == "function" then
entity.data.attention.watch_callback(entity,
entity.dynamic_data.attention.most_relevant_target)
end
end
end
entity.dynamic_data.attention.current_value = current_attention_value
end
-------------------------------------------------------------------------------
-- @function [parent=#attention] init_dynamic_data(entity)
--
--! @brief initialize all dynamic data on activate
--! @memberof attention
--
--! @param entity mob to do action
--! @param now current time
-------------------------------------------------------------------------------
function attention.init_dynamic_data(entity,now)
local data = {
watched_objects = {},
most_relevant_target = nil,
current_value = 0,
}
entity.dynamic_data.attention = data
end
-------------------------------------------------------------------------------
-- @function [parent=#attention] increase_attention_level(entity,source,value)
--
--! @brief initialize all dynamic data on activate
--! @memberof attention
--
--! @param entity mob to do action
--! @param source object causing this change
--! @param value amount of change
-------------------------------------------------------------------------------
function attention.increase_attention_level(entity,source,value)
table_id = tostring(source)
if entity.dynamic_data.attention ~= nil then
if entity.dynamic_data.attention.watched_objects[table_id] == nil then
entity.dynamic_data.attention.watched_objects[table_id] = { value = 0 }
end
entity.dynamic_data.attention.watched_objects[table_id].value =
entity.dynamic_data.attention.watched_objects[table_id].value + value
end
end
-------------------------------------------------------------------------------
-- @function [parent=#attention] is_enemy(entity,object)
--
--! @brief initialize all dynamic data on activate
--! @memberof attention
--
--! @param entity mob to do action
--! @param object to check if it's an enemy
--! @return true/false
-------------------------------------------------------------------------------
function attention.is_enemy(entity,object)
mobf_assert_backtrace(entity ~= nil)
mobf_assert_backtrace(object ~= nil)
if mobf_rtd.factions_available then
if entity.object == object then
return false
end
local remote_factions = factions.get_factions(object)
if remote_factions == nil or
#remote_factions < 1 then
dbg_mobf.attention_lvl3("MOBF: " .. entity.data.name ..
" no remote factions for: " .. tostring(object))
return false
end
for j=1, #remote_factions, 1 do
local rep = factions.get_reputation(remote_factions[j],entity)
if rep < 0 then
dbg_mobf.attention_lvl3("MOBF: ".. remote_factions[j] .. " "
.. tostring(object) .. " is enemy: " .. rep)
return true
end
end
end
return false
end