Add "mobf" (MOB Framework core) mod.

master
AntumDeluge 2016-08-01 01:39:51 -07:00
parent 9cf7659104
commit 9c45047e0a
104 changed files with 19276 additions and 2 deletions

View File

@ -25,8 +25,6 @@ The following mods are also included:
* [chatlog][] ([CC0](mods/chat/chatlog/Readme.txt))
* crafting/
* [craft_guide][] ([BSD 3-Clause](mods/crafting/craft_guide/LICENSE))
* engines/
* [creatures (Creatures MOB-Engine)][cme] ([zlib/CC-BY-SA](doc/modpacks/cme/README.txt))
* farming/
* [farming_plus][] ([WTFPL](mods/farming/farming_plus/README.txt))
* friendlies/
@ -44,6 +42,9 @@ The following mods are also included:
* [quartz][] ([MIT](mods/materials/quartz/LICENSE.txt))
* [unifieddyes][] ([GPL](mods/materials/unifieddyes/LICENSE))
* [mesecons (modpack)][mesecons] ([LGPL/CC-BY-SA](mods/mesecons/COPYING.txt))
* mob_engines/
* [creatures (Creatures MOB-Engine)][cme] ([zlib/CC-BY-SA](doc/modpacks/cme/README.txt))
* [mobf (MOB Framework core)][mobf] ([CC-BY-SA](mods/mob_engines/mobf/License.txt))
* [pipeworks][] ([WTFPL](mods/pipeworks/LICENSE))
* plantlife/
* player/
@ -86,6 +87,7 @@ The following mods are also included:
[homedecor]: https://forum.minetest.net/viewtopic.php?t=2041
[lightning]: https://forum.minetest.net/viewtopic.php?t=13886
[mesecons]: https://forum.minetest.net/viewtopic.php?t=628
[mobf]: https://github.com/sapier/mobf_core
[moreblocks]: https://forum.minetest.net/viewtopic.php?t=509
[moreores]: https://forum.minetest.net/viewtopic.php?t=549
[moretrees]: https://forum.minetest.net/viewtopic.php?t=4394

View File

@ -0,0 +1,5 @@
Licenses
Everything not mentioned:
CC-BY-SA 3.0, Author sapier
URL: http://creativecommons.org/licenses/by-sa/3.0/de/legalcode

View File

@ -0,0 +1,63 @@
-------------------------------------------------------------------------------
Mob Framework Mod (former animals mod) provides a framework for creating mobs
(c) sapier (code,some graphics)
Contact sapier a t gmx net
-------------------------------------------------------------------------------
Note: this is now mobf core readme only,
for modpack readme see animals_modpack/README
-------------------------------------------------------------------------------
Documentation:
https://github.com/sapier/animals_modpack/wiki/User-documentation
FAQ:
https://github.com/sapier/animals_modpack/wiki/Frequently-asked-questions
Changelog:
-------------------------------------------------------------------------------
Changes 2.5.1
-Add compatibility fix for old minetest versions
Changes 2.5.0
-Pass clicker to rightlick button name handlers
-Implement usage of different animations in mgen_follow for walking and waiting
-Fixed some bugs detected on writing mobs_redo compat layer
-Add multitexture mob support
-Add support for manually specifying item image
-Add support for manual animation speed
-Add support for configurable footstep sounds
Changes 2.4.94
-Add support for enabling spawner regeneration
Changes 2.4.93
-Fix custom oncatch handler triggered to early
-Fix facedir offset fix not applied to initial state after activation
Changes 2.4.92
-add owner() member function to mobf entities
-add get_persistent_data() member fucntion to mobf entities
-add set_state(statename) member function to mobf entities
-add support for entity specific on_rightclick callbacks
-add support for entity specific function can_be_cought in catching definition
-fix crash if barn beeing punched by something different to player
-fix a lot of warnings about use of undefined globals
-add workaround for minetests broken pathfinding
-add spanish translation (wip)
Changes 2.4.91
-fix crash on loading mobf_settings
-fix crash on fighting
-fix owner of breed animals
-add more debug info
Changes 2.4.90
-make mobs stop within sane distance to player on doing melee fight
-add support for specifiying a model orientation offset for models "heading"
towards "side"
Changes 2.4.9x
-multi random sound specification has changed in incompatible way and needs to be
updated.

View File

@ -0,0 +1,296 @@
-------------------------------------------------------------------------------
-- 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 api.lua
--! @brief api functions to be used by other mods
--! @copyright Sapier
--! @author Sapier
--! @date 2012-12-27
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- @function mobf_register_on_death_callback(callback)
--
--! @brief get version of mob framework
--! @ingroup framework_mob
--
--! @param callback callback to register
--! @return true/false
-------------------------------------------------------------------------------
function mobf_register_on_death_callback(callback)
return fighting.register_on_death_callback(callback)
end
-------------------------------------------------------------------------------
-- @function mobf_get_mob_definition(mobname)
--
--! @brief get COPY of mob definition
--! @ingroup framework_mob
--
--! @return mobf version
-------------------------------------------------------------------------------
function mobf_get_mob_definition(mobname)
if mobf_rtd.registred_mob_data[mobname] ~= nil then
local copy = minetest.serialize(mobf_rtd.registred_mob_data[mobname])
return minetest.deserialize(copy)
end
return nil
end
-------------------------------------------------------------------------------
-- @function mobf_get_version()
--
--! @brief get version of mob framework
--! @ingroup framework_mob
--
--! @return mobf version
-------------------------------------------------------------------------------
function mobf_get_version()
return mobf_version
end
------------------------------------------------------------------------------
-- @function mobf_add_mob(mob)
--
--! @brief register a mob within mob framework
--! @ingroup framework_mob
--
--! @param mob a mob declaration
-------------------------------------------------------------------------------
function mobf_add_mob(mob)
if not mobf.check_definition(mob) then
minetest.log(LOGLEVEL_ERROR,"MOBF: mob definition is invalid!")
return
end
--check if mob is blacklisted
--mobs from the blacklist are pre-registered at startup
if mobf_contains(mobf_rtd.registred_mob,mob.modname.. ":"..mob.name) then
mobf.blacklisthandling(mob)
return false
end
--if a random drop is specified for this mob register it
if mob.random_drop ~= nil then
random_drop.register(mob.random_drop)
end
--create default entity
minetest.log(LOGLEVEL_INFO,"MOBF: adding: " .. mob.name)
mob_state.prepare_states(mob)
mobf.register_entity(":" .. mob.modname .. ":"..mob.name,
graphics.graphics_by_statename(mob,"default"), mob)
--add compatibility entity to replace old __default entities by new ones
minetest.log(LOGLEVEL_INFO,"MOBF: registering compatibility entity: >" ..
":" .. mob.modname .. ":"..mob.name .. "__default" .. "<")
minetest.register_entity(":" .. mob.modname .. ":"..mob.name .. "__default",
{
replacement_name = mob.modname .. ":"..mob.name,
on_activate = function(self,staticdata)
local pos = self.object:getpos()
if pos ~= nil then
local newobject = minetest.add_entity(pos,self.replacement_name)
local spawned_entity = mobf_find_entity(newobject)
if spawned_entity ~= nil then
spawned_entity.dynamic_data.initialized = false
if (staticdata ~= "") then
spawned_entity.dynamic_data.last_static_data = staticdata
end
end
end
self.object:remove()
end,
})
mobf.register_mob_item(mob.name,mob.modname,mob.generic.description, mob.generic.itemimage)
--check if a movement pattern was specified
if mobf_rtd.movement_patterns[mob.movement.pattern] == nil then
minetest.log(LOGLEVEL_WARNING,"MOBF: no movement pattern specified!")
end
if mob.spawning ~= nil then
minetest.log(LOGLEVEL_WARNING,"MOBF: \"" .. mob.name ..
"\" is still using mob internal spawning," ..
" this is DEPRECATED and going to be removed soon!")
spawning.register_mob(mob)
end
--register factions required by mob
mobf_factions.setupmob(mob.factions)
if mob.generic.stepheight == nil then
mob.generic.stepheight = 0
end
--register mob name to internal data structures
table.insert(mobf_rtd.registred_mob,mob.modname.. ":"..mob.name)
mobf_rtd.registred_mob_data[mob.modname.. ":"..mob.name] = mob;
return true
end
------------------------------------------------------------------------------
-- @function mobf_is_known_mob(name)
--
--! @brief check if mob of name is known
--! @ingroup framework_mob
--
--! @param name name to check if it's a mob
--! @return true/false
-------------------------------------------------------------------------------
function mobf_is_known_mob(name)
for i,val in ipairs(mobf_rtd.registred_mob) do
if name == val then
return true
end
end
return false
end
------------------------------------------------------------------------------
-- @function mobf_register_environment(name,environment)
--
--! @brief register an environment to mob framework
--! @ingroup framework_mob
--
--! @param name of environment
--! @param environment specification
--! @return true/false
-------------------------------------------------------------------------------
function mobf_register_environment(name,environment)
return environment.register(name,environment)
end
------------------------------------------------------------------------------
-- @function mobf_environment_by_name(name)
--
--! @brief get environment by name
--! @ingroup framework_mob
--
--! @param name of environment
--! @return environment definition
-------------------------------------------------------------------------------
function mobf_environment_by_name(name)
if environment_list[name] ~= nil then
return minetest.deserialize(minetest.serialize(environment_list[name]))
else
return nil
end
end
------------------------------------------------------------------------------
-- @function mobf_probab_movgen_register_pattern(pattern)
--
--! @brief register an movement pattern for probabilistic movement gen
--! @ingroup framework_mob
--
--! @param pattern to register (see pattern specification)
--! @return true/false
-------------------------------------------------------------------------------
function mobf_probab_movgen_register_pattern(pattern)
return movement_gen.register_pattern(pattern)
end
------------------------------------------------------------------------------
-- @function mobf_spawner_register(name,spawndef)
--
--! @brief register a spawndef to adv_spawning
--! @ingroup framework_mob
--
--! @param name of spawner
--! @param mobname name of mob to register spawner for
--! @param spawndef defintion of spawner
--! @return true/false
-------------------------------------------------------------------------------
function mobf_spawner_register(name,mobname,spawndef)
--check if spawning is enabled
if minetest.world_setting_get("mobf_disable_animal_spawning") then
return false
end
--check if mob is blacklisted
if mobf_contains(mobf_rtd.registred_mob,mobname) then
minetest.log(LOGLEVEL_NOTICE,"MOBF: " .. mobname .. " is blacklisted, not adding spawner")
return false
end
local customcheck = spawndef.custom_check
spawndef.custom_check = function(pos,spawndef)
local entities_around = spawndef.entities_around
if entities_around ~= nil then
for i=1,#entities_around,1 do
--only do this check if relevant area is larger then activity range
if entities_around[i].distance > adv_spawning.active_range then
local count = spawning.count_deactivated_mobs(
mobname,
pos,
entities_around[i].distance)
local entity_active =
minetest.get_objects_inside_radius(pos,
entities_around[i].distance)
for j=1,#entity_active,1 do
local entity = entity_active[j]:get_luaentity()
if entity ~= nil then
if entity.name == entities_around[i].entityname then
count = count +1
end
if count + count > entities_around[i].threshold then
break
end
end
end
if entities_around[i].type == "MIN" and
count < entities_around[i].threshold then
dbg_mobf.mobf_core_lvl3(
"MOBF: MIN around not met: already: " .. count ..
" relevant entities around")
return false, "not enough entities around, only: " .. count .. " < " .. entities_around[i].threshold
end
if entities_around[i].type == "MAX" and
count > entities_around[i].threshold then
dbg_mobf.mobf_core_lvl3(
"MOBF: MAX around not met: already: " .. count ..
" relevant entities around")
return false, "too many entities around, " .. count .. " > " .. entities_around[i].threshold
end
end
end
end
if type(customcheck) == "function" and not customcheck(pos,spawndef) then
return false, "customcheck failed"
end
return true, "entities around and customcheck ok"
end
--register
adv_spawning.register(name,spawndef)
end

View File

@ -0,0 +1,421 @@
-------------------------------------------------------------------------------
-- 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(not core.global_exists("attention"))
--! @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 = graphics.getyaw(entity)
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
local table_id = tostring(objectlist[i])
--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 object: " ..table_id)
end
end
--calculate new value
local sum_values = 0;
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

View File

@ -0,0 +1,111 @@
-------------------------------------------------------------------------------
-- 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 compatibility.lua
--! @brief contains compatibility/transition code thats to be removed
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
minetest.register_abm({
nodenames = { "animalmaterials:wool_white" },
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
minetest.remove_node(pos)
minetest.add_node(pos,{name="wool:white"})
end
})
minetest.register_abm({
nodenames = { "animalmaterials:wool_grey" },
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
minetest.remove_node(pos)
minetest.add_node(pos,{name="wool:grey"})
end
})
minetest.register_abm({
nodenames = { "animalmaterials:wool_brown" },
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
minetest.remove_node(pos)
minetest.add_node(pos,{name="wool:brown"})
end
})
minetest.register_abm({
nodenames = { "animalmaterials:wool_black" },
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
minetest.remove_node(pos)
minetest.add_node(pos,{name="wool:black"})
end
})
minetest.register_entity("mobf:compat_spawner",
{
collisionbox = {0,0,0,0,0,0},
physical = false,
groups = { "immortal" },
on_activate =
function(self,staticdata,dtime_s)
local pos = self.object:getpos()
local delta,y_offset = adv_spawning.get_spawner_density()
local spawnerpos = {
x = math.floor(pos.x/delta) * delta,
y = math.floor((pos.y-y_offset)/delta) * delta + y_offset,
z = math.floor(pos.x/delta) * delta
}
local objects_at = minetest.get_objects_inside_radius(spawnerpos, 0.5)
local found = false
for i=1,#objects_at,1 do
local luaentity = objects_at[i]:get_luaentity()
if luaentity ~= nil then
if luaentity.name == "adv_spawning:spawn_seed" then
found = true
end
end
end
if not found then
minetest.add_entity(spawnerpos,"adv_spawning:spawn_seed")
end
self.object:remove()
end,
})
-------------------------------------------------------------------------------
-- compatibility functions to make transition to new name easier
-------------------------------------------------------------------------------
function animals_add_animal(animal)
mobf_add_mob(animal)
end

View File

@ -0,0 +1,500 @@
-------------------------------------------------------------------------------
-- 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 debug.lua
--! @brief contains debug functions for mob framework
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--!
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mobf_debug"))
--! @defgroup debug_in_game In game debugging functions
--! @brief debugging functions to be called from in game
--! @ingroup framework_int
--! @{
mobf_debug = {}
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] print_usage(player,command,toadd)
--
--! @brief send errormessage to player
--
--! @param player name of player to print usage
--! @param command display usage for this command
--! @param toadd additional information to transfer to player
-------------------------------------------------------------------------------
function mobf_debug.print_usage(player, command, toadd)
if toadd == nil then
toadd = ""
end
if command == "spawnmob" then
print("CMD: ".. player .."> ".. "Usage: /spawnmob <mobname> <X,Y,Z> " .. toadd)
minetest.chat_send_player(player, "Usage: /spawnmob <mobname> <X,Y,Z> " .. toadd)
end
if command == "ukn_mob" then
print("CMD: ".. player .."> ".. "Unknown mob name "..toadd)
minetest.chat_send_player(player, "Unknown mob name "..toadd)
end
if command == "inv_pos" then
print("CMD: ".. player .."> ".. "Invalid position "..toadd)
minetest.chat_send_player(player, "Invalid position "..toadd)
end
if command == "mob_spawned" then
print("CMD: ".. player .."> ".. "Mob successfully spawned "..toadd)
minetest.chat_send_player(player, "Mob successfully spawned "..toadd)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] spawn_mob(name,param)
--
--! @brief handle a spawn mob command
--
--! @param name name of player
--! @param param parameters received
------------------------------------------------------------------------------
function mobf_debug.spawn_mob(name,param)
local parameters = param:split(" ")
if #parameters ~= 1 and
#parameters ~= 2 then
mobf_debug.print_usage(name,"spawnmob")
return
end
if mobf_is_known_mob(parameters[1]) ~= true then
mobf_debug.print_usage(name,"ukn_mob", ">"..parameters[1].."<")
return true
end
if #parameters == 2 then
local pos_strings = parameters[2]:split(",")
if #pos_strings ~= 3 then
mobf_debug.print_usage(name,"spawmob")
return
end
local spawnpoint = {
x=tonumber(pos_strings[1]),
y=tonumber(pos_strings[2]),
z=tonumber(pos_strings[3])
}
if spawnpoint.x == nil or
spawnpoint.y == nil or
spawnpoint.z == nil then
mobf_debug.print_usage(name,"spawnmob")
return
end
spawning.spawn_and_check(parameters[1],spawnpoint,"mobf_debug_spawner")
else
--todo find random pos
local player = minetest.get_player_by_name(name)
if player == nil then
return
end
local pos = player:getpos()
if pos == nil then
return
end
local found = false
local maxtries = 10
while (found == false) and (maxtries > 0) do
local toadd = {}
toadd.x = pos.x + (math.random(20) -10)
toadd.z = pos.z + (math.random(20) -10)
local y = mobf_get_surface(toadd.x,toadd.z,pos.y-10,pos.y+10)
if y ~= nil then
toadd.y = y +2
if spawning.spawn_and_check(parameters[1],toadd,"mobf_debug_spawner") then
found = true
end
end
maxtries = maxtries -1
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] list_active_mobs(name,param)
--
--! @brief print list of all current active mobs
--
--! @param name name of player
--! @param param parameters received
------------------------------------------------------------------------------
function mobf_debug.list_active_mobs(name,param)
local count = 1
for index,value in pairs(minetest.luaentities) do
if value.data ~= nil and value.data.name ~= nil then
local tosend = count .. ": " .. value.data.name .. " at "
.. printpos(value.object:getpos())
print(tosend)
minetest.chat_send_player(name,tosend)
count = count +1
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] list_spawners(name,param)
--
--! @brief print list of all spawners around player
--
--! @param name name of player
--! @param param parameters received
------------------------------------------------------------------------------
function mobf_debug.list_spawners(name,param)
for index,value in pairs(minetest.luaentities) do
if value ~= nil and value.spawner_mob_name ~= nil then
local resultline = "SPW: "
.. mobf_fixed_size_string(value.spawner_mob_name,24) .. " "
.. mobf_fixed_size_string(printpos(value.object:getpos()),16)
.. " STATE: "
.. mobf_fixed_size_string(dump(value.spawner_last_result),32)
.. " TIME: " .. value.spawner_time_passed
print(resultline)
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] mob_count(name,param)
--
--! @brief count active mobs
--
--! @param name name of player
--! @param param parameters received
------------------------------------------------------------------------------
function mobf_debug.mob_count(name,param)
local count = 1
for index,value in pairs(minetest.luaentities) do
if value.data ~= nil and value.data.name ~= nil then
count = count +1
end
end
minetest.chat_send_player(name,"Active mobs: " .. count)
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] add_tools(name,param)
--
--! @brief add toolset for testing
--
--! @param name name of player
--! @param param parameters received
------------------------------------------------------------------------------
function mobf_debug.add_tools(name,param)
local player = minetest.get_player_by_name(name)
if player ~= nil then
player:get_inventory():add_item("main", "animalmaterials:lasso 20")
player:get_inventory():add_item("main", "animalmaterials:net 20")
player:get_inventory():add_item("main", "animalmaterials:scissors 1")
player:get_inventory():add_item("main", "vessels:drinking_glass 10")
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] list_defined_mobs(name,param)
--
--! @brief list all registred mobs
--
--! @param name name of player
--! @param param parameters received
------------------------------------------------------------------------------
function mobf_debug.list_defined_mobs(name,param)
local text = ""
for i,val in ipairs(mobf_rtd.registred_mob) do
text = text .. val .. " "
end
minetest.chat_send_player(name, "MOBF: "..text)
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] init()
--
--! @brief initialize debug commands chat handler
--
------------------------------------------------------------------------------
function mobf_debug.init()
minetest.register_chatcommand("spawnmob",
{
params = "<name> <pos>",
description = "spawn a mob at position(optional)" ,
privs = {mobfw_admin=true},
func = mobf_debug.spawn_mob
})
minetest.register_chatcommand("listactivemobs",
{
params = "",
description = "list all currently active mobs" ,
privs = {mobfw_admin=true},
func = mobf_debug.list_active_mobs
})
minetest.register_chatcommand("listdefinedmobs",
{
params = "",
description = "list all currently defined mobs" ,
privs = {mobfw_admin=true},
func = mobf_debug.list_defined_mobs
})
minetest.register_chatcommand("mob_add_tools",
{
params = "",
description = "add some mob specific tools to player" ,
privs = {mobfw_admin=true},
func = mobf_debug.add_tools
})
minetest.register_chatcommand("mobf_version",
{
params = "",
description = "show mobf version number" ,
privs = {},
func = function(name,param)
minetest.chat_send_player(name,"MOBF version: " .. mobf_version)
end
})
minetest.register_chatcommand("listspawners",
{
params = "",
description = "debug info about spawner entities" ,
privs = {mobfw_admin=true},
func = mobf_debug.list_spawners
})
minetest.register_chatcommand("mobf_count",
{
params = "",
description = "number of active mobs" ,
privs = {},
func = mobf_debug.mob_count
})
minetest.register_chatcommand("mobf_mobs_offline",
{
params = "",
description = "print offline mobs" ,
privs = {},
func = mobf_debug.print_offline_mobs
})
if mobf_rtd.luatrace_enabled then
minetest.register_chatcommand("traceon",
{
params = "",
description = "start luatrace tracing" ,
privs = {mobfw_admin=true},
func = function()
luatrace.tron(nil)
end
})
minetest.register_chatcommand("traceoff",
{
params = "",
description = "stop luatrace tracing" ,
privs = {mobfw_admin=true},
func = function()
luatrace.troff()
end
})
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] print_offline_mobs(name,message)
--
--! @brief spawn small house
--
--! @param name name of player
--! @param param parameters
------------------------------------------------------------------------------
function mobf_debug.print_offline_mobs(name,param)
local count = 0
for key,value in pairs(spawning.mob_spawn_data) do
for hash,enabled in pairs (value) do
count = count +1
local mobpos = mobf_hash_to_pos(hash)
print(string.format("%5d: ",count) .. key .. " " .. printpos(mobpos))
end
end
print("Total of " .. count .. " mobs stored as offline")
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_debug] rightclick_callback(entity,player)
--
--! @brief show rightclick info
--
--! @param entity entity rightclicked
--! @param player player doing rightclick
------------------------------------------------------------------------------
function mobf_debug.rightclick_callback(entity,player)
local basepos = entity.getbasepos(entity)
local lifetime = mobf_get_current_time() - entity.dynamic_data.spawning.original_spawntime
print("MOBF: " .. entity.data.name .. " " .. tostring(entity) .. " is alive for " .. lifetime .. " seconds")
print("MOBF: \tAbsolute spawntime: " .. entity.dynamic_data.spawning.original_spawntime)
print("MOBF: \tCurrent state: " .. entity.dynamic_data.state.current.name )
print("MOBF: \tCurrent movgen: " .. entity.dynamic_data.current_movement_gen.name )
print("MOBF: \tHP: " .. entity.object:get_hp())
if entity.dynamic_data.current_movement_gen.name == "follow_mov_gen" or
entity.dynamic_data.current_movement_gen.name == "mgen_path" then
local targetpos = entity.dynamic_data.spawning.spawnpoint
if entity.dynamic_data.movement.target ~= nil then
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 then
print("MOBF: \t\tmovement state: " .. mgen_follow.identify_movement_state(basepos,targetpos) )
else
print("MOBF: \t\tmovement state: invalid")
end
print("MOBF: \t\tguard spawnpoint: " .. dump(entity.dynamic_data.movement.guardspawnpoint))
print("MOBF: \t\ttarget: " .. dump(entity.dynamic_data.movement.target))
end
if entity.dynamic_data.current_movement_gen.name == "mgen_path" then
print("MOBF: \t\tpath index: " .. entity.dynamic_data.p_movement.next_path_index)
print("MOBF: \t\tpath: " .. dump(entity.dynamic_data.p_movement.path))
if entity.dynamic_data.p_movement.path ~= nil then
for i,v in ipairs(entity.dynamic_data.p_movement.path) do
local objects = minetest.get_objects_inside_radius(v,0.5)
local found = false;
for i=1,#objects,1 do
local luaentity = objects[i]:get_luaentity()
if luaentity ~= nil and
luaentity.name == "mobf:path_marker_entity" then
found = true
break
end
end
local node_at = minetest.get_node(v)
if not found and
node_at.name ~= nil and
node_at.name ~= "ignore" then
spawning.spawn_and_check("mobf:path_marker_entity",v,"mark_path")
end
end
print("MOBF: \t\tdistance to next point: " .. p_mov_gen.distance_to_next_point(entity,entity.object:getpos()))
end
end
local predicted_pos = movement_generic.predict_next_block(
basepos,
entity.object:getvelocity(),
entity.object:getacceleration())
if not ( entity.data.movement.canfly == true) then
predicted_pos.y = basepos.y
end
local pos_state = environment.pos_is_ok(predicted_pos,entity)
local pos_quality = environment.pos_quality(basepos,entity)
local predicted_quality = environment.pos_quality(predicted_pos,entity)
print("MOBF: \tTime to state change: " .. entity.dynamic_data.state.time_to_next_change .. " seconds")
print("MOBF: \tCurrent environmental state: " .. environment.pos_is_ok(entity.getbasepos(entity),entity))
if mobf_rtd.detailed_state then
print("MOBF: \tCurrent detailed state: " .. pos_quality:shortstring())
end
print("MOBF: \tCan fly: " .. dump(entity.data.movement.canfly))
print("MOBF: \tCurrent accel: " .. printpos(entity.object:getacceleration()))
print("MOBF: \tDefault gravity: " .. dump(environment.get_default_gravity(basepos, entity.environment.media, entity.data.movement.canfly)))
print("MOBF: \tCurrent speed: " .. printpos(entity.object:getvelocity()))
print("MOBF: \tSpawnpoint: " .. printpos(entity.dynamic_data.spawning.spawnpoint))
print("MOBF: \tSpawner: " .. dump(entity.dynamic_data.spawning.spawner))
print("MOBF: \tCurrent pos: " .. printpos(basepos))
print("MOBF: \tPredicted pos: " .. printpos(predicted_pos))
print("MOBF: \tPredicted state: " .. pos_state)
print("MOBF: \tMovement facedir offset: " .. dump(entity.automatic_face_movement_dir))
if entity.dynamic_data.sound ~= nil and entity.dynamic_data.sound.random_next ~= nil then
print("MOBF: \tNext random sound: " ..
(entity.dynamic_data.sound.random_next - mobf_get_current_time()))
end
if mobf_rtd.detailed_state then
print("MOBF: \tPredicted detail: " .. predicted_quality:shortstring())
end
if entity.dynamic_data.combat ~= nil then
print("MOBF: \tCurrent combat target: " .. fighting.get_target_name(entity.dynamic_data.combat.target))
end
if entity.dynamic_data.attention ~= nil then
print("MOBF: \t Current attention table:")
for k,v in pairs(entity.dynamic_data.attention.watched_objects) do
print("MOBF: \t\t " .. k .. ": " .. v.value)
end
if entity.dynamic_data.attention.most_relevant_target ~= nil then
local attention_name = tostring(entity.dynamic_data.attention.most_relevant_target)
if (entity.dynamic_data.attention.most_relevant_target:is_player()) then
attention_name = entity.dynamic_data.attention.most_relevant_target:get_player_name()
end
print("MOBF: \tTop attention object: " .. attention_name)
end
end
if entity.dynamic_data.graphics.last_fps ~= nil then
print("MOBF: Animating with: " .. entity.dynamic_data.graphics.last_fps .. " fps")
end
if entity.data.states ~= nil then
print("MOBF: \tStatecount: " .. #entity.data.states)
for i,v in ipairs(entity.data.states) do
print("MOBF: \t\t" .. i .. ": " .. v.name .. " chance=" .. v.chance)
end
end
return false
end
--!@}

View File

@ -0,0 +1,116 @@
-------------------------------------------------------------------------------
-- 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 debug_trace.lua
--! @brief contains switchable debug trace functions
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--!
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @defgroup debug_trace Debug trace functions
--! @brief central configuration of trace functions
--! @ingroup framework_int
--lvl1 excessive output
--lvl2 medium output
--lvl3 less output
--! @brief configuration of trace level to use for various components
--! @ingroup debug_trace
dbg_mobf = {
generic_lvl1 = function () end,
generic_lvl2 = function () end,
generic_lvl3 = function () end,
graphics_lvl1 = function () end,
graphics_lvl2 = function () end,
graphics_lvl3 = function () end,
spawning_lvl1 = function () end,
spawning_lvl2 = function () end,
spawning_lvl3 = function () end,
permanent_store_lvl1 = function () end,
permanent_store_lvl2 = function () end,
permanent_store_lvl3 = function () end,
movement_lvl1 = function () end,
movement_lvl2 = function () end,
movement_lvl3 = function () end,
pmovement_lvl1 = function () end,
pmovement_lvl2 = function () end,
pmovement_lvl3 = function () end,
fmovement_lvl1 = function () end,
fmovement_lvl2 = function () end,
fmovement_lvl3 = function () end,
flmovement_lvl1 = function () end,
flmovement_lvl2 = function () end,
flmovement_lvl3 = function () end,
path_mov_lvl1 = function () end,
path_mov_lvl2 = function () end,
path_mov_lvl3 = function () end,
fighting_lvl1 = function () end,
fighting_lvl2 = function () end,
fighting_lvl3 = function () end,
environment_lvl1 = function () end,
environment_lvl2 = function () end,
environment_lvl3 = function () end,
harvesting_lvl1 = function () end,
harvesting_lvl2 = function () end,
harvesting_lvl3 = function () end,
sound_lvl1 = function () end,
sound_lvl2 = function () end,
sound_lvl3 = function () end,
random_drop_lvl1 = function () end,
random_drop_lvl2 = function () end,
random_drop_lvl3 = function () end,
mob_state_lvl1 = function () end,
mob_state_lvl2 = function () end,
mob_state_lvl3 = function () end,
mobf_core_lvl1 = function () end,
mobf_core_lvl2 = function () end,
mobf_core_lvl3 = function () end,
mobf_core_helper_lvl1 = function () end,
mobf_core_helper_lvl2 = function () end,
mobf_core_helper_lvl3 = function () end,
trader_inv_lvl1 = function () end,
trader_inv_lvl2 = function () end,
trader_inv_lvl3 = function () end,
ride_lvl1 = function () end,
ride_lvl2 = function () end,
ride_lvl3 = function () end,
path_lvl1 = function () end,
path_lvl2 = function () end,
path_lvl3 = function () end,
lifebar_lvl1 = function () end,
lifebar_lvl2 = function () end,
lifebar_lvl3 = function () end,
attention_lvl1 = function () end,
attention_lvl2 = function () end,
attention_lvl3 = function () end,
}

View File

@ -0,0 +1,4 @@
default
adv_spawning?
intllib?
factions?

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
Date 2012 01 28
testcases to check before release:
Features:
-mobs moving
-catching mobs with lasso
-catching mobs with net
-killing mobs
-harvesting mobs
-mobs doing meele attack
-mobs transforming on harvest
-mobs auto transform
-mobs taking damage in sun
-mobs self destruct
-mobs honoring jump limitiations
-mobs random dropping
-mobs honoring movement medium
-mobs doing random jumps
-mobs level changing
General:
-version number correct?
-readme correct?
-debug output disabled?

View File

@ -0,0 +1,47 @@
-------------------------------------------------------------------------------
-- 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 mainpage_description.lua
--! @brief just a doc page
--! @copyright Sapier
--! @author Sapier
--! @date 2013-04-28
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @mainpage Mob Framework Mod 2.5.x
--!
--! This documentation uses doxygen created from lua code. As some of you
--! probably know doxygen doesn't support lua on it's own so some of the
--! shown descriptions arent perfectly correct.\n
--!
--! On this page some of the caveats are explained:\n
--! Lua doesn't support classes and structs but tables which ain't supported by
--! doxygen.\n
--!
--! Mapping of classes and structs:
--! \li \b classes are used to group (sub)components containing primary functions\n
--! \li \b structs are result of parsing tables and containing configuration or
--! temporary data
--!
--! Datatypes shown in doxygen and it's meaning:
--! \li \b function this is a return value of function and can be nil too
--! \li \b var a variable
--! \li \b struct some table element
--! \li \b anonymous_value a table element specified without a name
--! \li \b parameter is a parameter declared in a function definition
--!
--!
--! Keywords used in config parameter description
--! \li \b MANDATORY some parameter required
--! \li \b OPTIONAL parameter may be nill without causinh an error
--! \li \b MOV_GEN_DEPENDENT parameter is required dependent of selected movement gen
--! \li \b 2D MANDATORY parameter is required in case of 2D mob
--! \li \b 3D MANDATORY parameter is required in case of 3D mob
--! \li \b ALGORITHM \b DEPENDENT is required dependent on selected algorithm
--! \li \b UPPER_VALUE_DEPENDENT if you specify upper value you need to specify this too

View File

@ -0,0 +1,455 @@
-------------------------------------------------------------------------------
-- 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 mob_template.lua
--! @brief template for mob
--! @copyright Sapier
--! @author Sapier
--! @date 2013-01-27
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- WARNING this code might be not correct lua in order to get doxygen
-- compatibility!
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--! @brief Template for creating mobs
--!
--! This template trys to describe all available configuration options
--! in mob framework.
--! @ingroup framework_mob
local mob_template = {
--! @brief [MANDATORY] name of mob @b (alphanumeric and "_" only!!)
name = "some name",
--! @brief [MANDATORY] name of mod defining the mob
modname = "name of mod",
--! @brief [MANDATORY] generic parameters for mob
generic = {
--! @brief [MANDATORY] description to show on mouse over in inventory
description="Some mob",
--! @brief [MANDATORY] maximum health
base_health=0,
--! @brief [MANDATORY] environment of mob to be
envid="some environment id",
--! @brief [OPTIONAL] item description OR function all returning a item description of whats the result of a kill
kill_result = nil,
--! @brief [OPTIONAL] armor groups of mob
armor_groups = nil,
--! @brief [OPTIONAL] custom on_hit(entity,player) callback return true to skip normal fight callback
on_hit_callback = nil,
--! @brief [OPTIONAL] custom on_kill(entity,player) callback return true to skip normal on kill handling
on_kill_callback = nil,
--! @brief [OPTIONAL] custom on_place(entity, placer, pointed_thing) callback called after normal on place handling is done
custom_on_place_handler = nil,
--! @brief [OPTIONAL] custom on_activate(entity) callback called after normal on_activate handling is done
custom_on_activate_handler = function(entity) end,
--! @brief [OPTIONAL] custom on_step(entity) callback called after normal on_step handling is done
custom_on_step_handler = nil,
--! @brief [OPTIONAL] list of callbacks
on_rightclick_callbacks = {
{
handler = function(entity, player) end,
name = "internal name shown in debug info",
visiblename = function(entity) end or "some label of rightclick button"
}
},
},
--! @brief [MANDATORY] configuration of movement generator
movement = {
--! @brief [MANDATORY] is this a flying mob
canfly=false,
--! @brief [MANDATORY] minumum acceleration of mob
min_accel=0,
--! @brief [MANDATORY] maximum acceleration of mob
max_accel=0,
--! @brief [MANDATORY] maximum absolute speed of mob
max_speed=0,
--! @brief [OPTIONAL] minimum speed a mob shall move (if moving at all)
min_speed=0,
--! @brief [MOV_GEN_DEPENDENT | MANDATORY] pattern based movement gen -> pattern to use for movement
pattern="some pattern id",
--! @brief [MOV_GEN_DEPENDENT | OPTIONAL] follow movement gen -> does this mob guard it's spawnpoint
guardspawnpoint = false,
--! @brief [MOV_GEN_DEPENDENT | OPTIONAL] follow movement gen -> time until this mob teleports to its target
teleportdelay = 60,
},
--! @brief [OPTIONAL] if mob is harvestable configure it here
harvest = {
--! @brief [OPTIONAL] tool required for harvesting
tool=nil,
--! @brief [OPTIONAL] is tool consuled by harvesting
tool_consumed=false,
--! @brief [MANDATORY] result of harvest
result="",
--! @brief [OPTIONAL] mob transforms to this mob on harvest
transforms_to="",
--! @brief [MANDATORY] minimum time between two harvests (in case of transform set this to -1)
min_delay=-1,
},
--! @brief [OPTIONAL] configuration how to catch the mob
catching = {
--! @brief [MANDATORY] tool required to wear to catch the mob
tool = "some item",
--! @brief [MANDATORY] is tool consumed by catching
consumed = true,
--! @brief [OPTIONAL] function to be called to check if a mob can be cought in current state
can_be_cought = function(entity) end,
},
--! @brief [OPTIONAL] does this mob do random drops
random_drop = {
--! @brief [MANDATORY] item to be dropped
result="some_material",
--! @brief [MANDATORY] minimum delay between two drops
min_delay=60,
--! @brief [MANDATORY] chance per second to drop after min_delay has passed
chance=0.2
},
--! @brief [OPTIONAL] if this mob s intended to transform by its own configure it here
auto_transform = {
--! @brief [MANDATORY] mob to transform to
result="some_mob",
--! @brief [MANDATORY] time to transformation
delay=1800
},
--! @brief [OPTIONAL] combat settings for mob
combat = {
--! @brief [MANDATORY] does mob start an attack on its own?
starts_attack=true,
--! @brief [MANDATORY] chance mob will attack (if starting attack on its own or beeing attacked)
angryness=0.95,
--! @brief [OPTIONAL] is mob sensitive to sun?
sun_sensitive=true,
--! @brief [OPTIONAL] attacks hostile mobs
attack_hostile_mobs=false,
--! @brief [UNUSED] attacks hostile mobs
--attack_friendly_mobs=false,
--! @brief [OPTIONAL] configuration of meele attack
melee = {
--! @brief [MANDATORY] maximum damage mob does per hit
maxdamage=4,
--! @brief [MANDATORY] range mob will hit
range=2,
--! @brief [MANDATORY] minimum time between two hits
speed=2,
--! @brief [OPTIONAL] list of groups damage is done to, if not specified "fleshy" is used
weapon_damage_groups= { "fleshy" },
},
--! @brief [OPTIONAL] configuration of distance attack
distance = {
--! @brief [MANDATORY] distance projectile to issue
attack="some_entity",
--! @brief [MANDATORY] distance to issue an attack
range=10,
--! @brief [OPTIONAL] minimum range of distance attack (should be > range of melee attack)
min_range = 3,
--! @brief [MANDATORY] minimum time between two attacks
speed=2,
},
--! @brief [OPTIONAL] configuration for self destructing mob
self_destruct = {
--! [MANDATORY] maximum damage to be done on self destruct
damage=15,
--! [MANDATORY] maximum range to do damage
range=5,
--! [MANDATORY] range to destroy nodes on self destruction
node_damage_range = 1.5,
--! [MANDATORY] delay between self destruct triggering and explosion
delay=5,
},
},
--! @brief [MANDATORY] spawning configuration for mob
spawning = {
--! @brief [MANDATORY] rate this mob is spawned
rate=0.01,
--! @brief [MANDATORY] typical distance between two mobs of this type when spawend
density=1000,
--! @brief [MANDATORY] identifyer of spawn algorithm
algorithm="some algorithm id",
--! @brief [ALGORITHM DEPENDENT] shadows minimum number of air blocks above pos
height = 1,
},
--! @brief [OPTIONAL] sounds to be played by mob
sound = {
--! @brief [OPTIONAL] random sound to be played
random = {
--! @brief [MANDATORY] default interval between random sounds
interval = 60,
--! @brief [MANDATORY] maximum deviation from default interval
max_interval_deviation = 10,
--! @brief [MANDATORY] list of random sounds to play
list = {
{
--! @brief [MANDATORY] basename of file
name="random_1",
--! @brief [MANDATORY] amplify the sound by this value
gain = 1,
--! @brief [MANDATORY] maximum distance sound is heared
max_hear_distance = 75,
},
{
--! @brief [MANDATORY] basename of file
name="random_2",
--! @brief [MANDATORY] amplify the sound by this value
gain = 1,
--! @brief [MANDATORY] maximum distance sound is heared
max_hear_distance = 75,
},
{
--! @brief [MANDATORY] basename of file
name="random_x",
--! @brief [MANDATORY] amplify the sound by this value
gain = 1,
--! @brief [MANDATORY] maximum distance sound is heared
max_hear_distance = 75,
},
},
},
--! @brief [OPTIONAL] sound played on self destruction
self_destruct = {
--! @brief [MANDATORY] basename of file
name="bomb_explosion",
--! @brief [MANDATORY] amplify the sound by this value
gain = 2,
--! @brief [MANDATORY] maximum distance sound is heared
max_hear_distance = 1000,
},
--! @brief [OPTIONAL] sound played on harvest
harvest = {
--! @brief [MANDATORY] basename of file
name="harvest",
--! @brief [MANDATORY] amplify the sound by this value
gain = 0.8,
--! @brief [MANDATORY] maximum distance sound is heared
max_hear_distance = 5,
},
--! @brief [OPTIONAL] sound played on distance attack
distance = {
--! @brief [MANDATORY] basename of file
name="fireball",
--! @brief [MANDATORY] amplify the sound by this value
gain = 1,
--! @brief [MANDATORY] maximum distance sound is heared
max_hear_distance = 100,
},
--! @brief [OPTIONAL] sound played if mob dies
die = {
--! @brief [MANDATORY] basename of file
name="die",
--! @brief [MANDATORY] amplify the sound by this value
gain = 1,
--! @brief [MANDATORY] maximum distance sound is heared
max_hear_distance = 100,
},
--! @brief [OPTIONAL] sound played if mob does meele attack
melee = {
--! @brief [MANDATORY] basename of file
name="hit",
--! @brief [MANDATORY] amplify the sound by this value
gain = 1,
--! @brief [MANDATORY] maximum distance sound is heared
max_hear_distance = 100,
},
},
--! @brief [OPTIONAL] parameters for rideable mobs
ride = {
--! @brief [OPTIONAL] speed when walking
walkspeed = 7.8,
--! @brief [OPTIONAL] speed when sneaking
sneakspeed = 0.8,
--! @brief [OPTIONAL] inital jumpspeed
jumpspeed = 58,
--! @brief [OPTIONAL] offset to center player is put to
attacheoffset = { x=0,y=2,z=0},
--! @brief [OPTIONAL] texture modifier when player is attached
texturemod = "^mob_ostrich_ostrich_saddle_mesh.png",
--! @brief [OPTIONAL] animation to show when riding
walk_anim = "walk",
},
--! @brief [OPTIONAL] description of animations
animation = {
--! @brief [OPTIONAL] one or many animation descriptions
animationname = {
--! @brief [MANDATORY] start frame of animation
start_frame = 1,
--! @brief [MANDATORY] end frame of animation
end_frame = 2,
},
},
--! @brief [OPTIONAL] configuration for a trader mob
trader_inventory = {
--! @brief [MANDATORY] goodlist to be sold
goods = {
--! @brief [MANDOTORY] first element in list
{ "default:mese 1", "default:dirt 99", "default:cobble 50"},
--! @brief [OPTIONAL] any further element
{ "default:steel_ingot 1", "default:dirt 50", "default:cobble 20"},
},
--! @brief [MANDATORY] list of names randomly choosen for trader
random_names = { "Name1","Name2","Name3"},
},
--! @brief [OPTIONAL] configuration for attention handling
attention = {
--! @brief [OPTIONAL] hear distance value
hear_distance = 3,
--! @brief [UPPER_VALUE_DEPENDENT | MANDATORY] value to add if within hear distance
hear_distance_value = 0.5,
--! @brief [OPTIONAL] view angle to consider
view_angle = nil,
--! @brief [UPPER_VALUE_DEPENDENT | MANDATORY] value to add if someone is within view angke
own_view_value = 0,
--! @brief [OPTIONAL] is remove view angle relevant?
remote_view = false,
--! @brief [UPPER_VALUE_DEPENDENT | MANDATORY] value to add if remote target looks at mob
remote_view_value = 0,
--! @brief [MANDATORY] value to add if within attention distance
attention_distance_value = 0.2,
--! @brief [MANDATORY] threshold to issue watch callback
watch_threshold = 2,
--! @brief [OPTIONAL] threshold to issue attack callback
attack_threshold = nil,
--! @brief [MANDATORY] maximum distance to consider objects for attantion
attention_distance = 7.5,
--! @brief [MANDATORY] maximum amount of attention any object can draw
attention_max = 10,
--! @brief [OPTIONAL] watch_callback(entity,target) a callback to be called once attention threshold is reached
watch_callback = nil
},
--! @brief [OPTIONAL] factions configuration
factions = {
--! @brief [OPTIONAL] define factions to be member in
member = {
"faction_1",
"faction_2",
"faction_3",
"faction_4"
},
},
--! @brief [OPTIONAL] used to specify different movement/model states,
--! you may specify as many states as you like
states = {
{
--! @brief [MANDATORY] (default state is MUST have!) name of state
name = "default",
--! @brief [MANDATORY] typical duration of this state
typical_state_time = 180,
--! @brief [MANDATORY] chance of state to be selected (SUM may not be > 1)
chance = 0.5,
--! @brief [MANDATORY] a special movement handler for this state
movgen = "none",
--! @brief [3D MANDATORY] a special model to be used for this state
graphics_3d = {
--! @brief [MANDATORY] this is the drawtype to use
visual = "mesh",
--! @brief [MANDATORY] this is the drawtype to use
mesh = "mesh.x",
--! @brief [MANDATORY] the model of the mob
textures = {"some node declatation"},
--! @brief [MANDATORY] collisionbox to use
collisionbox = { "<selectionbox declatation>" },
--! @brief [MANDATORY] xyz scale factors for the model
visual_size = {x=1,y=1,z=1},
--! @brief [OPTIONAL] additional yaw offset used to fix models with broken orientation
model_orientation_fix = -math.pi/2,
},
--! @brief [2D MANDATORY] a special sprite to be used for this state
graphics_2d = {
--! @brief [MANDATORY] scale of sprite
sprite_scale={x=0,y=0},
--! @brief [MANDATORY] description of multi dimensional sprites (e.g. containing burning/ differend view directions)
sprite_div = {x=0,y=0},
--! @brief [MANDATORY] height of sprite
visible_height = 3.2,
},
--! @brief [MANDATORY] a animation to be played while this state is active
animation = "walk",
},
{
--! @brief [MANDATORY] name of state
name = "another_state_example",
--! @brief [MANDATORY] typical duration of this state
typical_state_time = 180,
--! @brief [MANDATORY] chance of state to be selected (SUM may not be > 1)
chance = 0.5,
--! @brief [OPTIONAL] a function to check before switching to this state
HANDLER_precondition = nil,
--! @brief [OPTIONAL] a special movement handler for this state
movgen = "none",
--! @brief [OPTIONAL] a special model to be used for this state
graphics_3d = "<graphic definition as previous described>",
--! @brief [OPTIONAL] a special sprite to be used for this state
graphics_2d = "<graphic definition as previous described>",
--! @brief [OPTIONAL] a animation to be played while this state is active
animation = "name",
--! @brief [OPTIONAL] mode to differentiate different states e.g. "auto" for auto switched states "user_def" for user defined or "combat" for combat states
state_mode = "auto",
},
},
}

View File

@ -0,0 +1,140 @@
-------------------------------------------------------------------------------
-- 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 env_constants.lua
--! @brief constants used for environmental functions
--! @copyright Sapier
--! @author Sapier
--! @date 2013-08-17
--
--! @defgroup environment Environment subcomponent
--! @brief Environment check functions used by different subcomponents
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
MQ_IN_MEDIA = 100
MQ_IN_WATER = 20
MQ_IN_AIR = 10
MQ_SOLID = 5
GQ_FULL = 100
GQ_PARTIAL = 60
GQ_NONE = 30
SQ_OK = 100
SQ_POSSIBLE = 60
SQ_WRONG = 30
SQ_WATER = 10
LQ_OK = 100
LQ_ABOVE = 60
LQ_BELOW = 30
Q_UNKNOWN = 0
--define some common limits for convenience
--! @brief a position where mob is in media, has full surface contact and is at least on a possible surface
LT_GOOD_POS = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=GQ_FULL,
min_geom_center=nil,
min_min_surface=SQ_POSSIBLE,
min_max_surface=SQ_OK,
min_center_surface=nil
}
LT_GOOD_FLY_POS = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=nil,
min_geom_center=nil,
min_min_surface=Q_UNKNOWN,
min_max_surface=Q_UNKNOWN,
min_center_surface=nil
}
--! @brief a position where mob is in media, has surface contact and is at least on a possible surface
LT_SAFE_POS = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=GQ_PARTIAL,
min_geom_center=nil,
min_min_surface=nil,
min_max_surface=SQ_POSSIBLE,
min_center_surface=nil
}
--! @brief a position where mob is in media, it's center has surface contact and is at least on a possible surface
LT_SAFE_EDGE_POS = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=nil,
min_geom_center=GQ_FULL,
min_min_surface=SQ_POSSIBLE,
min_max_surface=SQ_OK,
min_center_surface=nil
}
--! @brief a position where mob is in media, it's center has surface contact and is on a possible surface
LT_SAFE_POSSIBLE_EDGE_POS = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=nil,
min_geom_center=GQ_FULL,
min_min_surface=SQ_POSSIBLE,
min_max_surface=SQ_POSSIBLE,
min_center_surface=nil
}
--! @brief a position where mob is in media, it's center has surface contact and it's center is at least on a possible surface
LT_EDGE_POS_POSSIBLE_CENTER = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=nil,
min_geom_center=GQ_FULL,
min_min_surface=nil,
min_max_surface=nil,
min_center_surface=SQ_POSSIBLE
}
--! @brief a position where mob is in media, it's center has surface contact and it's center is at least on a good surface
LT_EDGE_POS_GOOD_CENTER = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=nil,
min_geom_center=GQ_FULL,
min_min_surface=nil,
min_max_surface=nil,
min_center_surface=SQ_OK
}
--! @brief a position where mob is in media, has at least partial contact and is at least on possible surface
LT_EDGE_POS = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=GQ_PARTIAL,
min_geom_center=nil,
min_min_surface=SQ_POSSIBLE,
min_max_surface=SQ_OK,
min_center_surface=nil
}
--! @brief a position where mob is in media, has at least partial contact but no contact at center
LT_DROP_PENDING = {
old_state=nil,
min_media=MQ_IN_MEDIA,
min_geom=GQ_PARTIAL,
min_geom_center=GQ_NONE,
min_min_surface=nil,
min_max_surface=nil,
min_center_surface=nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,325 @@
-------------------------------------------------------------------------------
-- 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 factions.lua
--! @brief contains factions adaption of mobf
--! @copyright Sapier
--! @author Sapier
--! @date 2013-08-21
--!
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mobf_factions"))
--! @class mobf_factions
--! @brief faction mod support for mobf
mobf_factions = {}
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] init()
--
--! @brief initialize mobf factions support
--! @memberof mobf_factions
--
-------------------------------------------------------------------------------
function mobf_factions.init()
if minetest.get_modpath("factions")then
mobf_rtd.factions_available = true
end
--need to catch my form events
minetest.register_on_player_receive_fields(mobf_factions.button_handler)
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] setupmob(factionsdata)
--
--! @brief creat factions a mob may be member of
--! @memberof mobf_factions
--
--! @param factionsdata data to set for mob
--
-------------------------------------------------------------------------------
function mobf_factions.setupmob(factionsdata)
if mobf_rtd.factions_available then
if factionsdata ~= nil and
factionsdata.member ~= nil then
for i=1,#factionsdata.member,1 do
if not factions.exists(factionsdata.member[i]) then
factions.add_faction(factionsdata.member[i])
end
end
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] setupentity(entity,preserved_data)
--
--! @brief add a mob to it's factons
--! @memberof mobf_factions
--
--! @param entity to be added to it's factions
--! @param preserved_data mob reputation data
--
-------------------------------------------------------------------------------
function mobf_factions.setupentity(entity,preserved_data)
if mobf_rtd.factions_available then
if entity.data.factions ~= nil and
entity.data.factions.member ~= nil then
for i=1,#entity.data.factions.member,1 do
factions.member_add(entity.data.factions.member[i],entity)
end
end
if preserved_data ~= nil then
for i=1,#preserved_data,1 do
factions.member_add(preserved_data[i],entity)
end
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] cleanup_entity(entity)
--
--! @brief called on deactivation of a mob
--! @memberof mobf_factions
--
--! @param entity to be cleant up
--
-------------------------------------------------------------------------------
function mobf_factions.cleanupentity(entity)
if mobf_rtd.factions_available then
return factions.get_factions(entity.object)
else
return ""
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] mob_rightclick_callback(entity,player)
--
--! @brief show factions rightclick menu
--! @memberof mobf_factions
--
--! @param entity to modify
--! @param player issuing rightclick
--
-------------------------------------------------------------------------------
function mobf_factions.mob_rightclick_callback(entity,player)
local menu_data = {}
local new_id = mobf_global_data_store(menu_data)
menu_data.entity = entity
menu_data.player = player
mobf_factions.show_mob_factions_menu(new_id,menu_data)
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] config_check(entity,player)
--
--! @brief show factions rightclick menu
--! @memberof mobf_factions
--
--! @param entity clicked
--
-------------------------------------------------------------------------------
function mobf_factions.config_check(entity)
if not mobf_rtd.factions_available then
return false
end
return false
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] button_handler(entity,player)
--
--! @brief show factions rightclick menu
--! @memberof mobf_factions
--
--! @param player issuing rightclick
--! @param formname name of form being transmitted
--! @param fields date for fields in current form
--
-------------------------------------------------------------------------------
function mobf_factions.button_handler(player, formname, fields)
local playername = player:get_player_name()
mobf_assert_backtrace(playername ~= nil)
if formname == "mobf:factions:main_menu" then
dbg_mobf.path_lvl2("MOBF: factions menu opened")
for k,v in pairs(fields) do
local parts = string.split(k,":")
if parts[1] == "mobf_factions" then
local menu_data = mobf_factions.handle_rightclick(parts,fields,formname,player,k)
if menu_data ~= nil then
--push data back to store
local new_id = mobf_global_data_store(menu_data)
mobf_factions.show_mob_factions_menu(new_id,menu_data)
break
end
end
end
return true
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] handle_rightclick(current_fields,fields,formname,player,fieldname)
--
--! @brief show factions rightclick menu
--! @memberof mobf_factions
--
--! @param current_fields
--! @param fields
--! @param formname
--! @param player
--! @param fieldname name of field causingt the callback
--
--! @return true/false show menu again
-------------------------------------------------------------------------------
function mobf_factions.handle_rightclick(current_fields,fields,formname,player,fieldname)
print("current_fields: " .. dump(current_fields))
--try to read data from global store
local menu_data = mobf_global_data_get(current_fields[3])
if menu_data ~= nil then
if current_fields[2] == "btn_add_mob_faction" then
if menu_data.tl_owner_selected ~= nil then
local factionlist_owner = factions.get_factions(menu_data.player)
if factionlist_owner ~= nil and
#factionlist_owner >= menu_data.tl_owner_selected then
local toadd = factionlist_owner[menu_data.tl_owner_selected]
factions.member_add(toadd,menu_data.entity.object)
end
end
elseif current_fields[2] == "btn_drop_mob_faction" then
if menu_data.tl_mob_selected ~= nil then
local factionlist_mob = factions.get_factions(menu_data.entity.object)
local todel = factionlist_mob[menu_data.tl_mob_selected]
if factions.is_member(todel,player) then
factions.member_remove(todel,menu_data.entity.object)
else
--TODO maybe send chat message
print("MOBF Factions you're not member of " .. todel .. " so you can't remove a mob from it")
end
else
print("MOBF Factions: trying to delete from faction but no faction selected")
end
elseif current_fields[2] == "tl_mob_factions" then
local event = core.explode_textlist_event(fields[fieldname])
if event.typ ~= "INV" then
menu_data.tl_mob_selected = event.index
end
elseif current_fields[2] == "tl_owner_factions" then
local event = core.explode_textlist_event(fields[fieldname])
if event.typ ~= "INV" then
menu_data.tl_owner_selected = event.index
end
end
else
dbg_mobf.path_lvl1("MOBF: factions menu failed to find menu_data")
end
return menu_data
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_factions] show_mob_factions_menu(new_id,menu_data)
--
--! @brief show factions rightclick menu
--! @memberof mobf_factions
--
--! @param new_id
--! @param menu_data
--
--! @return true/false show menu again
-------------------------------------------------------------------------------
function mobf_factions.show_mob_factions_menu(new_id,menu_data)
mobf_assert_backtrace(menu_data.entity ~= nil)
mobf_assert_backtrace(menu_data.player ~= nil)
local playername = menu_data.player:get_player_name()
local formspec = ""
--check if menu creator is owner of the specific mob
if menu_data.entity.dynamic_data.spawning.spawner == playername then
if menu_data.tl_mob_selected == nil then
menu_data.tl_mob_selected = 0
end
if menu_data.tl_owner_selected == nil then
menu_data.tl_owner_selected = 0
end
formspec = formspec .. "size[8,8]"
.."label[0,-0.25;Member of factions:]"
.."textlist[0,0.3;3.7,7;mobf_factions:tl_mob_factions:" .. new_id .. ";"
local factionlist_mob = factions.get_factions(menu_data.entity.object)
if #factionlist_mob ~= 0 then
for i=1,#factionlist_mob,1 do
formspec = formspec .. factionlist_mob[i] .. ","
end
else
formspec = formspec .. "not member of any faction"
end
formspec = formspec .. ";" .. menu_data.tl_mob_selected .. "]"
local factionlist_player = factions.get_factions(menu_data.player)
formspec = formspec
.."label[4,-0.25;Owner factions:]"
.."textlist[4,0.3;3.7,7;mobf_factions:tl_owner_factions: " .. new_id .. ";"
if #factionlist_player ~= 0 then
for i=1,#factionlist_player,1 do
formspec = formspec .. factionlist_player[i] .. ","
end
else
formspec = formspec .. "not member of any faction"
end
formspec = formspec .. ";" .. menu_data.tl_owner_selected .. "]"
formspec = formspec
.. "button[0,7.5;3.95,0.5;mobf_factions:btn_drop_mob_faction:" .. new_id .. ";Remove from faction]"
.. "button[4,7.5;3.95,0.5;mobf_factions:btn_add_mob_faction:" .. new_id .. ";<-- Mob join faction]"
else
formspec = "size[4,1]label[0,0;This is not your mob keep away!]" ..
"button_exit[1,0.75;2,0.5;btn_exit;Okay Okay!]"
end
--show formspec
minetest.show_formspec(playername,"mobf:factions:main_menu",formspec)
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,370 @@
-------------------------------------------------------------------------------
-- 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 graphics.lua
--! @brief graphics related parts of mob
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("graphics"))
--! @class graphics
--! @brief graphic features
graphics = {}
-------------------------------------------------------------------------------
-- @function [parent=#graphics] init_dynamic_data(entity,velocity)
--
--! @brief initialize values required by graphics
--! @memberof graphics
--
--! @param entity mob initialize
--! @param now current time
-------------------------------------------------------------------------------
function graphics.init_dynamic_data(entity,now)
local data = {
last_scalar_speed = nil,
}
entity.dynamic_data.graphics = data
end
-------------------------------------------------------------------------------
-- @function [parent=#graphics] update(entity,now,dtime)
--
--! @brief callback for updating graphics of mob
--! @memberof graphics
--
--! @param entity mob to calculate direction
--! @param now current time
--! @param dtime current dtime
-------------------------------------------------------------------------------
function graphics.update(entity,now,dtime)
--update animation speed
--replaced by core function (if ever merged)
--graphics.update_animation(entity,now,dtime)
--update attention
if entity.dynamic_data ~= nil and
entity.dynamic_data.attention ~= nil and
entity.data.attention ~= nil and
entity.dynamic_data.attention.most_relevant_target ~= nil and
entity.data.attention.watch_threshold ~= nil and
(entity.dynamic_data.attention.current_value >
entity.data.attention.watch_threshold) then
dbg_mobf.graphics_lvl3("MOBF: attention mode orientation update")
local direction = mobf_get_direction(entity.object:getpos(),
entity.dynamic_data.attention.most_relevant_target:getpos())
if entity.mode == "3d" then
graphics.setyaw(entity,
mobf_calc_yaw(direction.x,direction.z))
else
graphics.setyaw(entity,
mobf_calc_yaw(direction.x,direction.z)+math.pi/2)
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#graphics] update_animation(entity,now,dtime)
--
--! @brief callback for updating graphics of mob
--! @memberof graphics
--
--! @param entity mob to calculate direction
--! @param now current time
--! @param dtime current dtime
-------------------------------------------------------------------------------
function graphics.update_animation(entity,now,dtime)
if entity.dynamic_data.animation ~= nil then
local animdata = entity.data.animation[entity.dynamic_data.animation]
if animdata ~= nil and
animdata.basevelocity ~= nil then
local current_velocity = entity.object:getvelocity()
local scalar_velocity = mobf_calc_scalar_speed(current_velocity.x,current_velocity.z)
if entity.dynamic_data.graphics.last_scalar_speed ~= nil then
local speeddiff =
DELTA(scalar_velocity,
entity.dynamic_data.graphics.last_scalar_speed)
if speeddiff > 0.05 then
local current_fps = scalar_velocity/animdata.basevelocity * 15
entity.object:set_animation_speed(current_fps)
entity.dynamic_data.graphics.last_scalar_speed = scalar_velocity
entity.dynamic_data.graphics.last_fps = current_fps
end
else
entity.dynamic_data.graphics.last_scalar_speed = scalar_velocity
end
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#graphics] set_animation(entity,name)
--
--! @brief set the drawmode for an mob entity
--! @memberof graphics
--
--! @param entity mob to set drawmode for
--! @param name name of animation
-------------------------------------------------------------------------------
function graphics.set_animation(entity,name)
if name == nil then
dbg_mobf.graphics_lvl2("MOBF: calling updating animation without name for " .. entity.data.name)
return
end
if entity.mode == "2d" then
if id == "stand" then
entity.object:setsprite({x=0,y=0}, 1, 0, true)
end
if name == "burning" then
entity.object:setsprite({x=0,y=1}, 1, 0, true)
return
end
--fallback
entity.object:setsprite({x=0,y=0}, 1, 0, true)
return
end
if entity.mode == "3d" then
--TODO change frame rate due to movement speed
dbg_mobf.graphics_lvl2("MOBF: " .. entity.data.name .. " updating animation: " .. name)
if entity.data.animation ~= nil and
name ~= nil and
entity.data.animation[name] ~= nil and
entity.dynamic_data.animation ~= name then
dbg_mobf.graphics_lvl2("MOBF:\tSetting animation to " .. name
.. " start: " .. entity.data.animation[name].start_frame
.. " end: " .. entity.data.animation[name].end_frame)
entity.object:set_animation({
x=entity.data.animation[name].start_frame,
y=entity.data.animation[name].end_frame
},
entity.data.animation[name].anim_speed,
nil,
entity.data.animation[name].basevelocity)
entity.dynamic_data.animation = name
end
return
end
mobf_bug_warning(LOGLEVEL_WARNING,"MOBF BUG!!: invalid graphics mode specified "
.. dump(entity.mode))
end
------------------------------------------------------------------------------
-- @function [parent=#graphics] graphics_by_statename(graphics2d,graphics3d)
--
--! @brief get graphics information
--! @memberof graphics
--
--! @param mob static data
--! @param statename name of state
--
--! @return grahphic information
-------------------------------------------------------------------------------
function graphics.graphics_by_statename(mob,statename)
local dummyentity = { data = mob }
local default_state = mob_state.get_state_by_name(dummyentity,"default")
local selected_state = nil
if statename == "default" then
selected_state = default_state
else
selected_state = mob_state.get_state_by_name(dummyentity,statename)
end
if selected_state == nil then
selected_state = default_state
end
if selected_state.graphics_3d == nil then
selected_state.graphics_3d = default_state.graphics_3d
end
if selected_state.graphics == nil then
selected_state.graphics = default_state.graphics
end
local setgraphics = {}
if (selected_state.graphics_3d == nil) or
minetest.world_setting_get("mobf_disable_3d_mode") then
if (selected_state.graphics == nil) then
--no idea if there is any legitimate reason for this
mobf_print("state: " .. dump(selected_state))
mobf_print("there ain't even 2d graphics available")
return nil
end
local basename = modname .. name
if statename ~= nil and
statename ~= "default" then
basename = basename .. "__" .. statename
end
setgraphics.collisionbox = {-0.5,
-0.5 * selected_state.graphics.visible_height,
-0.5,
0.5,
0.5 * selected_state.graphics.visible_height,
0.5}
if selected_state.graphics.visual ~= nil then
selected_state.graphics.visual = selected_state.graphics.visual
else
selected_state.graphics.visual = "sprite"
end
setgraphics.textures = { basename..".png^[makealpha:128,0,0^[makealpha:128,128,0" }
setgraphics.visual_size = selected_state.graphics.sprite_scale
setgraphics.spritediv = selected_state.graphics.sprite_div
setgraphics.mode = "2d"
else
if selected_state.graphics_3d.visual == "mesh" then
setgraphics.mesh = selected_state.graphics_3d.mesh
end
setgraphics.collisionbox = selected_state.graphics_3d.collisionbox
setgraphics.visual = selected_state.graphics_3d.visual
setgraphics.visual_size = selected_state.graphics_3d.visual_size
setgraphics.textures = selected_state.graphics_3d.textures
setgraphics.texturelist = selected_state.graphics_3d.texturelist
setgraphics.mode = "3d"
setgraphics.model_orientation_fix = selected_state.graphics_3d.model_orientation_fix
end
return setgraphics
end
------------------------------------------------------------------------------
-- @function [parent=#graphics] prepare_graphic_info(graphics2d,graphics3d)
--
--! @brief get graphics information
--! @memberof graphics
--
--! @param graphics2d
--! @param graphics3d
--! @param modname
--! @param name
--! @param statename
--! @return grahpic information
-------------------------------------------------------------------------------
function graphics.prepare_info(graphics2d,graphics3d,modname,name,statename)
local setgraphics = {}
if (graphics3d == nil) or
minetest.world_setting_get("mobf_disable_3d_mode") then
if (graphics2d == nil) then
--this maybe correct if there's a state model requested!
return nil
end
local basename = modname .. name
if statename ~= nil and
statename ~= "default" then
basename = basename .. "__" .. statename
end
setgraphics.collisionbox = {-0.5,
-0.5 * graphics2d.visible_height,
-0.5,
0.5,
0.5 * graphics2d.visible_height,
0.5}
if graphics2d.visual ~= nil then
setgraphics.visual = graphics2d.visual
else
setgraphics.visual = "sprite"
end
setgraphics.textures = { basename..".png^[makealpha:128,0,0^[makealpha:128,128,0" }
setgraphics.visual_size = graphics2d.sprite_scale
setgraphics.spritediv = graphics2d.sprite_div
setgraphics.mode = "2d"
else
if graphics3d.visual == "mesh" then
setgraphics.mesh = graphics3d.mesh
end
setgraphics.collisionbox = graphics3d.collisionbox --todo is this required for mesh?
setgraphics.visual = graphics3d.visual
setgraphics.visual_size = graphics3d.visual_size
setgraphics.textures = graphics3d.textures
setgraphics.mode = "3d"
end
return setgraphics
end
------------------------------------------------------------------------------
-- @function [parent=#graphics] setyaw(entity,value)
--
--! @brief update yaw for a specific entity (overlay to workaround model bugs)
--! @memberof graphics
--
--! @param entity entity to set yaw for
--! @param value yaw value to set
-------------------------------------------------------------------------------
function graphics.setyaw(entity, value)
local current_graphics = graphics.graphics_by_statename(entity.data,
entity.dynamic_data.state.current.name)
if current_graphics.model_orientation_fix ~= nil then
value = value + current_graphics.model_orientation_fix
end
entity.object:setyaw(value)
end
------------------------------------------------------------------------------
-- @function [parent=#graphics] getyaw(entity,value)
--
--! @brief read yaw for this object, fix model offset
--! @memberof graphics
--
--! @param entity entity to set yaw for
--! @param value yaw value to set
-------------------------------------------------------------------------------
function graphics.getyaw(entity)
local retval = 0
local current_graphics = graphics.graphics_by_statename(entity.data,
entity.dynamic_data.state.current.name)
if current_graphics.model_orientation_fix ~= nil then
retval = retval - current_graphics.model_orientation_fix
end
retval = retval + entity.object:getyaw()
return retval
end

View File

@ -0,0 +1,258 @@
-------------------------------------------------------------------------------
-- 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 harvesting.lua
--! @brief component for all harvesting related mob features
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup harvesting Harvesting subcomponent
--! @brief Component handling harvesting
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("harvesting"))
--! @class harvesting
--! @brief harvesting features
harvesting = {}
--!@}
-------------------------------------------------------------------------------
-- @function [parent=#harvesting] init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by harvesting
--! @memberof harvesting
--
--! @param entity mob to initialize harvest dynamic data
--! @param now current time
-------------------------------------------------------------------------------
function harvesting.init_dynamic_data(entity,now)
dbg_mobf.harvesting_lvl1("MOBF: " .. entity.data.name
.. " initializing harvesting dynamic data")
local data = {
ts_last = now,
}
entity.dynamic_data.harvesting = data
end
-------------------------------------------------------------------------------
-- @function [parent=#harvesting] callback(entity,player,now)
--
--! @brief callback handler for harvest by player
--! @memberof harvesting
--
--! @param entity mob being harvested
--! @param player player harvesting
--! @param now the current time
--! @return true/false if handled by harvesting or not
-------------------------------------------------------------------------------
function harvesting.callback(entity,player,now)
dbg_mobf.harvesting_lvl1("MOBF: harvest function called")
local now = mobf_get_current_time()
--handle catching of mob
if entity.data.catching ~= nil and
entity.data.catching.tool ~= "" then
if (entity.dynamic_data.spawning.player_spawned and
entity.dynamic_data.spawning.spawner == nil) then
dbg_mobf.harvesting_lvl1("MOBF: mob flagged as player spanwned but no spawner set!")
entity.dynamic_data.spawning.player_spawned = false
end
--grief protection
if minetest.world_setting_get("mobf_grief_protection") and
entity.dynamic_data.spawning.player_spawned and
entity.dynamic_data.spawning.spawner ~= player:get_player_name() then
dbg_mobf.harvesting_lvl1("MOBF: anti gief triggered catching aborted")
return true
end
-- what's wielded by player
local tool = player:get_wielded_item()
if tool:get_name() == entity.data.catching.tool then
dbg_mobf.harvesting_lvl1("MOBF: player wearing ".. entity.data.catching.tool)
if type(entity.data.catching.can_be_cought) == "function" then
if (not entity.data.catching.can_be_cought(entity)) then
dbg_mobf.harvesting_lvl1("MOBF: entity denied catching")
return true
end
end
--check if player has enough room
local inventory_add_result = nil
if entity.data.generic.addoncatch ~= nil then
inventory_add_result = player:get_inventory():add_item("main",
entity.data.generic.addoncatch.." 1")
dbg_mobf.harvesting_lvl2(
"MOBF: adding specified oncatch item: " ..
entity.data.generic.addoncatch)
else
inventory_add_result = player:get_inventory():add_item("main",
entity.data.modname ..":"..entity.data.name.." 1")
dbg_mobf.harvesting_lvl2(
"MOBF: adding automatic oncatch item: " ..
entity.data.modname ..":"..entity.data.name)
end
if not inventory_add_result:is_empty() then
minetest.chat_send_player(player:get_player_name(),
"You don't have any room left in inventory!")
return true
end
--play catch sound
if entity.data.sound ~= nil then
sound.play(entity.object:getpos(),entity.data.sound.catch);
end
if entity.data.catching.consumed == true then
if player:get_inventory():contains_item("main",entity.data.catching.tool.." 1") then
dbg_mobf.harvesting_lvl2("MOBF: removing: "
.. entity.data.catching.tool.." 1")
player:get_inventory():remove_item("main",
entity.data.catching.tool.." 1")
else
mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!! player is"
.. " wearing a item he doesn't have in inventory!!!")
end
end
spawning.remove(entity, "cought")
return true
end
end
--handle harvestable mobs, check if player is wearing correct tool
if entity.data.harvest ~= nil then
dbg_mobf.harvesting_lvl1("MOBF: trying to harvest harvestable mob")
if (entity.data.harvest.tool ~= "") then
local tool = player:get_wielded_item()
if tool ~= nil then
dbg_mobf.harvesting_lvl1("MOBF: Player is wearing >"
.. tool:get_name() .. "< required is >".. entity.data.harvest.tool
.. "< wear: " .. tool:get_wear())
if (tool:get_name() ~= entity.data.harvest.tool) then
--player is wearing wrong tool do an attack
return false
else
--tool is completely consumed
if entity.data.harvest.tool_consumed == true then
if player:get_inventory():contains_item("main",entity.data.harvest.tool.." 1") == false then
dbg_mobf.harvesting_lvl1("MOBF: Player doesn't have"
.. " at least 1 of ".. entity.data.harvest.tool)
--handled but not ok so don't attack
return true
end
else
--damage tool
local tool_wear = tool:get_wear()
dbg_mobf.harvesting_lvl1("MOBF: tool " .. tool:get_name()
.. " wear: " .. tool_wear)
-- damage used tool
if tool_wear ~= nil and
entity.data.harvest.max_tool_usage ~= nil then
local todamage = (65535/entity.data.harvest.max_tool_usage)
dbg_mobf.harvesting_lvl1("MOBF: tool damage calculated: "
.. todamage);
if tool:add_wear(todamage) ~= true then
dbg_mobf.harvesting_lvl3("MOBF: Tried to damage non tool item "
.. tool:get_name() .. "!");
end
player:set_wielded_item(tool)
end
end
end
else
--player isn't wearing a tool so this has to be an attack
return false
end
else
--no havest tool defined so this has to be an attack
return false
end
--transformation and harvest delay is exclusive
--harvest delay mode
if entity.data.harvest.min_delay < 0 or
entity.dynamic_data.harvesting.ts_last + entity.data.harvest.min_delay < now then
--TODO check if player has enough room
player:get_inventory():add_item("main", entity.data.harvest.result.." 1")
--check if tool is consumed by action
if entity.data.harvest.tool_consumed then
dbg_mobf.harvesting_lvl2("MOBF: removing "
..entity.data.harvest.tool.." 1")
player:get_inventory():remove_item("main",entity.data.harvest.tool.." 1")
end
else
dbg_mobf.harvesting_lvl1("MOBF: " .. entity.data.name
.. " not ready to be harvested")
end
-- check if mob is transformed by harvest
if entity.data.harvest.transforms_to ~= nil and
entity.data.harvest.transforms_to ~= "" then
local transformed = spawning.replace_entity(entity,
entity.data.harvest.transforms_to)
else
entity.dynamic_data.harvesting.ts_last = mobf_get_current_time()
end
--play harvest sound
if entity.data.sound ~= nil then
sound.play(entity.object:getpos(),entity.data.sound.harvest);
end
--harvest done
return true
end
return false
end
-------------------------------------------------------------------------------
-- @function transform(entity)
--
--! @brief self transform callback for mob
--! @ingroup harvesting
--
--! @param entity mob calling
--! @param now current time
-------------------------------------------------------------------------------
function transform(entity,now)
--check if it's a transformable mob
if entity.data.auto_transform ~= nil then
if now - entity.dynamic_data.spawning.original_spawntime
> entity.data.auto_transform.delay then
spawning.replace_entity(entity,entity.data.auto_transform.result)
return false
end
end
end

View File

@ -0,0 +1,408 @@
-------------------------------------------------------------------------------
-- 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 allowed to pretend you have written it.
--
--! @file init.lua
--! @brief main module file responsible for including all parts of mob framework mod
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup framework_int Internal framework subcomponent API
--! @brief this functions are used to provide additional features to mob framework
--! e.g. add additional spawn algorithms, movement generators, environments ...
--
--
--! @defgroup framework_mob Mob Framework API
--! @brief this functions are used to add a mob to mob framework
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
core.log("action","MOD: mobf loading ...")
--! @brief runtime data required to be setup once on start
mobf_rtd = {
--!is mob running with fire support
fire_enabled = false,
--!do we have luatrace
luatrace_enabled = false,
--!do we have inventory plus support
have_adv_spawning = false,
--!do we have factions support
factions_available = false,
--!registry for movement patterns
movement_patterns = {},
--!registry of mobs
registred_mob = {},
--!registred mobs_data
registred_mob_data = {},
--!timesource
timesource = "os.clock() (10ms ONLY!)",
--!total spawned mobs
total_spawned = 0,
--!detailed debug state
detailed_state = false,
}
--!path of mod
mobf_modpath = minetest.get_modpath("mobf")
LOGLEVEL_INFO = "verbose"
LOGLEVEL_NOTICE = "info"
LOGLEVEL_WARNING = "action"
LOGLEVEL_ERROR = "error"
LOGLEVEL_CRITICAL = "error"
-- initialize luatrace if necessary
if mobf_rtd.luatrace_enabled then
luatrace = require("luatrace")
end
-- minetest workarounds
if not type(core.global_exists) ~= "function" then
core.global_exists = function(name)
return core[name] ~= nil
end
end
--include debug trace functions
dofile (mobf_modpath .. "/utils/text.lua")
dofile (mobf_modpath .. "/debug_trace.lua")
--include engine
dofile (mobf_modpath .. "/utils/error_handling.lua")
dofile (mobf_modpath .. "/utils/settings.lua")
dofile (mobf_modpath .. "/utils/generic_functions.lua")
dofile (mobf_modpath .. "/utils/data_storage.lua")
dofile (mobf_modpath .. "/utils/tracing.lua")
dofile (mobf_modpath .. "/utils/geometry.lua")
dofile (mobf_modpath .. "/utils/permanent_data.lua")
dofile (mobf_modpath .. "/lifebar.lua")
dofile (mobf_modpath .. "/env_constants.lua")
dofile (mobf_modpath .. "/environment.lua")
dofile (mobf_modpath .. "/attention.lua")
dofile (mobf_modpath .. "/movement_generic.lua")
dofile (mobf_modpath .. "/graphics.lua")
dofile (mobf_modpath .. "/movement_gen_registry.lua")
dofile (mobf_modpath .. "/harvesting.lua")
dofile (mobf_modpath .. "/fighting.lua")
dofile (mobf_modpath .. "/random_drop.lua")
dofile (mobf_modpath .. "/sound.lua")
dofile (mobf_modpath .. "/ride.lua")
dofile (mobf_modpath .. "/mobf.lua")
dofile (mobf_modpath .. "/api.lua")
dofile (mobf_modpath .. "/debug.lua")
dofile (mobf_modpath .. "/mob_state.lua")
dofile (mobf_modpath .. "/inventory.lua")
dofile (mobf_modpath .. "/path.lua")
dofile (mobf_modpath .. "/factions.lua")
dofile (mobf_modpath .. "/step_quota.lua")
--include spawning support
dofile (mobf_modpath .. "/spawning.lua")
--include movement generators
dofile (mobf_modpath .. "/mgen_probab/main_probab.lua")
dofile (mobf_modpath .. "/mgen_follow/main_follow.lua")
dofile (mobf_modpath .. "/mgen_rasterized/mgen_raster.lua")
dofile (mobf_modpath .. "/mgen_jordan4ibanez/mgen_jordan4ibanez.lua")
dofile (mobf_modpath .. "/mgen_pathbased/main.lua")
dofile (mobf_modpath .. "/mgen_flee/main_flee.lua")
dofile (mobf_modpath .. "/mov_gen_none.lua")
mobf_version = "2.5.1"
--! @brief main initialization function
function mobf_init_framework()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initializing mob framework")
minetest.log(LOGLEVEL_NOTICE,"MOBF: Reading mob blacklist")
local mobf_mob_blacklist_string = minetest.world_setting_get("mobf_blacklist")
if mobf_mob_blacklist_string ~= nil then
mobf_rtd.registred_mob = minetest.deserialize(mobf_mob_blacklist_string)
if mobf_rtd.registred_mob == nil then
minetest.log(LOGLEVEL_ERROR,"MOBF: Error on serializing blacklist!")
mobf_rtd.registred_mob = {}
end
end
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize timesource...")
mobf_init_timesource()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize statistics...")
mobf_init_statistics()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize factions support...")
mobf_factions.init()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize external mod dependencys...")
mobf_init_mod_deps()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initializing probabilistic movement generator")
movement_gen.initialize()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initializing debug hooks..")
mobf_debug.init()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initializing mob preservation..")
local preserved_mobs_raw = mobf_get_world_setting("mobf_preserve_mobs")
if preserved_mobs_raw ~= nil then
mobf.current_preserve_list =
minetest.deserialize(preserved_mobs_raw)
end
if mobf.current_preserve_list == nil then
mobf.current_preserve_list = {}
end
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize path handling subsystem..")
mobf_path.init()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize lifebar subsystem..")
mobf_lifebar.init()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize spawning subsystem..")
spawning.init()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize mobf supplied modules..")
mobf_init_modules()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Register rightclick button handler..")
minetest.register_on_player_receive_fields(mobf.rightclick_button_handler)
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initializing step time quota .. ")
mobf_step_quota.initialize()
-- register privilege to change mobf settings
minetest.register_privilege("mobfw_admin",
{
description = "Player may change mobf settings",
give_to_singleplayer = true
})
core.log("action","MOD: mob framework mod "..mobf_version.." loaded")
end
--! @brief initialize mod dependencys
function mobf_init_mod_deps()
local modlist = minetest.get_modnames()
for i=1,#modlist,1 do
if modlist[i] == "fire" then
mobf_rtd.fire_enabled = true
end
if modlist[i] == "adv_spawning" then
mobf_rtd.have_adv_spawning = true
end
end
end
--! @brief initialize mobf submodules
function mobf_init_modules()
--state change callback
mobf.register_on_step_callback({
name = "state_change",
handler = mob_state.callback,
init = mob_state.initialize,
configcheck = function(entity)
if entity.data.states ~= nil then
return true
end
return false
end
})
--auto transform hook
mobf.register_on_step_callback({
name = "transform",
handler = transform,
init = nil,
configcheck = function(entity)
if entity.data.auto_transform ~= nil then
return true
end
return false
end
})
--combat hook
mobf.register_on_step_callback({
name = "combat",
handler = fighting.combat,
init = fighting.init_dynamic_data,
configcheck = function(entity)
if entity.data.combat ~= nil then
return true
end
return false
end
})
--attention hook
mobf.register_on_step_callback({
name = "attention",
handler = attention.callback,
init = attention.init_dynamic_data,
configcheck = function(entity)
if entity.data.attention ~= nil or
entity.data.combat ~= nil then
return true
end
return false
end
})
--workaround for shortcomings in spawn algorithm
mobf.register_on_step_callback({
name = "check_pop_dense",
handler = spawning.population_density_check,
init = spawning.init_dynamic_data,
configcheck = function(entity)
if entity.data.generic.population_density ~= nil or
entity.data.spawning ~= nil then
return true
else
return false
end
end
})
--random drop hook
mobf.register_on_step_callback({
name = "random_drop",
handler = random_drop.callback,
init = random_drop.init_dynamic_data,
configcheck = function(entity)
if entity.data.random_drop ~= nil then
return true
end
return false
end
})
--random sound hook
mobf.register_on_step_callback({
name = "sound",
handler = sound.play_random,
init = sound.init_dynamic_data,
configcheck = function(entity)
if entity.data.sound ~= nil and
entity.data.sound.random ~= nil then
return true
end
return false
end
})
--visual change hook
mobf.register_on_step_callback({
name = "update_graphics",
handler = graphics.update,
init = graphics.init_dynamic_data,
configcheck = function(entity)
return true
end
})
--custom hook
mobf.register_on_step_callback({
name = "custom_hooks",
handler = function(entity,now,dtime)
if type(entity.data.generic.custom_on_step_handler) == "function" then
entity.data.generic.custom_on_step_handler(entity,now,dtime)
end
end,
configcheck = function(entity)
return true
end
})
--on punch callbacks
mobf.register_on_punch_callback({
name = "harvesting",
handler = harvesting.callback,
init = harvesting.init_dynamic_data,
configcheck = function(entity)
if (entity.data.catching ~= nil and
entity.data.catching.tool ~= "" ) or
entity.data.harvest ~= nil then
return true
end
return false
end
})
mobf.register_on_punch_callback({
name = "riding",
handler = mobf_ride.on_punch_callback,
configcheck = mobf_ride.is_enabled
})
mobf.register_on_punch_callback({
name = "punching",
handler = fighting.hit,
configcheck = function(entity)
return true
end
})
--on rightclick callbacks
mobf.register_on_rightclick_callback({
name = "tradercallback",
visiblename = "Trade",
handler = mob_inventory.trader_callback,
configcheck = mob_inventory.config_check
})
mobf.register_on_rightclick_callback({
name = "debugcallback",
visiblename = "Show debuginfo",
handler = mobf_debug.rightclick_callback,
configcheck = function(entity)
return true
end,
privs = {mobfw_admin=true}
})
mobf.register_on_rightclick_callback({
name = "pathcallback",
visiblename = mobf_path.buttontext,
handler = mobf_path.mob_rightclick_callback,
configcheck = mobf_path.config_check
})
mobf.register_on_rightclick_callback({
name = "factions",
visiblename = "Factions",
handler = mobf_factions.mob_rightclick_callback,
configcheck = mobf_factions.config_check
})
mobf.register_on_rightclick_callback({
name = "heal",
visiblename = fighting.heal_caption,
handler = fighting.heal,
configcheck = function(entity)
return true
end,
})
end
mobf_init_framework()
dofile (mobf_modpath .. "/compatibility.lua")

View File

@ -0,0 +1,643 @@
-------------------------------------------------------------------------------
-- 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 inventory.lua
--! @brief component containing mob inventory related functions
--! @copyright Sapier
--! @author Sapier
--! @date 2013-01-02
--
--! @defgroup Inventory Inventory subcomponent
--! @brief Component handling mob inventory
--! @ingroup framework_int
--! @{
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mob_inventory"))
--! @class mob_inventory
--! @brief inventory handling for trader like mobs
--! @}
mob_inventory = {}
-- Boilerplate to support localized strings if intllib mod is installed.
local S
if core.global_exists("intllib") then
S = intllib.Getter()
else
S = function(s) return s end
end
-------------------------------------------------------------------------------
mob_inventory.trader_inventories = {}
mob_inventory.formspecs = {}
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] allow_move(inv, from_list, from_index, to_list, to_index, count, player)
--
--! @brief check if there is enough at payroll
--! @memberof mob_inventory
--! @private
--
--! @param inv inventory reference
--! @param from_list name of list elements taken
--! @param from_index index at list elements taken
--! @param to_list list name of list elements being put
--! @param to_index index at list elements being put
--! @param count number of elements moved
--! @param player doing changes
--
--! @return number of elements allowed to move
-------------------------------------------------------------------------------
function mob_inventory.allow_move(inv, from_list, from_index, to_list, to_index, count, player)
dbg_mobf.trader_inv_lvl1("MOBF: move inv: " .. tostring(inv) .. " from:"
.. dump(from_list) .. " to: " .. dump(to_list))
if to_list ~= "selection" or
from_list == "price_1" or
from_list == "price_2" or
from_list == "pay" or
from_list == "takeaway" or
from_list == "identifier" then
return 0
end
-- forbid moving of parts of stacks
local old_stack = inv.get_stack(inv, from_list, from_index)
if count ~= old_stack.get_count(old_stack) then
return 0;
end
return count
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] allow_put(inv, listname, index, stack, player)
--
--! @brief check if there is enough at payroll
--! @memberof mob_inventory
--! @private
--
--! @param inv inventory reference
--! @param listname name of list changed
--! @param index index in list changed
--! @param stack moved
--! @param player doing changes
--
--! @return number of elements allowed to put
-------------------------------------------------------------------------------
function mob_inventory.allow_put(inv, listname, index, stack, player)
dbg_mobf.trader_inv_lvl1("MOBF: put inv: " .. tostring(inv) .. " to:"
.. dump(listname))
if listname == "pay" then
return 99
end
return 0
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] allow_take(inv, listname, index, stack, player)
--
--! @brief check if there is enough at payroll
--! @memberof mob_inventory
--! @private
--
--! @param inv inventory reference
--! @param listname name of list changed
--! @param index index in list changed
--! @param stack moved
--! @param player doing changes
--
--! @return number of elements allowed to take
-------------------------------------------------------------------------------
function mob_inventory.allow_take(inv, listname, index, stack, player)
dbg_mobf.trader_inv_lvl1("MOBF: take inv: " .. tostring(inv) .. " to:"
.. dump(listname))
if listname == "takeaway" or
listname == "pay" then
return 99
end
return 0
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] on_move(inv, from_list, from_index, to_list, to_index, count, player)
--
--! @brief check if there is enough at payroll
--! @memberof mob_inventory
--! @private
--
--! @param inv inventory reference
--! @param from_list name of list elements taken
--! @param from_index index at list elements taken
--! @param to_list list name of list elements being put
--! @param to_index index at list elements being put
--! @param count number of elements moved
--! @param player doing changes
-------------------------------------------------------------------------------
function mob_inventory.on_move(inv, from_list, from_index, to_list, to_index, count, player)
dbg_mobf.trader_inv_lvl1("MOBF: inv\"" .. tostring(inv) .. "\" moving "
.. count .. " items from: " .. from_list .. ":" .. from_index .. " to: "
.. to_list .. ":" .. to_index)
if from_list == "goods" and
to_list == "selection" then
local moved = inv.get_stack(inv,to_list, to_index)
local goodname = moved.get_name(moved)
local elements = moved.get_count(moved)
-- handle situations where diffrent amounts of the same good are offered-
dbg_mobf.trader_inv_lvl2("MOBF: moving " .. dump(elements) .."/"
.. dump(count) .. " from "
.. dump(from_list).. " to ".. dump(from_list )..
" at index "..dump(from_index ))
-- if it was the same type of good (and now contains a summed up number
-- of offerings) put the old stack back into the traders inv
if( elements > count ) then
dbg_mobf.trader_inv_lvl2(
"MOBF: ok, whe have to give back a stack of "
..tostring( elements - count ).." "..tostring( goodname )
..". target stack: "..tostring( from_index ))
-- remove the surplus parts
inv.set_stack(inv,"selection", 1,goodname.." "..tostring( count ))
-- the slot we took from is now free
inv.set_stack(inv,"goods",from_index,
goodname.." "..tostring( elements - count ))
-- update the real amount of items in the slot now
elements = count
end
local entity = mob_inventory.get_entity(inv)
if entity == nil then
dbg_mobf.trader_inv_lvl1("MOBF: move unable to find linked entity")
return
end
dbg_mobf.trader_inv_lvl1("MOBF: good selected: " .. goodname)
--get element put to selection
mob_inventory.fill_prices(entity,inv,goodname,count)
mob_inventory.update_takeaway(inv)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] on_put(inv, listname, index, stack, player)
--
--! @brief check if there is enough at payroll
--! @memberof mob_inventory
--! @private
--
--! @param inv inventory reference
--! @param listname name of list changed
--! @param index index in list changed
--! @param stack moved
--! @param player doing changes
-------------------------------------------------------------------------------
function mob_inventory.on_put(inv, listname, index, stack, player)
if listname == "pay" then
local now_at_pay = inv.get_stack(inv,"pay",1)
local playername = player.get_player_name(player)
local count = now_at_pay.get_count(now_at_pay)
local name = now_at_pay.get_name(now_at_pay)
dbg_mobf.trader_inv_lvl1("MOBF: putpay player: " .. playername
.. " pays now count=" .. count .. " of type=" ..name)
mob_inventory.update_takeaway(inv)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] on_take(inv, listname, index, stack, player)
--
--! @brief check if there is enough at payroll
--! @memberof mob_inventory
--! @private
--
--! @param inv inventory reference
--! @param listname name of list changed
--! @param index index in list changed
--! @param stack moved
--! @param player doing changes
-------------------------------------------------------------------------------
function mob_inventory.on_take(inv, listname, index, stack, player)
if listname == "takeaway" then
local now_at_pay = inv.get_stack(inv,"pay",index)
local playername = player.get_player_name(player)
local count = now_at_pay.get_count(now_at_pay)
local name = now_at_pay.get_name(now_at_pay)
dbg_mobf.trader_inv_lvl2("MOBF: takeaway player: " .. playername
.. " pays now count=" .. count .. " of type=" ..name)
if not mob_inventory.check_pay(inv,true) then
dbg_mobf.trader_inv_lvl1("MOBF: error player hasn't payed enough!")
end
mob_inventory.update_takeaway(inv)
end
if listname == "pay" then
if mob_inventory.check_pay(inv,false) then
local selection = inv.get_stack(inv,"selection", 1)
if selection ~= nil then
inv.set_stack(inv,"takeaway",1,selection)
else
dbg_mobf.trader_inv_lvl1("MOBF: nothing selected to buy")
end
else
inv.set_stack(inv,"takeaway",1,nil)
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] update_takeaway(inv)
--
--! @brief update content of takeaway
--! @memberof mob_inventory
--! @private
--
--! @param inv to update
-------------------------------------------------------------------------------
function mob_inventory.update_takeaway(inv)
if mob_inventory.check_pay(inv,false) then
local selection = inv.get_stack(inv,"selection", 1)
if selection ~= nil then
inv.set_stack(inv,"takeaway",1,selection)
else
dbg_mobf.trader_inv_lvl1("MOBF: nothing selected to buy")
end
else
inv.set_stack(inv,"takeaway",1,nil)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] check_pay(inv)
--
--! @brief check if there is enough at payroll
--! @memberof mob_inventory
--! @private
--
--! @param inv inventory to do check
--! @param paynow true/false if it's called to pay or not
--
--! @return true/false
-------------------------------------------------------------------------------
function mob_inventory.check_pay(inv,paynow)
local now_at_pay = inv.get_stack(inv,"pay",1)
local count = now_at_pay.get_count(now_at_pay)
local name = now_at_pay.get_name(now_at_pay)
local price1 = inv.get_stack(inv,"price_1", 1)
local price2 = inv.get_stack(inv,"price_2", 1)
dbg_mobf.trader_inv_lvl1(
"MOBF: p1 " .. dump(price1) .. " " .. dump(price1:get_name()))
if price1:get_name() == name then
local price = price1:get_count()
if price > 0 and
price <= count then
if paynow then
now_at_pay.take_item(now_at_pay,price)
inv.set_stack(inv,"pay",1,now_at_pay)
return true
else
return true
end
else
if paynow then
inv.set_stack(inv,"pay",1,nil)
end
end
end
dbg_mobf.trader_inv_lvl1(
"MOBF: p2 " .. dump(price1) .. " " .. dump(price2:get_name()))
if price2:get_name() == name then
local price = price2:get_count()
if price > 0 and
price <= count then
if paynow then
now_at_pay.take_item(now_at_pay,price)
inv.set_stack(inv,"pay",1,now_at_pay)
return true
else
return true
end
else
if paynow then
inv.set_stack(inv,"pay",1,nil)
end
end
end
return false
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] init_detached_inventories(entity,now)
--
--! @brief initialize dynamic data required by harvesting
--! @memberof mob_inventory
--! @public
--
--! @param entity mob to initialize harvest dynamic data
-------------------------------------------------------------------------------
function mob_inventory.init_trader_inventory(entity)
--TODO find out why calling "tostring" is necessary?!
local tradername = tostring(
entity.data.trader_inventory.random_names[
math.random(1,#entity.data.trader_inventory.random_names)])
dbg_mobf.trader_inv_lvl3(
"MOBF: randomly selected \"" .. tradername .. "\" as name")
local unique_entity_id = string.gsub(tostring(entity),"table: ","")
--local unique_entity_id = "testinv"
local trader_inventory =
minetest.create_detached_inventory(unique_entity_id,
{
allow_move = mob_inventory.allow_move,
allow_put = mob_inventory.allow_put,
allow_take = mob_inventory.allow_take,
on_move = mob_inventory.on_move,
on_put = mob_inventory.on_put,
on_take = mob_inventory.on_take,
})
trader_inventory.set_size(trader_inventory,"goods",16)
trader_inventory.set_size(trader_inventory,"takeaway",1)
trader_inventory.set_size(trader_inventory,"selection",1)
trader_inventory.set_size(trader_inventory,"price_1",1)
trader_inventory.set_size(trader_inventory,"price_2",1)
trader_inventory.set_size(trader_inventory,"pay",1)
mob_inventory.add_goods(entity,trader_inventory)
--register to trader inventories
table.insert(mob_inventory.trader_inventories, {
identifier = unique_entity_id,
inv_ref = trader_inventory,
ent_ref = entity,
})
dbg_mobf.trader_inv_lvl3(
"MOBF: registering identifier: " .. unique_entity_id
.. " invref \"" .. tostring(trader_inventory) .. "\" for entity \""
.. tostring(entity) .. "\"" )
local trader_formspec = "size[8,10;]" ..
"label[2,0;"..S("Trader %s Inventory"):format(tradername).."]" ..
"label[0,1;"..S("Selling:").."]" ..
"list[detached:" .. unique_entity_id .. ";goods;0,1.5;8,2;]" ..
"label[0,4.0;"..S("Selection").."]" ..
"list[detached:" .. unique_entity_id .. ";selection;0,4.5;1,1;]" ..
"label[1.25,4.75;-->]" ..
"label[2,4.0;"..S("Price").."]" ..
"list[detached:" .. unique_entity_id .. ";price_1;2,4.5;1,1;]" ..
"label[3,4.0;"..S("or").."]" ..
"list[detached:" .. unique_entity_id .. ";price_2;3,4.5;1,1;]" ..
"label[4.25,4.75;-->]" ..
"label[5,4.0;"..S("Pay").."]" ..
"list[detached:" .. unique_entity_id .. ";pay;5,4.5;1,1;]" ..
"label[6.25,4.75;-->]" ..
"label[6.75,4.0;"..S("Takeaway").."]" ..
"list[detached:" .. unique_entity_id .. ";takeaway;7,4.5;1,1;]" ..
"list[current_player;main;0,6;8,4;]"
if mob_inventory.register_formspec("formspec_"
.. unique_entity_id,trader_formspec) == false then
dbg_mobf.trader_inv_lvl1("MOBF: unable to create trader formspec")
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] config_check(entity)
--
--! @brief check if mob is configured as trader
--! @memberof mob_inventory
--! @public
--
--! @param entity mob being checked
--! @return true/false if trader or not
-------------------------------------------------------------------------------
function mob_inventory.config_check(entity)
if entity.data.trader_inventory ~= nil then
return true
end
return false
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] register_formspec(name,formspec)
--
--! @brief check if mob is configured as trader
--! @memberof mob_inventory
--! @public
--
--! @param name name of formspec to register
--! @param formspec formspec definition
--
--! @return true/false if succesfull or not
-------------------------------------------------------------------------------
function mob_inventory.register_formspec(name,formspec)
if mob_inventory.formspecs[name] == nil then
mob_inventory.formspecs[name] = formspec
return true
end
return false
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] trader_callback(entity,player)
--
--! @brief callback handler for inventory by rightclick
--! @memberof mob_inventory
--! @public
--
--! @param entity mob being harvested
--! @param player player harvesting
--
--! @return true/false if handled by harvesting or not
-------------------------------------------------------------------------------
function mob_inventory.trader_callback(entity, player)
local unique_entity_id = string.gsub(tostring(entity),"table: ","")
--local unique_entity_id = "testinv"
local playername = player.get_player_name(player)
if entity.data.sound ~= nil and
entity.data.sound.inventory_open ~= nil then
sound.play(playername, entity.data.sound.inventory_open)
end
if mob_inventory.formspecs["formspec_" .. unique_entity_id] ~= nil then
local pos = entity.object:getpos()
if pos == nil then
dbg_mobf.trader_inv_lvl1("MOBF: unable to get trader pos")
minetest.show_formspec(playername,"")
return
end
--rotate mob to face player
local direction = mobf_get_direction(pos, player:getpos())
if entity.mode == "3d" then
graphics.setyaw(entity,
mobf_calc_yaw(direction.x,direction.z))
else
graphics.setyaw(entity,
mobf_calc_yaw(direction.x,direction.z)+math.pi/2)
end
attention.increase_attention_level(entity,player,10)
if minetest.show_formspec(playername,
"formspec_" .. unique_entity_id,
mob_inventory.formspecs["formspec_" .. unique_entity_id])
== false then
dbg_mobf.trader_inv_lvl1("MOBF: unable to show trader formspec")
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] get_entity(inv)
--
--! @brief find entity linked to inventory
--! @memberof mob_inventory
--! @private
--
--! @param inv name of inventory
-------------------------------------------------------------------------------
function mob_inventory.get_entity(inv)
dbg_mobf.trader_inv_lvl3("MOBF: checking "
.. #mob_inventory.trader_inventories
.. " registred inventorys")
local location = inv.get_location(inv)
if location.type == "detached" then
for i=1,#mob_inventory.trader_inventories,1 do
dbg_mobf.trader_inv_lvl3("MOBF: comparing \""
.. location.name .. "\" to \""
.. mob_inventory.trader_inventories[i].identifier .. "\"")
if mob_inventory.trader_inventories[i].identifier
== location.name then
return mob_inventory.trader_inventories[i].ent_ref
end
end
end
return nil
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] fill_prices(entity,inventory,goodname)
--
--! @brief fill price fields
--! @memberof mob_inventory
--! @private
--
--! @param entity to look for prices
--! @param inventory to set prices
--! @param goodname name of good to set prices for
--! @param count number of elements
-------------------------------------------------------------------------------
function mob_inventory.fill_prices(entity,inventory,goodname,count)
--get price info from entity
local good = nil
for i=1,#entity.data.trader_inventory.goods,1 do
local stackstring = goodname .." " .. count
dbg_mobf.trader_inv_lvl3("MOBF: comparing \"" .. stackstring .. "\"" ..
" to \"" .. entity.data.trader_inventory.goods[i][1] .. "\"")
if entity.data.trader_inventory.goods[i][1] == stackstring then
good = entity.data.trader_inventory.goods[i]
end
end
if good ~= nil then
inventory.set_stack(inventory,"price_1", 1, good[2])
inventory.set_stack(inventory,"price_2", 1, good[3])
else
inventory.set_stack(inventory,"price_1", 1, nil)
inventory.set_stack(inventory,"price_2", 1, nil)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_inventory] add_goods(entity,trader_inventory)
--
--! @brief fill inventory with mobs goods
--! @memberof mob_inventory
--! @private
--
--! @param entity to look for prices
--! @param trader_inventory to put goods
-------------------------------------------------------------------------------
function mob_inventory.add_goods(entity,trader_inventory)
local goods_to_add = nil
if entity.data.trader_inventory.goodlist ~= nil then
local total_sum_chances = 0
for i=1,#entity.data.trader_inventory.goodlist,1 do
total_sum_chances = total_sum_chances +
entity.data.trader_inventory.goodlist[i].chance
end
local rand_value = math.random(0,total_sum_chances)
local selected_index=1
local runvalue = 0
for j=1,#entity.data.trader_inventory.goodlist,1 do
runvalue = runvalue +
entity.data.trader_inventory.goodlist[j].chance
if runvalue < rand_value then
selected_index=j
break;
end
end
goods_to_add =
entity.data.trader_inventory.goodlist[selected_index].goods
else
goods_to_add = entity.data.trader_inventory.goods
end
dbg_mobf.trader_inv_lvl3("MOBF: adding "
.. #goods_to_add
.. " goods for trader")
for i=1,#goods_to_add,1 do
dbg_mobf.trader_inv_lvl3("MOBF:\tadding " ..
goods_to_add[i][1])
trader_inventory.set_stack(trader_inventory,"goods", i,
goods_to_add[i][1])
end
end

View File

@ -0,0 +1,158 @@
-------------------------------------------------------------------------------
-- 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 lifebar.lua
--! @brief mobf_lifebar implementation
--! @copyright Sapier
--! @author Sapier
--! @date 2013-02-14
--
--! @defgroup mobf_lifebar
--! @brief lifebar implements a visible lifebar showing health of a mob abov
--! its head
--! @ingroup framework_int
--! @{
-- Contact: sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mobf_lifebar"))
--! @class mobf_lifebar
--! @brief a simple lifebar implementation
--!@}
mobf_lifebar = {}
-------------------------------------------------------------------------------
-- @function [parent=#mobf_lifebar] init()
--
--! @brief register lifebar entity
--! @memberof mobf_lifebar
-------------------------------------------------------------------------------
function mobf_lifebar.init()
minetest.register_entity(":mobf:lifebar",
{
physical = false,
collisionbox = { 0,0,0,0,0,0 },
visual = "sprite",
textures = { "mobf_lb_64.png" },
visual_size = {x=1,y=0.2},
groups = { immortal=1, },
is_visible = true,
initial_sprite_basepos = {x=0, y=0},
lifetime = 0,
initialized = false,
on_step = function (self,dtime)
self.lifetime = self.lifetime + dtime
if not self.initialized then
if self.lifetime > 1 then
dbg_mobf.lifebar_lvl3("MOBF: lifebar not attached deleting")
self.object:remove()
end
end
--parent will reset lifetime while it's active
if self.lifetime > 5 then
self.object:remove()
end
end
})
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_lifebar] add(entity)
--
--! @brief add a lifebat to an entity
--! @memberof mobf_lifebar
--
--! @param entity entity to add lifebar
--
--! @return reference to lifebar added
-------------------------------------------------------------------------------
function mobf_lifebar.add(entity)
local pos = entity.object:getpos()
local BS = 10
local lifebar_offset = (MAX(entity.collisionbox[4]-entity.collisionbox[1],
entity.collisionbox[6]-entity.collisionbox[3]) / 0.5) * 0.4
lifebar_offset = lifebar_offset * lifebar_offset
pos.y = pos.y + entity.collisionbox[5] + lifebar_offset
local lifebar = minetest.add_entity(pos,"mobf:lifebar")
if lifebar ~= nil then
lifebar:set_attach(entity.object,"",{x=0,y=(entity.collisionbox[5] + 0.1) * BS,z=0},{x=0,y=-90,z=0})
local luaentity = lifebar:get_luaentity()
if luaentity ~= nil then
dbg_mobf.lifebar_lvl3("MOBF: marking lifebar as initialized")
luaentity.initialized = true
else
dbg_mobf.lifebar_lvl3("MOBF: unable to create lifebar entity")
end
end
return lifebar
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_lifebar] del(lifebar)
--
--! @brief delete a lifebar
--! @memberof mobf_lifebar
--
--! @param lifebar lifebar do telete
-------------------------------------------------------------------------------
function mobf_lifebar.del(lifebar)
if lifebar ~= nil then
lifebar:set_detach()
lifebar:remove()
end
end
-------------------------------------------------------------------------------
-- name: set(lifebar,value)
--
--! @brief set value of a lifebar
--! @memberof mobf_lifebar
--! @private
--
--! @param lifebar lifebar do update
--! @param value (0-1) value of lifebar
-------------------------------------------------------------------------------
function mobf_lifebar.set(lifebar,value)
if lifebar ~= nil then
local modifiername = mobf_lifebar.get_imagename(value)
dbg_mobf.lifebar_lvl2("MOBF: got modifier " .. modifiername .. " for value " .. value)
lifebar:settexturemod(modifiername)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_lifebar] get_imagename(value)
--
--! @brief calculate imagename from value
--! @memberof mobf_lifebar
--! @private
--
--! @param value to get image for
-------------------------------------------------------------------------------
function mobf_lifebar.get_imagename(value)
local number = math.floor((value*32) +0.5)
dbg_mobf.lifebar_lvl2("MOBF: calculated number: " .. number )
if number < 5 then
return "^mobf_lb_0" .. number * 2 .. ".png"
else
return "^mobf_lb_" .. number * 2 .. ".png"
end
end

View File

@ -0,0 +1,12 @@
# Translation by Xanthin
Path marker tool = Pfadmarkierer
###Inventory.lua###
Trader %s Inventory = Haendler %s Waren
Selling: = Zu verkaufen:
Selection = Auswahl
Price = Preis
or = oder
Pay = Bezahlen
Takeaway = Wegnehmen

View File

@ -0,0 +1,14 @@
# Spanish translation for Animals Modpack.
# Traducción al español de Animals Modpack.
# Author/Autor: Diego Martínez <kaeza>
Path marker tool = Herramienta de marcado de camino
###Inventory.lua###
Trader %s Inventory = Inventario de Mercader %s
Selling: = Vende:
Selection = Seleccion
Price = Precio
or = o
Pay = Paga
Takeaway = Toma

View File

@ -0,0 +1,12 @@
# Template
Path marker tool =
###Inventory.lua###
Trader %s Inventory =
Selling: =
Selection =
Price =
or =
Pay =
Takeaway =

View File

@ -0,0 +1,451 @@
-------------------------------------------------------------------------------
-- 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 main_flee.lua
--! @brief component containing a movement generator trying to avoid a target
--! @copyright Sapier
--! @author Sapier
--! @date 2013-09-10
--
--! @defgroup mgen_flee MGEN: flee movement generator
--! @brief A movement generator creating movement that trys to avoid a moving
--! target or get away as far as possible from a given point on map
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @class mgen_flee
--! @brief a movement generator trying to get a way from a target
--!@}
mgen_flee = {}
--! @brief movement generator identifier
--! @memberof mgen_flee
mgen_flee.name = "flee_mov_gen"
-------------------------------------------------------------------------------
-- name: callback(entity,now)
--
--! @brief main callback to make a mob flee from a target
--! @memberof mgen_flee
--
--! @param entity mob to generate movement for
--! @param now current time
-------------------------------------------------------------------------------
function mgen_flee.callback(entity,now)
dbg_mobf.flmovement_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 flee_speedup = {x=10,y=2,z=10 }
if entity.data.movement.flee_speedup ~= nil then
if type(entity.data.movement.flee_speedup) == "table" then
flee_speedup = entity.data.movement.flee_speedup
else
flee_speedup.x= entity.data.movement.flee_speedup
flee_speedup.z= entity.data.movement.flee_speedup
end
end
--check max speed limit
mgen_flee.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
environment.evaluate_state(pos_quality,LT_GOOD_FLY_POS)) then
local toset = {
x= basepos.x,
y= basepos.y - 0.5 - entity.collisionbox[2],
z= basepos.z }
--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 + 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
dbg_mobf.flmovement_lvl1("MOBF: fled to wrong place " .. pos_quality.tostring(pos_quality))
if entity.dynamic_data.movement.last_pos_in_env ~= nil then
entity.object:moveto(entity.dynamic_data.movement.last_pos_in_env)
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,
y=basepos.y-1,
z=basepos.z }
,1,entity,true)
end
if newpos == nil then
newpos = environment.get_suitable_pos_same_level( {
x=basepos.x,
y=basepos.y+1,
z=basepos.z }
,1,entity,true)
end
if newpos == nil then
dbg_mobf.flmovement_lvl1("MOBF: no way to fix it removing mob")
spawning.remove(entity,"mgen_flee poscheck")
else
newpos.y = newpos.y - (entity.collisionbox[2] + 0.49)
entity.object:moveto(newpos)
basepos = entity.getbasepos(entity)
end
end
end
else
entity.dynamic_data.movement.invalid_env_count = 0
end
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
local current_accel = entity.object:getacceleration()
if entity.data.movement.canfly then
if current_accel.y ~= 0 then
current_accel.y = 0
entity.object:setacceleration(current_accel)
end
end
if entity.dynamic_data.movement.target ~= nil then
dbg_mobf.flmovement_lvl3("MOBF: Target available")
--calculate distance to target
local targetpos = nil
if entity.dynamic_data.movement.target ~= nil then
dbg_mobf.flmovement_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 then
mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: flee " .. entity.data.name
.. " don't have targetpos "
.. " TGT: " .. dump(entity.dynamic_data.movement.target))
return
end
--TODO need to do something if not line of sight?
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.flmovement_lvl3("MOBF: no line of sight")
--TODO teleport support?
--TODO other ways to handle this?
--return
end
local current_velocity = entity.object:getvelocity()
local predicted_pos =
movement_generic.predict_next_block(basepos,current_velocity,current_accel)
local current_distance = mobf_calc_distance(targetpos,basepos)
local predicted_distance = mobf_calc_distance(targetpos,predicted_pos)
if not (predicted_distance > current_distance) then
local current_rotation_addon = 0
local accel_to_set = nil
local jump_required = false
while accel_to_set == nil and current_rotation_addon < math.pi*(5/16) do
accel_to_set,jump_required = mgen_flee.calc_accel(entity,
targetpos,
basepos,
current_rotation_addon,
current_velocity)
if accel_to_set == nil then
accel_to_set,jump_required = mgen_flee.calc_accel(entity,
targetpos,
basepos,
-current_rotation_addon,
current_velocity)
end
if accel_to_set == nil then
current_rotation_addon = current_rotation_addon + math.pi/8
end
end
if accel_to_set ~= nil then
mgen_flee.set_acceleration(entity,accel_to_set,flee_speedup,basepos)
if jump_required then
local jumpvel = current_velocity
jumpvel.y = 5
entity.object:setvelocity(jumpvel)
end
else
dbg_mobf.flmovement_lvl2("MOBF: didn't find a way to flee, stopping")
end
end
else
--TODO evaluate if this is an error case
end
end
function mgen_flee.calc_accel(entity,targetpos,basepos,rot_addon,cur_vel)
local dirxz,dirxy = mobf_calc_direction(targetpos,basepos)
local dirxz = dirxz + rot_addon
local accel_to_set = mobf_calc_vector_components(dirxz,entity.data.movement.max_speed)
local yaccel = environment.get_default_gravity(basepos,
entity.environment.media,
entity.data.movement.canfly)
accel_to_set.y = yaccel
local predicted_pos =
movement_generic.predict_next_block(basepos,cur_vel,accel_to_set)
local pos_quality = environment.pos_quality(predicted_pos,entity)
if pos_quality.media_quality == MQ_IN_MEDIA and
pos_quality.surface_quality_min > SQ_WATER then
return accel_to_set,false
end
predicted_pos.y = predicted_pos.y+1
pos_quality = environment.pos_quality(predicted_pos,entity)
if pos_quality.media_quality == MQ_IN_MEDIA and
pos_quality.surface_quality_min > SQ_WATER then
return accel_to_set,true
end
return nil,false
end
-------------------------------------------------------------------------------
-- name: next_block_ok()
--
--! @brief check quality of next block
--! @memberof mgen_flee
--! @public
-------------------------------------------------------------------------------
function mgen_flee.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.level_quality == LQ_OK) and
(
(quality.surface_quality_min == Q_UNKNOWN) or
(quality.surface_quality_min >= SQ_WRONG)
)
)
end
-------------------------------------------------------------------------------
-- name: initialize()
--
--! @brief initialize movement generator
--! @memberof mgen_flee
--! @public
-------------------------------------------------------------------------------
function mgen_flee.initialize(entity,now)
--intentionally empty
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by movement generator
--! @memberof mgen_flee
--! @public
--
--! @param entity mob to initialize dynamic data
--! @param now current time
-------------------------------------------------------------------------------
function mgen_flee.init_dynamic_data(entity,now)
local pos = entity.object:getpos()
local data = {
target = nil,
invalid_env_count = 0,
}
entity.dynamic_data.movement = data
end
-------------------------------------------------------------------------------
-- name: checkspeed(entity)
--
--! @brief check if mobs speed is within it's limits and correct if necessary
--! @memberof mgen_flee
--! @private
--
--! @param entity mob to initialize dynamic data
-------------------------------------------------------------------------------
function mgen_flee.checkspeed(entity)
local current_velocity = entity.object:getvelocity()
local xzspeed =
mobf_calc_scalar_speed(current_velocity.x,current_velocity.z)
if (xzspeed > (entity.data.movement.max_speed * 10)) 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)
return true
end
return false
end
-------------------------------------------------------------------------------
-- name: set_acceleration(entity,accel,speedup)
--
--! @brief apply acceleration to entity
--! @memberof mgen_flee
--! @private
--
--! @param entity mob to apply to
--! @param accel acceleration to set
--! @param speedup speedup factor
--! @param pos current position
-------------------------------------------------------------------------------
function mgen_flee.set_acceleration(entity,accel,speedup,pos)
mobf_assert_backtrace(entity ~= nil)
mobf_assert_backtrace(accel ~= nil)
mobf_assert_backtrace(speedup ~= nil)
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
if mgen_flee.next_block_ok(entity,pos,accel) then
dbg_mobf.flmovement_lvl3("MOBF: flee setting acceleration to: " .. printpos(accel));
entity.object:setacceleration(accel)
return true
elseif mgen_flee.next_block_ok(entity,pos,{x=0,y=0,z=0}) then
accel = {x=0,y=0,z=0}
dbg_mobf.flmovement_lvl3("MOBF: flee setting acceleration to: " .. printpos(accel));
entity.object:setacceleration(accel)
return true
else
local current_velocity = entity.object:getvelocity()
current_velocity.y = 0
if mgen_flee.next_block_ok(entity,pos,{x=0,y=0,z=0},current_velocity) then
accel = {x=0,y=0,z=0}
entity.object:setvelocity(current_velocity)
entity.object:setacceleration(accel)
return true
end
end
dbg_mobf.flmovement_lvl1(
"MOBF: \t acceleration " .. printpos(accel) ..
" would result in invalid position not applying!")
return false
end
-------------------------------------------------------------------------------
-- name: set_target(entity, target, follow_speedup, max_distance)
--
--! @brief set target for movgen
--! @memberof mgen_flee
--
--! @param entity mob to apply to
--! @param target to set
--! @param follow_speedup --unused here
--! @param max_distance --unused here
-------------------------------------------------------------------------------
function mgen_flee.set_target(entity, target, follow_speedup, max_distance)
mobf_assert_backtrace(entity ~= nil)
mobf_assert_backtrace(target ~= nil)
entity.dynamic_data.movement.target = target
return true
end
--register this movement generator
registerMovementGen(mgen_flee.name,mgen_flee)

View File

@ -0,0 +1,676 @@
-------------------------------------------------------------------------------
-- 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 main_follow.lua
--! @brief component containing a targeted movement generator
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup mgen_follow MGEN: follow movement generator
--! @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
-------------------------------------------------------------------------------
--! @class mgen_follow
--! @brief a movement generator trying to follow or reach a target
--!@}
mgen_follow = {}
--! @brief movement generator identifier
--! @memberof mgen_follow
mgen_follow.name = "follow_mov_gen"
-------------------------------------------------------------------------------
-- name: identify_movement_state(ownpos,targetpos)
--
--! @brief check what situation we are
--! @memberof mgen_follow
--! @private
--
--! @param ownpos position of entity
--! @param targetpos position of target
--!
--! @return "below_los"
--! "below_no_los"
--! "same_height_los"
--! "same_height_no_los"
--! "above_los"
--! "above_no_los"
--! "unknown"
-------------------------------------------------------------------------------
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"
else
return "below_no_los"
end
end
if ownpos.y > targetpos.y then
if los then
return "above_los"
else
return "above_no_los"
end
end
return "unknown"
end
-------------------------------------------------------------------------------
-- name: handleteleport(entity,now)
--
--! @brief handle teleportsupport
--! @memberof mgen_follow
--! @private
--
--! @param entity mob to check for teleport
--! @param now current time
--! @param targetpos position of target
--!
--! @return true/false finish processing
-------------------------------------------------------------------------------
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 ..
" 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
while (not environment.possible_pos(entity,{
x=targetpos.x,
y=targetpos.y + current_offset,
z=targetpos.z
})) and
current_offset < maxoffset do
dbg_mobf.fmovement_lvl2(
"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)
end
entity.object:setvelocity({x=0,y=0,z=0})
entity.object:setacceleration({x=0,y=0,z=0})
entity.object:moveto(targetpos)
entity.dynamic_data.movement.last_next_to_target = now
return true
end
end
return false
end
-------------------------------------------------------------------------------
-- name: callback(entity,now)
--
--! @brief main callback to make a mob follow its target
--! @memberof mgen_follow
--
--! @param entity mob to generate movement for
--! @param now current time
-------------------------------------------------------------------------------
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
else
follow_speedup.x= entity.data.movement.follow_speedup
follow_speedup.z= entity.data.movement.follow_speedup
end
end
--if speedup is disabled reset
if not entity.dynamic_data.movement.follow_speedup then
follow_speedup = { x=1, y=1, z=1}
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
environment.evaluate_state(pos_quality,LT_GOOD_FLY_POS)) then
local toset = {
x= basepos.x,
y= basepos.y - 0.5 - entity.collisionbox[2],
z= basepos.z }
--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 + 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
dbg_mobf.fmovement_lvl1("MOBF: followed to wrong place " .. pos_quality.tostring(pos_quality))
if entity.dynamic_data.movement.last_pos_in_env ~= nil then
entity.object:moveto(entity.dynamic_data.movement.last_pos_in_env)
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,
y=basepos.y-1,
z=basepos.z }
,1,entity,true)
end
if newpos == nil then
newpos = environment.get_suitable_pos_same_level( {
x=basepos.x,
y=basepos.y+1,
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")
else
newpos.y = newpos.y - (entity.collisionbox[2] + 0.49)
entity.object:moveto(newpos)
basepos = entity.getbasepos(entity)
end
end
end
else
entity.dynamic_data.movement.invalid_env_count = 0
end
local current_accel = entity.object:getacceleration()
if pos_quality.level_quality ~= LQ_OK and
entity.data.movement.canfly then
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
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
end
if targetpos == nil then
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 = nil
local height_distance = nil
if entity.data.movement.canfly then
--real pos is relevant not basepos for flying mobs
--target for flying mobs is always slightly above it's target
distance = mobf_calc_distance(entity.object:getpos(),
{x=targetpos.x, y=(targetpos.y+1), z=targetpos.z })
height_distance = entity.object:getpos().y - (targetpos.y+1)
else
distance = mobf_calc_distance_2d(basepos,targetpos)
end
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
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 : "
.. 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
(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 =
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
current_state == "same_height_no_los" ) then
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 =
mgen_follow.set_acceleration(entity,
accel_to_set,
follow_speedup,
basepos)
else
local current_velocity = entity.object:getvelocity()
local predicted_pos =
movement_generic.predict_next_block(basepos,
current_velocity,
accel_to_set)
--TODO replace by quality based mechanism!!!!!!!------------
local pos_state =
environment.pos_is_ok(predicted_pos,entity)
if pos_state == "collision_jumpable" then
local pos_to_set = entity.object:getpos()
pos_to_set.y = pos_to_set.y + 1.1
entity.object:moveto(pos_to_set)
basepos.y=basepos.y+1.1
end
------------------------------------------------------------
dbg_mobf.fmovement_lvl3("MOBF: setting acceleration to: "
.. printpos(accel_to_set) .. " predicted_state: "
.. pos_state);
handled =
mgen_follow.set_acceleration(entity,
accel_to_set,
follow_speedup,
basepos)
end
end
if handled == false then
dbg_mobf.fmovement_lvl1(
"MOBF: \t Unexpected or unhandled movement state: "
.. current_state)
local yaccel = environment.get_default_gravity(basepos,
entity.environment.media,
entity.data.movement.canfly)
--entity.object:setvelocity({x=0,y=0,z=0})
entity.object:setacceleration({x=0,y=yaccel,z=0})
end
mgen_follow.update_animation(entity, "following")
--nothing to do
elseif height_distance ~= nil and math.abs(height_distance) > 0.1 then
mgen_follow.set_acceleration(entity,
{ x=0,y=(height_distance*-0.2),z=0},
follow_speedup,
basepos)
mgen_follow.update_animation(entity, "following")
--we're next to target stop movement
else
local yaccel = environment.get_default_gravity(basepos,
entity.environment.media,
entity.data.movement.canfly)
if entity.dynamic_data.movement.was_moving_last_step == true or
current_accel.Y ~= yaccel then
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
mgen_follow.update_animation(entity, "ntt")
end
end
else
--TODO evaluate if this is an error case
end
end
-------------------------------------------------------------------------------
-- name: update_animation()
--
--! @brief update animation according to the follow movegen substate
--! @memberof mgen_follow
--! @public
-------------------------------------------------------------------------------
function mgen_follow.update_animation(entity, anim_state)
-- no need to change
if anim_state == entity.dynamic_data.movement.anim_selected then
return
end
-- check if there's a animation specified for stand in this state
local statename, state = entity:get_state()
if anim_state == "following" then
entity.dynamic_data.movement.anim_selected = "following"
if state.animation_walk ~= nil then
graphics.set_animation(entity, state.animation_walk)
elseif state.animation ~= nil then
graphics.set_animation(entity, state.animation)
end
elseif anim_state == "ntt" then
entity.dynamic_data.movement.anim_selected = "ntt"
if state.animation_next_to_target ~= nil then
graphics.set_animation(entity, state.animation_next_to_target)
end
end
end
-------------------------------------------------------------------------------
-- name: next_block_ok()
--
--! @brief check quality of next block
--! @memberof mgen_follow
--! @public
-------------------------------------------------------------------------------
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.level_quality == LQ_OK) and
(
(quality.surface_quality_min == Q_UNKNOWN) or
(quality.surface_quality_min >= SQ_WRONG)
)
)
end
-------------------------------------------------------------------------------
-- name: initialize()
--
--! @brief initialize movement generator
--! @memberof mgen_follow
--! @public
-------------------------------------------------------------------------------
function mgen_follow.initialize(entity,now)
--intentionally empty
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by movement generator
--! @memberof mgen_follow
--! @public
--
--! @param entity mob to initialize dynamic data
--! @param now current time
-------------------------------------------------------------------------------
function mgen_follow.init_dynamic_data(entity,now)
local pos = entity.object:getpos()
local data = {
target = nil,
guardspawnpoint = false,
max_distance = entity.data.movement.max_distance,
invalid_env_count = 0,
follow_speedup = true,
}
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
-------------------------------------------------------------------------------
-- name: checkspeed(entity)
--
--! @brief check if mobs speed is within it's limits and correct if necessary
--! @memberof mgen_follow
--! @private
--
--! @param entity mob to initialize dynamic data
-------------------------------------------------------------------------------
function mgen_follow.checkspeed(entity)
local current_velocity = entity.object:getvelocity()
local xzspeed =
mobf_calc_scalar_speed(current_velocity.x,current_velocity.z)
if (xzspeed > entity.data.movement.max_speed) then
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
-------------------------------------------------------------------------------
-- name: set_acceleration(entity,accel,speedup)
--
--! @brief apply acceleration to entity
--! @memberof mgen_follow
--! @private
--
--! @param entity mob to apply to
--! @param accel acceleration to set
--! @param speedup speedup factor
--! @param pos current position
-------------------------------------------------------------------------------
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
if mgen_follow.next_block_ok(entity,pos,accel) then
dbg_mobf.fmovement_lvl3("MOBF: setting acceleration to: " .. printpos(accel));
entity.object:setacceleration(accel)
return true
elseif mgen_follow.next_block_ok(entity,pos,{x=0,y=0,z=0}) then
accel = {x=0,y=0,z=0}
dbg_mobf.fmovement_lvl3("MOBF: setting acceleration to: " .. printpos(accel));
entity.object:setacceleration(accel)
return true
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 = {x=0,y=0,z=0}
entity.object:setvelocity(current_velocity)
entity.object:setacceleration(accel)
return true
end
end
dbg_mobf.fmovement_lvl1(
"MOBF: \t acceleration " .. printpos(accel) ..
" would result in invalid position not applying!")
return false
end
-------------------------------------------------------------------------------
-- name: set_target(entity, target, follow_speedup, max_distance)
--
--! @brief set target for movgen
--! @memberof mgen_follow
--
--! @param entity mob to apply to
--! @param target to set
--! @param follow_speedup --unused here
--! @param max_distance maximum distance to target to be tried to reach
-------------------------------------------------------------------------------
function mgen_follow.set_target(entity,target, follow_speedup, max_distance)
entity.dynamic_data.movement.target = target
entity.dynamic_data.movement.max_distance = max_distance
return true
end
--register this movement generator
registerMovementGen(mgen_follow.name,mgen_follow)

View File

@ -0,0 +1,158 @@
-------------------------------------------------------------------------------
-- 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 main_follow.lua
--! @brief component containing a movement generator based uppon jordan4ibanez code
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup mgen_jordan4ibanez MGEN: a velocity based movement generator
--! @brief A movement generator creating simple random movement
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @class mgen_jordan4ibanez
--! @brief a movement generator trying to follow or reach a target
--!@}
mgen_jordan4ibanez = {}
--! @brief chillaxin_speed
--! @memberof mgen_jordan4ibanez
mgen_jordan4ibanez.chillaxin_speed = 0.1
--! @brief movement generator identifier
--! @memberof mgen_jordan4ibanez
mgen_jordan4ibanez.name = "jordan4ibanez_mov_gen"
-------------------------------------------------------------------------------
-- name: callback(entity,now)
--
--! @brief main callback to make a mob follow its target
--! @memberof mgen_jordan4ibanez
--
--! @param entity mob to generate movement for
--! @param now current time
-------------------------------------------------------------------------------
function mgen_jordan4ibanez.callback(entity,now)
--update timers
entity.dynamic_data.movement.timer = entity.dynamic_data.movement.timer + 0.01
entity.dynamic_data.movement.turn_timer = entity.dynamic_data.movement.turn_timer + 0.01
entity.dynamic_data.movement.jump_timer = entity.dynamic_data.movement.jump_timer + 0.01
entity.dynamic_data.movement.door_timer = entity.dynamic_data.movement.door_timer + 0.01
if entity.dynamic_data.movement.direction ~= nil then
entity.object:setvelocity({x=entity.dynamic_data.movement.direction.x*mgen_jordan4ibanez.chillaxin_speed,
y=entity.object:getvelocity().y,
z=entity.dynamic_data.movement.direction.z*mgen_jordan4ibanez.chillaxin_speed})
end
if entity.dynamic_data.movement.turn_timer > math.random(1,4) then
entity.dynamic_data.movement.yaw = 360 * math.random()
graphics.setyaw(entity, entity.dynamic_data.movement.yaw)
entity.dynamic_data.movement.turn_timer = 0
entity.dynamic_data.movement.direction = {x = math.sin(entity.dynamic_data.movement.yaw)*-1,
y = -10,
z = math.cos(entity.dynamic_data.movement.yaw)}
--entity.object:setvelocity({x=entity.dynamic_data.movement.direction.x,y=entity.object:getvelocity().y,z=entity.dynamic_data.movement.direction.z})
--entity.object:setacceleration(entity.dynamic_data.movement.direction)
end
--TODO update animation
--open a door [alpha]
if entity.dynamic_data.movement.direction ~= nil then
if entity.dynamic_data.movement.door_timer > 2 then
local is_a_door = minetest.get_node({x=entity.object:getpos().x + entity.dynamic_data.movement.direction.x,
y=entity.object:getpos().y,z=entity.object:getpos().
z + entity.dynamic_data.movement.direction.z}).name
if is_a_door == "doors:door_wood_t_1" then
minetest.punch_node({x=entity.object:getpos().x + entity.dynamic_data.movement.direction.x,
y=entity.object:getpos().y-1,
z=entity.object:getpos().z + entity.dynamic_data.movement.direction.z})
entity.dynamic_data.movement.door_timer = 0
end
local is_in_door = minetest.get_node(entity.object:getpos()).name
if is_in_door == "doors:door_wood_t_1" then
minetest.punch_node(entity.object:getpos())
end
end
end
--jump
if entity.dynamic_data.movement.direction ~= nil then
if entity.dynamic_data.movement.jump_timer > 0.3 then
if minetest.registered_nodes[minetest.get_node({x=entity.object:getpos().x + entity.dynamic_data.movement.direction.x,
y=entity.object:getpos().y-1,
z=entity.object:getpos().z + entity.dynamic_data.movement.direction.z}).name].walkable then
entity.object:setvelocity({x=entity.object:getvelocity().x,y=5,z=entity.object:getvelocity().z})
entity.dynamic_data.movement.jump_timer = 0
end
end
end
end
-------------------------------------------------------------------------------
-- name: initialize()
--
--! @brief initialize movement generator
--! @memberof mgen_jordan4ibanez
--! @public
-------------------------------------------------------------------------------
function mgen_jordan4ibanez.initialize(entity,now)
--intentionaly doing nothing this function is for symmetry reasons only
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by movement generator
--! @memberof mgen_jordan4ibanez
--! @public
--
--! @param entity mob to initialize dynamic data
--! @param now current time
-------------------------------------------------------------------------------
function mgen_jordan4ibanez.init_dynamic_data(entity,now)
local data = {
timer = 0,
turn_timer = 0,
jump_timer = 0,
door_timer = 0,
direction = nil,
yaw = nil,
}
entity.dynamic_data.movement = data
end
-------------------------------------------------------------------------------
-- name: set_target(entity, target, follow_speedup, max_distance)
--
--! @brief set target for movgen
--! @memberof mgen_jordan4ibanez
--
--! @param entity mob to apply to --unused here
--! @param target to set --unused here
--! @param follow_speedup --unused here
--! @param max_distance --unused here
-------------------------------------------------------------------------------
function mgen_jordan4ibanez.set_target(entity,target)
return false
end
--register this movement generator
registerMovementGen(mgen_jordan4ibanez.name,mgen_jordan4ibanez)

View File

@ -0,0 +1,359 @@
-------------------------------------------------------------------------------
-- 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 path_based_movement_gen.lua
--! @brief component containing a path based movement generator
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup mgen_path_based MGEN: Path based movement generator
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @class p_mov_gen
--! @brief a movement generator evaluating a path to a target and following it
--!@}
p_mov_gen = {}
p_mov_gen.max_waypoint_distance = 0.5
--! @brief movement generator identifier
--! @memberof p_mov_gen
p_mov_gen.name = "mgen_path"
-------------------------------------------------------------------------------
-- name: callback(entity,now)
--
--! @brief path based movement generator callback
--! @memberof p_mov_gen
--
-- param1: mob to do movement
-- param2: current time
-- retval: -
-------------------------------------------------------------------------------
function p_mov_gen.callback(entity,now,dstep)
mobf_assert_backtrace(entity ~= nil)
mobf_assert_backtrace(entity.dynamic_data ~= nil)
mobf_assert_backtrace(entity.dynamic_data.p_movement ~= nil)
if entity.dynamic_data.p_movement.eta ~= nil then
if now < entity.dynamic_data.p_movement.eta then
return
end
end
local current_pos = entity.object:getpos()
local handled = false
if entity.dynamic_data.p_movement.path == nil then
dbg_mobf.path_mov_lvl1(
"MOBF: path movement but mo path set!!")
return
end
local max_distance = p_mov_gen.max_waypoint_distance
mobf_assert_backtrace(entity.dynamic_data.p_movement.next_path_index ~= nil)
mobf_assert_backtrace(max_distance ~= nil)
--check if target is reached
if p_mov_gen.distance_to_next_point(entity,current_pos)
< max_distance then
dbg_mobf.path_mov_lvl1("MOBF: pathmov next to next point switching target")
local update_target = true
if entity.dynamic_data.p_movement.waypoint_stop then
entity.object:setvelocity({x=0,y=0,z=0})
end
--return to begining of path
if entity.dynamic_data.p_movement.next_path_index
== #entity.dynamic_data.p_movement.path then
if entity.dynamic_data.p_movement.cycle_path or
(entity.dynamic_data.p_movement.cycle_path == nil and
entity.data.patrol ~= nil and
entity.data.patrol.cycle_path) then
--0 is correct as it's incremented by one later
entity.dynamic_data.p_movement.next_path_index = 0
else
if entity.dynamic_data.p_movement.HANDLER_end_of_path ~= nil
and type(entity.dynamic_data.p_movement.HANDLER_end_of_path) == "function" then
entity.dynamic_data.p_movement.HANDLER_end_of_path(entity)
end
dbg_mobf.path_mov_lvl1("MOBF: cycle not set not updating point")
update_target = false
handled = true
end
end
if update_target then
mobf_assert_backtrace(entity.dynamic_data.p_movement.path ~= nil)
entity.dynamic_data.p_movement.next_path_index =
entity.dynamic_data.p_movement.next_path_index + 1
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path
[entity.dynamic_data.p_movement.next_path_index]
dbg_mobf.path_mov_lvl1("MOBF: (1) setting new target to index: " ..
entity.dynamic_data.p_movement.next_path_index .. " pos: " ..
printpos(entity.dynamic_data.movement.target))
handled = true
end
end
if not handled and
entity.dynamic_data.movement.target == nil then
mobf_assert_backtrace(entity.dynamic_data.p_movement.path ~= nil)
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path
[entity.dynamic_data.p_movement.next_path_index]
dbg_mobf.path_mov_lvl1("MOBF: (2) setting new target to index: " ..
entity.dynamic_data.p_movement.next_path_index .. " pos: " ..
printpos(entity.dynamic_data.movement.target))
end
mgen_follow.callback(entity,now)
end
-------------------------------------------------------------------------------
-- name: distance_to_next_point(entity)
--
--! @brief get distance to next target point (2d only)
--! @memberof p_mov_gen
--! @private
--
--! @param entity mob to check
--! @param current_pos position mob is atm
--
--! @retval distance
-------------------------------------------------------------------------------
function p_mov_gen.distance_to_next_point(entity,current_pos)
local index = entity.dynamic_data.p_movement.next_path_index
mobf_assert_backtrace(entity.dynamic_data.p_movement.path ~= nil)
mobf_assert_backtrace(index <= #entity.dynamic_data.p_movement.path)
return mobf_calc_distance_2d(current_pos,
entity.dynamic_data.p_movement.path[index])
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by movement generator
--! @memberof p_mov_gen
--
--! @param entity to initialize
--! @param now current time
--! @param restored_data data restored on activate
-------------------------------------------------------------------------------
function p_mov_gen.init_dynamic_data(entity,now,restored_data)
local pos = entity.object:getpos()
local data = {
path = nil,
eta = nil,
last_move_stop = now,
next_path_index = 1,
force_target = nil,
pathowner = nil,
pathname = nil,
waypoint_stop = true,
}
if restored_data ~= nil and
type(restored_data) == "table" then
dbg_mobf.path_mov_lvl3(
"MOBF: path movement reading stored data: " .. dump(restored_data))
if restored_data.pathowner ~= nil and
restored_data.pathname ~= nil then
data.pathowner = restored_data.pathowner
data.pathname = restored_data.pathname
data.path = mobf_path.getpoints(data.pathowner,data.pathname)
dbg_mobf.path_mov_lvl3(
"MOBF: path movement restored points: " .. dump(data.path))
end
if restored_data.pathindex ~= nil and
type(restored_data.pathindex) == "number" and
restored_data.pathindex > 0 and
data.path ~= nil and
restored_data.pathindex < #data.path then
data.next_path_index = restored_data.pathindex
end
end
entity.dynamic_data.p_movement = data
mgen_follow.init_dynamic_data(entity,now)
entity.dynamic_data.movement.follow_speedup = false
end
-------------------------------------------------------------------------------
-- name: set_path(entity,path)
--
--! @brief set target for movgen
--! @memberof p_mov_gen
--! @public
--
--! @param entity mob to apply to
--! @param path to set
--! @param enable_speedup shall follow speedup be applied to path movement?
-------------------------------------------------------------------------------
function p_mov_gen.set_path(entity,path, enable_speedup)
mobf_assert_backtrace(entity.dynamic_data.p_movement ~= nil)
if path ~= nil then
entity.dynamic_data.p_movement.next_path_index = 1
entity.dynamic_data.movement.max_distance =
p_mov_gen.max_waypoint_distance
entity.dynamic_data.p_movement.path = path
--a valid path has at least 2 positions
mobf_assert_backtrace(#entity.dynamic_data.p_movement.path > 1)
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path[2]
entity.dynamic_data.movement.follow_speedup = enable_speedup
return true
else
entity.dynamic_data.p_movement.next_path_index = nil
entity.dynamic_data.movement.max_distance = nil
entity.dynamic_data.p_movement.path = nil
entity.dynamic_data.movement.target = nil
entity.dynamic_data.movement.follow_speedup = nil
return false
end
end
-------------------------------------------------------------------------------
-- name: set_cycle_path(entity,value)
--
--! @brief set state of path cycle mechanism
--! @memberof p_mov_gen
--! @public
--
--! @param entity mob to apply to
--! @param value to set true/false/nil(mob global default)
-------------------------------------------------------------------------------
function p_mov_gen.set_cycle_path(entity,value)
mobf_assert_backtrace(entity.dynamic_data.p_movement ~= nil)
entity.dynamic_data.p_movement.cycle_path = value
end
-------------------------------------------------------------------------------
-- name: set_end_of_path_handler(entity,handler)
--
--! @brief set handler to call for non cyclic paths if final target is reached
--! @memberof p_mov_gen
--! @public
--
--! @param entity mob to apply to
--! @param handler to call at final target
-------------------------------------------------------------------------------
function p_mov_gen.set_end_of_path_handler(entity,handler)
entity.dynamic_data.p_movement.HANDLER_end_of_path = handler
end
-------------------------------------------------------------------------------
-- name: set_target(entity, target, follow_speedup, max_distance)
--
--! @brief set target for movgen
--! @memberof p_mov_gen
--! @public
--
--! @param entity mob to apply to
--! @param target to set
--! @param follow_speedup use follow speedup to reach target
--! @param max_distance --unused here
-------------------------------------------------------------------------------
function p_mov_gen.set_target(entity, target, follow_speedup, max_distance)
mobf_assert_backtrace(target ~= nil)
local current_pos = entity.getbasepos(entity)
local targetpos = nil
if not mobf_is_pos(target) then
if target:is_player() then
targetpos = target:getpos()
targetpos.y = targetpos.y +0.5
else
if type(target.getbasepos) == "function" then
targetpos = target.getbasepos(target)
else
targetpos = target:getpos()
end
end
else
targetpos = target
end
if targetpos == nil then
return false
end
if entity.dynamic_data.p_movement.lasttargetpos ~= nil then
if mobf_pos_is_same(entity.dynamic_data.p_movement.lasttargetpos,
targetpos) then
return true
end
end
entity.dynamic_data.p_movement.lasttargetpos = targetpos
entity.dynamic_data.p_movement.path = nil
entity.dynamic_data.p_movement.next_path_index = 1
--on target mode max distance is always 0.5
entity.dynamic_data.movement.max_distance = p_mov_gen.max_waypoint_distance
--try to find path on our own
if not mobf_get_world_setting("mobf_disable_pathfinding") then
entity.dynamic_data.p_movement.path =
mobf_path.find_path(current_pos,targetpos,5,1,1,nil)
else
entity.dynamic_data.p_movement.path = nil
end
if entity.dynamic_data.p_movement.path ~= nil then
--a valid path has at least 2 positions
mobf_assert_backtrace(#entity.dynamic_data.p_movement.path > 1)
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path[2]
entity.dynamic_data.movement.follow_speedup = follow_speedup
return true
end
if entity.dynamic_data.p_movement.path == nil then
minetest.log(LOGLEVEL_INFO,
"MOBF: no pathfinding support/ no path found directly setting targetpos as path")
entity.dynamic_data.p_movement.path = {}
table.insert(entity.dynamic_data.p_movement.path,targetpos)
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path[1]
entity.dynamic_data.movement.follow_speedup = follow_speedup
return true
end
return false
end
--register this movement generator
registerMovementGen(p_mov_gen.name,p_mov_gen)

View File

@ -0,0 +1,608 @@
-------------------------------------------------------------------------------
-- 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 direction_control.lua
--! @brief functions for direction control in probabilistic movement gen
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @ingroup mgen_probab
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @class direction_control
--! @brief functions for direction control in probabilistic movement gen
direction_control = {}
--!@}
-------------------------------------------------------------------------------
-- name: changeaccel(pos,entity,velocity)
--
--! @brief find a suitable new acceleration for mob
--! @memberof direction_control
--! @private
--
--! @param pos current position
--! @param entity mob to get acceleration for
--! @param current_velocity current velocity
--! @return {{ x/y/z accel} + jump flag really?
-------------------------------------------------------------------------------
function direction_control.changeaccel(pos,entity,current_velocity)
local maxtries = 5
local old_quality = environment.pos_quality(pos,entity)
local new_accel =
direction_control.get_random_acceleration(
entity.data.movement.min_accel,
entity.data.movement.max_accel,graphics.getyaw(entity),0)
local pos_predicted =
movement_generic.predict_next_block(pos,current_velocity,new_accel)
local new_quality = environment.pos_quality(pos_predicted,entity)
local prefered_state =
environment.evaluate_state( new_quality,
old_quality,
MQ_IN_MEDIA,
nil,
GQ_FULL,
SQ_POSSIBLE,
SQ_OK)
while not prefered_state do
dbg_mobf.pmovement_lvl1("MOBF: predicted pos " .. printpos(pos_predicted)
.. " isn't perfect " .. maxtries .. " tries left, state: "
.. new_quality.tostring(new_quality))
--don't loop forever get to save mode and try next time
if maxtries <= 0 then
dbg_mobf.pmovement_lvl1(
"MOBF: Aborting acceleration finding for this cycle due to max retries")
if state == "collision_jumpable" then
dbg_mobf.movement_lvl1("Returning "
..printpos(new_accel).." as new accel as mob may jump")
return new_accel
end
dbg_mobf.pmovement_lvl1(
"MOBF: Didn't find a suitable acceleration stopping movement: "
.. entity.data.name .. printpos(pos))
entity.object:setvelocity({x=0,y=0,z=0})
entity.dynamic_data.movement.started = false
--don't slow down mob
return { x=0,
y=0,
z=0 }
end
local probab = math.random()
--accept possible surface in rare cases
if probab < 0.3 then
local acceptable_state =
environment.evaluate_state(new_quality,
nil,
MQ_IN_MEDIA,
GQ_PARTIAL,
nil,
SQ_WRONG,
SQ_POSSIBLE)
if acceptable_state then
return new_accel
end
end
--accept possible surface in rare cases
if probab < 0.2 then
local acceptable_state =
environment.evaluate_state(new_quality,
nil,
MQ_IN_MEDIA,
nil,
GQ_FULL,
SQ_WRONG,
SQ_POSSIBLE)
if acceptable_state then
return new_accel
end
end
--try another acceleration
new_accel =
direction_control.get_random_acceleration(
entity.data.movement.min_accel,
entity.data.movement.max_accel,
graphics.getyaw(entity),1.57)
pos_predicted =
movement_generic.predict_next_block(pos,current_velocity,new_accel)
local prefered_state =
environment.evaluate_state( new_quality,
old_quality,
MQ_IN_MEDIA,
nil,
GQ_FULL,
SQ_POSSIBLE,
SQ_OK)
maxtries = maxtries -1
end
return new_accel
end
-------------------------------------------------------------------------------
-- name: get_random_acceleration(minaccel,maxaccel,current_yaw, minrotation)
--
--! @brief get a random x/z acceleration within a specified acceleration range
--! @memberof direction_control
--! @private
--
--! @param minaccel minimum acceleration to use
--! @param maxaccel maximum acceleration
--! @param current_yaw current orientation of mob
--! @param minrotation minimum rotation to perform
--! @return x/y/z acceleration
-------------------------------------------------------------------------------
function direction_control.get_random_acceleration(
minaccel,maxaccel,current_yaw, minrotation)
local direction = 1
if math.random() < 0.5 then
direction = -1
end
--calc random absolute value
local rand_accel = (math.random() * (maxaccel - minaccel)) + minaccel
local orientation_delta = mobf_gauss(math.pi/6,1/2)
--calculate new acceleration
local new_direction =
current_yaw + ((minrotation + orientation_delta) * direction)
local new_accel = mobf_calc_vector_components(new_direction,rand_accel)
dbg_mobf.pmovement_lvl3(" new direction: " .. new_direction ..
" old direction: " .. current_yaw ..
" new accel: " .. printpos(new_accel) ..
" orientation_delta: " .. orientation_delta)
return new_accel
end
-------------------------------------------------------------------------------
-- 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
--! @memberof direction_control
--
--! @param entity mob to generate movement
--! @param movement_state current state of movement
--! @param pos_predicted position mob will be next
--! @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_quality)
if movement_state.changed then
--someone already changed something
return
end
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
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
if (pos_predicted_quality.geometry_quality == GQ_NONE) then
dbg_mobf.pmovement_lvl2("MOBF: mob " .. entity.data.name .. " is dropping")
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_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: trying to redirect to safe position .. " .. printpos(new_pos))
local speedfactor = 0.1
local speed_found = false
repeat
movement_state.accel_to_set =
movement_generic.get_accel_to(new_pos, entity, nil,
entity.data.movement.max_accel*speedfactor)
local next_pos =
movement_generic.predict_next_block(
movement_state.basepos,
movement_state.current_velocity,
movement_state.accel_to_set)
local next_quality = environment.pos_quality(
next_pos,
entity
)
if environment.evaluate_state(next_quality,
LT_EDGE_POS_POSSIBLE_CENTER) then
speed_found = true
end
speedfactor = speedfactor +0.1
until ( speedfactor > 1 or speed_found)
-- try if our state would at least keep same if we walk towards
-- the good pos
if not speed_found then
dbg_mobf.pmovement_lvl2("MOBF: trying min speed towards good pos")
movement_state.accel_to_set =
movement_generic.get_accel_to(new_pos, entity, nil,
entity.data.movement.min_accel)
local next_pos =
movement_generic.predict_next_block(
movement_state.basepos,
movement_state.current_velocity,
movement_state.accel_to_set)
local next_quality = environment.pos_quality(
next_pos,
entity
)
if ((mobf_calc_distance(next_pos,new_pos) <
(mobf_calc_distance(movement_state.basepos,new_pos))) and
environment.evaluate_state(next_quality,
LT_DROP_PENDING)) then
speed_found = true
end
end
if speed_found then
dbg_mobf.pmovement_lvl2("MOBF: redirecting to safe position .. "
.. printpos(new_pos))
movement_state.changed = true
return
end
end
if new_pos == nil then
dbg_mobf.pmovement_lvl2("MOBF: no suitable pos found")
else
dbg_mobf.pmovement_lvl2("MOBF: didn't find a way to suitable pos")
end
--no suitable pos found, if mob is safe atm just stop it
if mob_is_safe then
if movement_state.current_quality == GQ_FULL then
local targetpos = {x= movement_state.basepos.x,
y=movement_state.basepos.y,
z=movement_state.basepos.z}
targetpos.x = targetpos.x - movement_state.current_velocity.x
targetpos.z = targetpos.z - movement_state.current_velocity.z
movement_state.accel_to_set =
movement_generic.get_accel_to(targetpos, entity, nil,
entity.data.movement.min_accel)
dbg_mobf.pmovement_lvl2(
"MOBF: good pos, slowing down")
movement_state.changed = true
return
else --stop immediatlely
entity.object:setvelocity({x=0,y=0,z=0})
movement_state.accel_to_set = {x=0,y=nil,z=0}
dbg_mobf.pmovement_lvl2(
"MOBF: stopping at safe pos")
movement_state.changed = true
return
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
--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
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
-- pos isn't critical don't do anything
return
end
end
local geom_ok =
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=nil,
min_max_surface=nil,
min_center_surface=nil })
if geom_ok and
pos_predicted_quality.surface_quality_max == SQ_WRONG then
dbg_mobf.pmovement_lvl2(
"MOBF: wrong surface detected trying to find better pos")
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,2,entity)
end
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
--try generic
movement_state.force_change = true
return
end
end
dbg_mobf.pmovement_lvl2("MOBF: Unhandled suboptimal state:"
.. pos_predicted_quality.tostring(pos_predicted_quality))
movement_state.force_change = true
end
end
-------------------------------------------------------------------------------
-- name: random_movement_handler(entity,movement_state)
--
--! @brief generate a random y-movement
--! @memberof direction_control
--
--! @param entity mob to apply random jump
--! @param movement_state current movement state
--! @return movement_state is modified!
-------------------------------------------------------------------------------
function direction_control.random_movement_handler(entity,movement_state)
dbg_mobf.pmovement_lvl3("MOBF: random movement handler called")
local rand_value = math.random()
local max_value =
entity.dynamic_data.movement.mpattern.random_acceleration_change
* PER_SECOND_CORRECTION_FACTOR
if movement_state.changed == false and
(rand_value < max_value or
movement_state.force_change) then
movement_state.accel_to_set = direction_control.changeaccel(movement_state.basepos,
entity,movement_state.current_velocity)
if movement_state.accel_to_set ~= nil then
--retain current y acceleration
movement_state.accel_to_set.y = movement_state.current_acceleration.y
movement_state.changed = true
end
dbg_mobf.pmovement_lvl1("MOBF: randomly changing speed from "..
printpos(movement_state.current_acceleration).." to "..
printpos(movement_state.accel_to_set))
else
dbg_mobf.pmovement_lvl3("MOBF:" .. entity.data.name ..
" not changing speed random: " .. rand_value .." >= " .. max_value)
end
end

View File

@ -0,0 +1,292 @@
-------------------------------------------------------------------------------
-- 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 height_level_control.lua
--! @brief component containing random drop features
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @ingroup mgen_probab
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @class height_level_control
--! @brief Subcomponent of probabilistic movement generator containing height
--! level control and change functionality
height_level_control = {}
--!@}
-------------------------------------------------------------------------------
-- name: calc_level_change_time(entity)
--
--! @brief calculate time required to change one height level
--! @memberof height_level_control
--! @private
--
--! @param entity mob to calculate change time
--! @param default_accel default accel for mob
--! @return time in seconds
-------------------------------------------------------------------------------
function height_level_control.calc_level_change_time(entity,default_accel)
local retval = 1 --default value
--calculate a reasonable value to stop level change
if entity.data.movement.canfly == nil or
entity.data.movement.canfly == false then --case mob can't fly
return 0
else
-- if it's a flying mob and left it's normal medium
if default_accel ~= 0 then
retval = 0
else
retval = math.sqrt(2/entity.data.movement.min_accel)
end
end
return retval
end
-------------------------------------------------------------------------------
-- name: precheck_movement(entity,movement_state,pos_predicted,pos_predicted_quality)
--
--! @brief check if there is a level change in progress that may
-- need to be stopped
--! @memberof height_level_control
--
--! @param entity mob to check for level change
--! @param movement_state current state of movement
--! @param pos_predicted position the mob will be next
--! @param pos_predicted_quality quality of the next position
-------------------------------------------------------------------------------
function height_level_control.precheck_movement(entity,movement_state,pos_predicted,pos_predicted_quality)
if entity.data.movement.canfly ~= nil and
entity.data.movement.canfly == true and
entity.dynamic_data.movement.changing_levels == true then
local level_change_time = height_level_control.calc_level_change_time(entity,movement_state.default_y_accel)
local time_completed = entity.dynamic_data.movement.ts_random_jump + level_change_time
dbg_mobf.pmovement_lvl1("MOBF: ".. movement_state.now .. " " .. entity.data.name ..
" check complete level change " .. time_completed)
if entity.dynamic_data.movement.changing_levels and
time_completed < movement_state.now then
dbg_mobf.pmovement_lvl2("MOBF: ".. movement_state.now .. " " .. entity.data.name ..
" level change complete reestablishing default y acceleration " .. movement_state.default_y_accel)
entity.dynamic_data.movement.changing_levels = false
movement_state.current_velocity.y = 0
entity.object:setvelocity(movement_state.current_velocity)
movement_state.accel_to_set = movement_state.current_acceleration
movement_state.accel_to_set.y = movement_state.default_y_accel
movement_state.changed = true
end
end
--mob would fly/swim into height it shouldn't be
if movement_state.changed == false then
local invalid_pos_handled = false
--mob would fly/swim into height it shouldn't be
if invalid_pos_handled == false and
pos_predicted_quality.level_quality == LQ_ABOVE then
local min_y,max_y = environment.get_absolute_min_max_pos(entity.environment,movement_state.basepos)
if (pos_predicted.y - max_y) > 10 then
movement_state.override_height_change_chance = 1
else
movement_state.override_height_change_chance = (pos_predicted.y - max_y)/10
end
invalid_pos_handled = true
end
--mob would fly/swim into height it shouldn't be
if invalid_pos_handled == false and
pos_predicted_quality.level_quality == LQ_BELOW then
local min_y,max_y = environment.get_absolute_min_max_pos(entity.environment,movement_state.basepos)
if (min_y - pos_predicted.y) > 10 then
movement_state.override_height_change_chance = 1
else
movement_state.override_height_change_chance = (min_y - pos_predicted.y)/10
end
end
end
end
-------------------------------------------------------------------------------
-- name: random_jump_fly(entity,movement_state)
--
--! @brief apply random jump for flying mobs
--! @memberof height_level_control
--! @private
--
--! @param entity mob to apply random jump
--! @param movement_state current movement state
--! @return movement_state is modified!
-------------------------------------------------------------------------------
function height_level_control.random_jump_fly(entity,movement_state)
--get some information
local accel_to_set = movement_state.current_acceleration
local current_state = environment.pos_is_ok(movement_state.basepos,entity)
--find direction
local ydirection = 1
if current_state == "ok" then
if math.random() <= 0.5 then
ydirection = -1
end
end
if current_state == "above_limit" then
ydirection = -1
end
--state "below_limit" is already handled by initialization value
--prepare basic information about what may be afterwards
local targetpos = {x=movement_state.basepos.x,y=movement_state.basepos.y+ydirection,z=movement_state.basepos.z}
local target_state = environment.pos_is_ok(targetpos,entity);
dbg_mobf.pmovement_lvl2("MOBF: " .. entity.data.name .." flying change y accel dir="..ydirection.." ypos="..movement_state.basepos.y..
" min="..entity.environment.min_height_above_ground..
" max="..entity.environment.max_height_above_ground..
" below="..target_state)
--really do level change
if (target_state == "ok") or
target_state == current_state then
local min_y, max_y = environment.get_absolute_min_max_pos(entity.environment,movement_state.basepos)
dbg_mobf.pmovement_lvl2("MOBF: check level borders current=".. movement_state.basepos.y ..
" min=" .. min_y ..
" max=" .. max_y)
movement_state.accel_to_set.y = ydirection * entity.data.movement.min_accel
entity.dynamic_data.movement.ts_random_jump = movement_state.now
entity.dynamic_data.movement.changing_levels = true
dbg_mobf.pmovement_lvl2("MOBF: " .. entity.data.name .. " " .. movement_state.now .. " " ..
" changing level within borders: " .. movement_state.accel_to_set.y)
movement_state.changed = true
end
end
-------------------------------------------------------------------------------
-- name: random_jump_ground(entity,movement_state)
--
--! @brief apply random jump for ground mobs
--! @memberof height_level_control
--! @private
--
--! @param entity mob to apply random jump
--! @param movement_state current movement state
--! @return movement_state is a modification!
-------------------------------------------------------------------------------
function height_level_control.random_jump_ground(entity,movement_state)
if movement_state.default_y_accel == 0 then
minetest.log(LOGLEVEL_ERROR,"MOBF BUG!!! ground mob can't have zero default acceleration!!!")
end
local random_jump_time = 2 * (entity.dynamic_data.movement.mpattern.random_jump_initial_speed /
math.abs(movement_state.default_y_accel))
local next_jump = entity.dynamic_data.movement.ts_random_jump +
entity.dynamic_data.movement.mpattern.random_jump_delay +
random_jump_time
dbg_mobf.movement_lvl2("MOBF: now=" .. movement_state.now .. " time of next jump=" .. next_jump ..
" | " .. random_jump_time .. " " .. entity.dynamic_data.movement.mpattern.random_jump_delay .. " " ..
entity.dynamic_data.movement.ts_random_jump .. " " .. movement_state.default_y_accel .. " " ..
entity.dynamic_data.movement.mpattern.random_jump_initial_speed)
if movement_state.now > next_jump then
local ground_distance = mobf_ground_distance(movement_state.basepos)
--we shouldn't be moving in y direction at that time so probably we are just dropping
--we can only jump while on solid ground!
if ground_distance <= 1 then
local current_speed = entity.object:getvelocity();
dbg_mobf.pmovement_lvl2("MOBF: " .. entity.data.name .." doing random jump "..
"speed: " .. entity.dynamic_data.movement.mpattern.random_jump_initial_speed ..
": ",entity)
current_speed.y = entity.dynamic_data.movement.mpattern.random_jump_initial_speed
entity.object:setvelocity(current_speed)
entity.dynamic_data.movement.ts_random_jump = movement_state.now
else
dbg_mobf.pmovement_lvl2("MOBF: " .. entity.data.name .. " Random jump but ground distance was:" .. ground_distance ..
" " ..movement_state.current_acceleration.y ..
" " ..movement_state.default_y_accel)
--this is a workaround for a bug
--setting y acceleration to 0 for ground born mobs shouldn't be possible
if movement_state.current_acceleration.y == 0 then
movement_state.accel_to_set.x = movement_state.current_acceleration.x
movement_state.accel_to_set.y = movement_state.default_y_accel
movement_state.accel_to_set.z = movement_state.current_acceleration.z
movement_state.changed = true
end
end
end
end
-------------------------------------------------------------------------------
-- name: random_movement_handler(entity,movement_state)
--
--! @brief generate a random y-movement
--! @memberof height_level_control
--
--! @param entity mob to apply random jump
--! @param movement_state current movement state
--! @return movement_state is a modified!
-------------------------------------------------------------------------------
function height_level_control.random_movement_handler(entity,movement_state)
--generate random y movement changes
if movement_state.changed == false and
entity.dynamic_data.movement.changing_levels == false and
(math.random() < entity.dynamic_data.movement.mpattern.random_jump_chance or
math.random() < movement_state.override_height_change_chance) then
dbg_mobf.pmovement_lvl1("MOBF: Random jump chance for mob " .. entity.data.name .. " "..
entity.dynamic_data.movement.ts_random_jump .. " "..
entity.dynamic_data.movement.mpattern.random_jump_delay .. " " .. movement_state.now
)
local to_set
--flying/swiming mobs do level change on random jump chance
if entity.data.movement.canfly then
height_level_control.random_jump_fly(entity,movement_state)
--ground mobs
else
height_level_control.random_jump_ground(entity,movement_state)
end
end
end

View File

@ -0,0 +1,656 @@
-------------------------------------------------------------------------------
-- 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 main_probab.lua
--! @brief component containing a probabilistic movement generator
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--
--! @defgroup mgen_probab MGEN: probabilistic movement generator
--! @brief A movement generator trying to create a random movement
--! @ingroup framework_int
--! @{
--
--! @defgroup mpatterns Movement patterns
--! @brief movement patterns used for probabilistic movement gen
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--factor used to adjust chances defined as x/per second to on_step frequency
PER_SECOND_CORRECTION_FACTOR = 0.25
--! @class movement_gen
--! @brief a probabilistic movement generator
movement_gen = {}
--! @brief movement patterns known to probabilistic movement gen
mov_patterns_defined = {}
--!@}
dofile (mobf_modpath .. "/mgen_probab/movement_patterns/dont_move.lua")
dofile (mobf_modpath .. "/mgen_probab/movement_patterns/run_and_jump_low.lua")
dofile (mobf_modpath .. "/mgen_probab/movement_patterns/stop_and_go.lua")
dofile (mobf_modpath .. "/mgen_probab/movement_patterns/swim_pattern1.lua")
dofile (mobf_modpath .. "/mgen_probab/movement_patterns/swim_pattern2.lua")
dofile (mobf_modpath .. "/mgen_probab/movement_patterns/flight_pattern1.lua")
dofile (mobf_modpath .. "/mgen_probab/height_level_control.lua")
dofile (mobf_modpath .. "/mgen_probab/direction_control.lua")
--! @brief movement generator identifier
--! @memberof movement_gen
movement_gen.name = "probab_mov_gen"
-------------------------------------------------------------------------------
-- name: register_pattern(pattern)
--
--! @brief initialize movement generator
--! @memberof movement_gen
--
--! @param pattern pattern to register
--! @return true/false
-------------------------------------------------------------------------------
function movement_gen.register_pattern(pattern)
core.log("action","\tregistering pattern "..pattern.name)
if mobf_rtd.movement_patterns[pattern.name] == nil then
mobf_rtd.movement_patterns[pattern.name] = pattern
return true
else
return false
end
end
-------------------------------------------------------------------------------
-- name: initialize(entity)
--
--! @brief initialize movement generator
--! @memberof movement_gen
--
-------------------------------------------------------------------------------
function movement_gen.initialize()
for index,value in ipairs(mov_patterns_defined) do
movement_gen.register_pattern(value)
end
end
-------------------------------------------------------------------------------
-- name: callback(entity)
--
--! @brief main movement generation function
--! @memberof movement_gen
--! @private
--
--! @param entity mob to generate movement for
-------------------------------------------------------------------------------
function movement_gen.callback(entity)
mobf_assert_backtrace(entity ~= nil)
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
--initialize status datastructure
local movement_state = {
basepos = entity.getbasepos(entity),
default_y_accel = nil,
centerpos = entity.object:getpos(),
current_acceleration = nil,
current_velocity = entity.object:getvelocity(),
now = nil,
override_height_change_chance = 0,
accel_to_set = nil,
force_change = false,
changed = false,
}
---------------------------------------------------------------------------
---------------------------------------------------------------------------
-- --
-- CHECK MOB POSITION --
-- --
---------------------------------------------------------------------------
---------------------------------------------------------------------------
dbg_mobf.pmovement_lvl3("MOBF: position check for mob ".. entity.data.name ..
" "..printpos(movement_state.basepos))
movement_state.default_y_accel =
environment.get_default_gravity(movement_state.basepos,
entity.environment.media,
entity.data.movement.canfly)
mobf_assert_backtrace(movement_state.default_y_accel ~= nil)
movement_state.now = mobf_get_current_time()
--check current position
--movement state is modified by this function
local retval = movement_gen.fix_current_pos(entity, movement_state)
--mob has been removed due to unfixable problems
if retval.abort_processing then
return
end
--read additional information required for further processing
movement_state.current_acceleration = entity.object:getacceleration()
if movement_state.changed then
minetest.log(LOGLEVEL_WARNING,
"MOBF:position got fixed ".. entity.data.name)
end
---------------------------------------------------------------------------
---------------------------------------------------------------------------
-- --
-- CHECK MOB MOVEMENT ITSELF --
-- --
---------------------------------------------------------------------------
---------------------------------------------------------------------------
dbg_mobf.pmovement_lvl3("MOBF: movement hard limits check for mob "..
entity.data.name .. " "..printpos(movement_state.basepos))
movement_gen.fix_runaway(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
---------------------------------------------------------------------------
---------------------------------------------------------------------------
-- --
-- DO / PREPARE CHANGES THAT NEED TO BE DONE BECAUSE OF MOB IS --
-- PREDICTED TO BE SOMEWHERE WHERE IT SHOULDN'T BE --
-- --
---------------------------------------------------------------------------
---------------------------------------------------------------------------
dbg_mobf.pmovement_lvl3("MOBF: movement check for mob ".. entity.data.name
.. " "..printpos(movement_state.basepos))
--skip if movement already got changed
if movement_state.changed == false then
--predict next block mob will be
local pos_predicted =
movement_generic.predict_next_block( movement_state.basepos,
movement_state.current_velocity,
movement_state.current_acceleration)
--if this isn't a flying mob ignore y prediction as it's not honoring
--collisions
if not entity.data.movement.canfly then
pos_predicted.y = movement_state.basepos.y
end
--local pos_predicted =
-- movement_generic.predict_enter_next_block(entity, movement_state.basepos,
-- movement_state.current_velocity,
-- movement_state.current_acceleration)
local pos_predicted_quality = environment.pos_quality(pos_predicted,entity)
dbg_mobf.pmovement_lvl3("MOBF: Pos pred quality: ".. entity.data.name
.. ": " .. pos_predicted_quality:shortstring())
-- Y-Movement
if movement_state.changed == false then
height_level_control.precheck_movement(entity,movement_state,
pos_predicted,pos_predicted_quality)
end
-- X/Z-Movement
if movement_state.changed == false then
direction_control.precheck_movement(entity,movement_state,
pos_predicted,pos_predicted_quality)
end
end
---------------------------------------------------------------------------
---------------------------------------------------------------------------
-- --
-- DO RANDOM CHANGES TO MOB MOVEMENT --
-- --
---------------------------------------------------------------------------
---------------------------------------------------------------------------
dbg_mobf.pmovement_lvl3("MOBF: randomized movement for mob "..
entity.data.name .. " "..printpos(movement_state.basepos))
--do randomized changes if not fighting
height_level_control.random_movement_handler(entity,movement_state)
direction_control.random_movement_handler(entity,movement_state)
---------------------------------------------------------------------------
---------------------------------------------------------------------------
-- --
-- APPLY CHANGES CALCULATED BEFORE --
-- --
---------------------------------------------------------------------------
---------------------------------------------------------------------------
movement_gen.apply_movement_changes(entity,movement_state)
end
-------------------------------------------------------------------------------
-- name: apply_movement_changes(entity,movement_state)
--
--! @brief check and apply the changes from movement_state
--! @memberof movement_gen
--! @private
--
--! @param entity mob to apply changes
--! @param movement_state changes to apply
-------------------------------------------------------------------------------
function movement_gen.apply_movement_changes(entity,movement_state)
if movement_state.changed then
--check if there is a acceleration to set
if movement_state.accel_to_set == nil then
movement_state.accel_to_set = {x=0,
y=movement_state.default_y_accel,
z=0}
entity.object:setvelocity({x=0,y=0,z=0})
minetest.log(LOGLEVEL_ERROR,
"MOBF BUG!!!! set accel requested but no accel given!")
end
--check if gravity is set
if movement_state.accel_to_set.y == nil then
--print("fixing y movement: ".. printpos(movement_state.accel_to_set))
movement_state.accel_to_set.y = movement_state.current_acceleration.y
end
-- make sure non flying mobs have default acceleration
if entity.data.movement.canfly == false then
movement_state.accel_to_set.y = movement_state.default_y_accel
if movement_state.default_y_accel == 0 then
minetest.log(LOGLEVEL_ERROR,"MOBF BUG!!! " .. entity.data.name
.. " mob that can't fly has acceleration 0!")
end
end
dbg_mobf.pmovement_lvl2("MOBF: setting acceleration to "
..printpos(movement_state.accel_to_set))
-- todo check for harsh direction changes
entity.dynamic_data.movement.acceleration = movement_state.accel_to_set
entity.object:setacceleration(movement_state.accel_to_set)
end
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by movement generator
--! @memberof movement_gen
--
--! @param entity mob to initialize
--! @param now current time
-------------------------------------------------------------------------------
function movement_gen.init_dynamic_data(entity,now)
local accel_to_set = {x=0,y=9.81,z=0}
local pos = entity.object:getpos()
--initialize acceleration values
accel_to_set.y = environment.get_default_gravity(pos,
entity.environment.media,
entity.data.movement.canfly)
mobf_assert_backtrace(accel_to_set.y ~= nil)
local data = {
started = false,
acceleration = accel_to_set,
changing_levels = false,
ts_random_jump = now,
ts_orientation_upd = now,
mpattern = mobf_rtd.movement_patterns[entity.data.movement.pattern],
ts_last_slowcheck = now,
}
entity.dynamic_data.movement = data
end
-------------------------------------------------------------------------------
-- name: fix_runaway(entity,movement_state)
--
--! @brief fix runaway speed mobs
--! @memberof movement_gen
--! @private
--
--! @param entity mob to check speed limits
--! @param movement_state current state of movement
-------------------------------------------------------------------------------
function movement_gen.fix_runaway(entity,movement_state)
--avoid mobs racing away
local xzspeed = mobf_calc_scalar_speed(movement_state.current_velocity.x,
movement_state.current_velocity.z)
dbg_mobf.pmovement_lvl3("MOBF: checkrunaway x=" ..
movement_state.current_velocity.x ..
" z=" ..movement_state.current_velocity.z ..
" xz=" .. xzspeed ..
" maximum=" .. entity.data.movement.max_speed)
if xzspeed > entity.data.movement.max_speed then
dbg_mobf.pmovement_lvl3("MOBF: too fast! vxz=" .. xzspeed)
dbg_mobf.pmovement_lvl3("MOBF: current acceleration:" ..
printpos(movement_state.current_acceleration))
local direction = mobf_calc_yaw(movement_state.current_velocity.x,
movement_state.current_velocity.z)
--reduce speed to 90% of current speed
local new_speed = mobf_calc_vector_components(direction,xzspeed*0.9)
new_speed.y = movement_state.current_velocity.y
entity.object:setvelocity(new_speed)
movement_state.current_velocity = new_speed
--don't accelerate any longer
movement_state.accel_to_set = {x=0,z=0}
movement_state.changed = true
dbg_mobf.pmovement_lvl2("MOBF: fix runaway new acceleration:" ..
printpos(movement_state.accel_to_set))
end
end
-------------------------------------------------------------------------------
-- name: fix_to_slow(entity,movement_state)
--
--! @brief check if mobs motion is below its minimal speed (e.g. flying birds)
--! @memberof movement_gen
--! @private
--
--! @param entity mob to check speed limits
--! @param movement_state current state of movement
-------------------------------------------------------------------------------
function movement_gen.fix_to_slow(entity,movement_state)
local xzspeed = mobf_calc_scalar_speed(movement_state.current_velocity.x,
movement_state.current_velocity.z)
dbg_mobf.pmovement_lvl3("MOBF: checktoslow x=" ..
movement_state.current_velocity.x ..
" z=" ..movement_state.current_velocity.z ..
" xz=" .. xzspeed ..
" minimum=" .. dump(entity.data.movement.min_speed))
--this ain't perfect to avoid flying mobs standing in air
--but it's a quick solution to fix most of the problems
if (entity.data.movement.min_speed ~= nil and
xzspeed < entity.data.movement.min_speed) or
xzspeed == nil or
xzspeed == 0 then
dbg_mobf.pmovement_lvl2("MOBF: too slow! vxz=" .. xzspeed)
--use normal speed change handling
movement_state.force_change = true
end
end
-------------------------------------------------------------------------------
-- name: fix_current_pos(entity,movement_state)
--
--! @brief check current position for validity and try to fix
--! @memberof movement_gen
--! @private
--
--! @param entity to fix position
--! @param movement_state movement state of mob
--! @return { speed_to_set = {x,y,z},
--! speed_changed = true/false,
--! force_speed_change = true/false }
-------------------------------------------------------------------------------
function movement_gen.fix_current_pos(entity,movement_state)
--check if current pos is ok
local current_state = environment.pos_is_ok(movement_state.basepos,entity)
local handled = false
dbg_mobf.pmovement_lvl3("MOBF: current state "..
entity.data.name .. ": " .. current_state)
movement_state.accel_to_set = { x=0,
y=movement_state.default_y_accel,
z=0 }
local abort_processing = false
if current_state == "ok" or
current_state == "possible_surface" then
entity.dynamic_data.movement.last_pos_in_env = movement_state.centerpos
handled = true
end
--states ok drop and wrong_surface don't require an imediate action
if current_state ~= "ok" and
current_state ~= "drop" and
current_state ~= "wrong_surface" and
current_state ~= "possible_surface" and
current_state ~= "below_limit" and
current_state ~= "above_limit" then
dbg_mobf.movement_lvl1("MOBF: BUG !!! somehow your mob managed to get"
.." where it shouldn't be ("
.. current_state .. "), trying to fix")
--stop mob from moving at all
entity.object:setacceleration({x=0,y=movement_state.default_y_accel,z=0})
movement_state.force_change = true
--mob is currently in water,
--try to find a suitable position 1 level above current level
if current_state == "in_water" or
current_state == "above_water" then
local targetpos = nil
--if we don't have an old pos or old pos is to far away
--try to finde another good pos around
if entity.dynamic_data.movement.last_pos_in_env == nil or
entity.dynamic_data.movement.last_pos_in_env.y >
movement_state.centerpos.y + 2 then
targetpos =
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 targetpos ~= nil then
targetpos.y = targetpos.y - entity.collisionbox[2]
end
else
targetpos = entity.dynamic_data.movement.last_pos_in_env
end
if targetpos ~= nil then
mobf_bug_warning(LOGLEVEL_INFO,"MOBF: BUG !!! didn't find a way"
.. " out of water, for mob at: " ..
printpos(movement_state.basepos) ..
" using last known good position")
if targetpos == nil then
targetpos = entity.dynamic_data.movement.last_pos_in_env
else
targetpos.y = targetpos.y - entity.collisionbox[2]
end
minetest.log(LOGLEVEL_INFO,"MOBF: Your mob " ..
entity.data.name .. " " ..
tostring(entity) .. " dropt into water moving to "..
printpos(targetpos).." state: "..
environment.pos_is_ok(targetpos,entity))
entity.object:moveto(targetpos)
movement_state.current_velocity.x = 0 --movement_state.current_velocity.x/10
movement_state.current_velocity.z = 0 --movement_state.current_velocity.z/10
entity.object:setvelocity(movement_state.current_velocity)
movement_state.centerpos = targetpos
movement_state.basepos = entity.getbasepos(entity)
movement_state.accel_to_set.y =
environment.get_default_gravity(targetpos,
entity.environment.media,
entity.data.movement.canfly)
mobf_assert_backtrace(movement_state.accel_to_set.y ~= nil)
else
mobf_bug_warning(LOGLEVEL_WARNING,"MOBF: BUG !!! didn't find a way"
.." out of water, for mob at: " ..
printpos(movement_state.basepos) .. " drowning, last pos in env:" ..
dump(entity.dynamic_data.movement.last_pos_in_env))
abort_processing = true
spawning.remove(entity, "mgen probab watercheck")
end
handled = true
end
if current_state == "in_air" then
--TODO die?
handled = true
end
end
local damagetime = 60
if entity.data.generic.wrong_surface_damage_time ~= nil then
damagetime = entity.data.generic.wrong_surface_damage_time
end
if current_state == "wrong_surface" and
entity.dynamic_data.good_surface ~= nil then
if movement_state.now - entity.dynamic_data.good_surface > damagetime then
entity.object:set_hp( entity.object:get_hp() -
(entity.data.generic.base_health/25))
dbg_mobf.movement_lvl1("MOBF: mob is on wrong surface it will slowly take damage")
--reset timer for next damage interval
entity.dynamic_data.good_surface = movement_state.now
if entity.object:get_hp() <= 0 then
abort_processing = true
spawning.remove(entity, "mgen probab surfacecheck")
end
movement_state.force_change = true
end
handled = true
else
entity.dynamic_data.good_surface = movement_state.now
end
if current_state == "collision" then
local current_pos = mobf_round_pos(movement_state.basepos);
local current_node = minetest.get_node( current_pos );
local walkable = false
if minetest.registered_nodes[current_node.name] then
walkable = minetest.registered_nodes[current_node.name].walkable
end
dbg_mobf.movement_lvl2( "MOBF: Mob collided with "..
tostring( current_pos.x )..":"..
tostring( current_pos.y )..":"..
tostring( current_pos.z ).." Nodename:"..
tostring( current_node.name ).." walkable:"..
tostring( walkable ))
if not mobf_is_walkable(current_node) then
local targetpos = entity.dynamic_data.movement.last_pos_in_env
if targetpos == nil then
targetpos =
environment.get_suitable_pos_same_level({x=current_pos.x,
y=current_pos.y,
z=current_pos.z},
1,
entity)
end
if targetpos == nil then
targetpos =
environment.get_suitable_pos_same_level({x=current_pos.x,
y=current_pos.y+1,
z=current_pos.z},
1,
entity)
end
if targetpos ~= nil then
minetest.log(LOGLEVEL_INFO,
"MOBF: Your mob " ..
entity.data.name ..
" is within solid block moving to"..
printpos(targetpos).." state: "..
environment.pos_is_ok(targetpos,entity))
entity.object:moveto(targetpos)
movement_state.accel_to_set.y =
environment.get_default_gravity(targetpos,
entity.environment.media,
entity.data.movement.canfly)
mobf_assert_backtrace(movement_state.default_y_accel ~= nil)
else
minetest.log(LOGLEVEL_WARNING,"MOBF: mob " .. entity.data.name
.. " was within solid block, removed")
abort_processing = true
spawning.remove(entity, "mgen probab solidblockcheck")
end
else
dbg_mobf.movement_lvl2( "MOBF: mob is on walkable surface " ..
"so no forced repositioning" )
end
handled = true
end
if not handled then
dbg_mobf.movement_lvl1("MOBF: ".. entity.data.name .. " state: "..
current_state .. " not handled!")
end
return { abort_processing=abort_processing, }
end
-------------------------------------------------------------------------------
-- name: set_target(entity,target)
--
--! @brief set target for movgen
--! @memberof movement_gen
--! @private
--
--! @param entity mob to apply to
--! @param target to set
-------------------------------------------------------------------------------
function movement_gen.set_target(entity,target)
return false
end
--register this movement generator
registerMovementGen(movement_gen.name,movement_gen)

View File

@ -0,0 +1,39 @@
-------------------------------------------------------------------------------
-- 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 dont_move.lua
--! @brief movementpattern for immobile mob
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct dont_move_prototype
--! @brief a movement pattern resulting in a mob not moving at all
local dont_move_prototype = {
name ="dont_move",
jump_up =0,
random_jump_chance =0,
random_jump_initial_speed =0,
random_jump_delay =0,
random_acceleration_change =0,
--
-- --run towards player or run away? 1 <-> -1
-- player_attraction =0,
-- --maximum distance a player has an effect
-- player_attraction_range =-1,
}
--!@}
table.insert(mov_patterns_defined,dont_move_prototype)

View File

@ -0,0 +1,34 @@
-------------------------------------------------------------------------------
-- 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 flight_pattern1.lua
--! @brief movementpattern flight 1
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct flight_pattern1_prototype
--! @brief a movement pattern used for flying mobs
local flight_pattern1_prototype = {
name ="flight_pattern1",
jump_up =0,
random_jump_chance =0.4,
random_jump_initial_speed =0,
random_jump_delay =10,
random_acceleration_change =0.3,
}
--!@}
table.insert(mov_patterns_defined,flight_pattern1_prototype)

View File

@ -0,0 +1,35 @@
-------------------------------------------------------------------------------
-- 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 run_and_jump_low.lua
--! @brief movementpattern running arround ad doing random low jumps
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct run_and_jump_low_prototype
--! @brief a movement pattern used for quick moving and direction changing mobs
--! that jump every now and then
local run_and_jump_low_prototype = {
name ="run_and_jump_low",
jump_up =0.2,
random_jump_chance =0.3,
random_jump_initial_speed =3.5,
random_jump_delay =2,
random_acceleration_change =0.6,
}
--!~@}
table.insert(mov_patterns_defined,run_and_jump_low_prototype)

View File

@ -0,0 +1,33 @@
-------------------------------------------------------------------------------
-- 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 stop_and_go.lua
--! @brief movementpattern creating a random stop and go movement e.g. sheep/cow
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct stop_and_go_prototype
--! @brief movement pattern for mobs wandering around randomly
local stop_and_go_prototype = {
name ="stop_and_go",
jump_up =0.4,
random_jump_chance =0,
random_jump_initial_speed =0,
random_jump_delay =0,
random_acceleration_change =0.05,
}
--!@}
table.insert(mov_patterns_defined,stop_and_go_prototype)

View File

@ -0,0 +1,33 @@
-------------------------------------------------------------------------------
-- 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 swim_pattern1.lua
--! @brief movementpattern for slow swimming mobs
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct swim_pattern1_prototype
--! @brief movement pattern for mobs swimming slow
local swim_pattern1_prototype = {
name ="swim_pattern1",
jump_up =0,
random_jump_chance =0.2,
random_jump_initial_speed =0,
random_jump_delay =10,
random_acceleration_change =0.5,
}
--!@}
table.insert(mov_patterns_defined,swim_pattern1_prototype)

View File

@ -0,0 +1,33 @@
-------------------------------------------------------------------------------
-- 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 swim_pattern2.lua
--! @brief movementpattern for medium swimming mobs
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct swim_pattern1_prototype
--! @brief movement pattern for mobs swimming medium speeds
local swim_pattern2_prototype = {
name ="swim_pattern2",
jump_up =0,
random_jump_chance =0.4,
random_jump_initial_speed =0,
random_jump_delay =15,
random_acceleration_change =0.7,
}
--!@}
table.insert(mov_patterns_defined,swim_pattern2_prototype)

View File

@ -0,0 +1,35 @@
-------------------------------------------------------------------------------
-- 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 swim_pattern1.lua
--! @brief movementpattern for slow swimming mobs
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct swim_pattern1_prototype
--! @brief movement pattern for mobs swimming slow
local swim_pattern3_prototype = {
name ="swim_pattern3",
jump_up =0,
random_jump_chance =0.2,
random_jump_initial_speed =0,
random_jump_delay =10,
random_acceleration_change =0.5,
min_height_above_ground = 3
}
--!@}
table.insert(mov_patterns_defined,swim_pattern3_prototype)

View File

@ -0,0 +1,37 @@
-------------------------------------------------------------------------------
-- 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 template.lua
--! @brief template file for movement patterns
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-11
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--movement pattern for movement generator
-- {
-- --patternname
-- name ="example"
--
-- --chance to jump to higher level instead of turning
-- jump_up =0,
--
-- --chance an mob does random jumps
-- random_jump_chance =0,
-- --mobs jump speed (controling height of jump)
-- random_jump_initial_speed =0,
-- --minimum time between random jumps
-- random_jump_delay =0,
--
-- --chance an mob randomly changes its speed/direction
-- random_acceleration_change =0,
-- }
--!@}

View File

@ -0,0 +1,208 @@
-------------------------------------------------------------------------------
-- 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 mgen_raster.lua
--! @brief component containing a probabilistic movement generator (uses mgen follow)
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup mgen_raster MGEN: raster based movement generator
--! @brief A movement generator creating movement that creates a rasterized
--! random movement
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @class mgen_raster
--! @brief a movement generator creating a rasterized probabilistic movement
--!@}
mgen_raster = {}
--! @brief movement generator identifier
--! @memberof mgen_follow
mgen_raster.name = "probab_raster_mov_gen"
-------------------------------------------------------------------------------
-- name: stop(entity)
--
--! @brief stop this entity
--! @memberof mgen_raster
--
--! @param entity mob to stop
-------------------------------------------------------------------------------
function mgen_raster.stop(entity)
local defgrav = environment.get_default_gravity(entity.getbasepos(entity),
entity.environment,
entity.data.movement.canfly)
entity.dynamic_data.movement.target = nil
entity.object:setvelocity({x=0,y=0,z=0})
entity.object:setacceleration({x=0,y=defgrav,z=0})
end
-------------------------------------------------------------------------------
-- name: callback(entity,now)
--
--! @brief main callback to make a mob follow its target
--! @memberof mgen_raster
--
--! @param entity mob to generate movement for
--! @param now current time
-------------------------------------------------------------------------------
function mgen_raster.callback(entity,now)
local basepos = entity.getbasepos(entity)
local dtime = entity.current_dtime
entity.dynamic_data.movement.time_travelled = entity.dynamic_data.movement.time_travelled + dtime
--check environment
local pos_state = environment.pos_is_ok(basepos,entity)
if pos_state ~= "ok" and
pos_state ~= "possible_surface" then
mgen_raster.stop(entity)
--try to find a good position around to move mob to
local new_pos = environment.get_suitable_pos_same_level(basepos,1,entity,false)
if (new_pos == nil ) then
new_pos = environment.get_suitable_pos_same_level({x=basepos.x,
y=basepos.y+1,
z=basepos.z}
,1,entity,false)
end
if (new_pos == nil ) then
new_pos = environment.get_suitable_pos_same_level({x=basepos.x,
y=basepos.y+1,
z=basepos.z}
,1,entity,false)
end
if (new_pos ~= nil ) then
--TODO fix position according to model information!
enity.object:moveto(new_pos)
basepos = new_pos
else
--TODO error handling
end
end
--check on ground
if not mgen_raster.onground(entity,now,basepos) then
mgen_raster.stop(entity)
end
--check how long we've been traveling
if ( entity.dynamic_data.movement.time_travelled > entity.dynamic_data.movement.eta ) then
mgen_raster.stop(entity)
end
--check distance to target (target reached or missed)
local distance_to_target = mobf_calc_distance(basepos,entity.dynamic_data.movement.target)
if distance_to_target > 2.0 or
distance_to_target < 0.1 then
mgen_raster.stop(entity)
end
--find next point
if entity.dynamic_data.nextpoint == nil then
entity.dynamic_data.nextpoint = environment.get_suitable_pos_same_level(basepos,1,entity,false)
--TODO calc maximum eta + 10%
end
--call move to target mgen
helper_mgen.callback(entity,now)
end
-------------------------------------------------------------------------------
-- name: initialize()
--
--! @brief initialize movement generator
--! @memberof mgen_raster
--! @public
-------------------------------------------------------------------------------
function mgen_raster.initialize(entity,now)
--intentionaly doing nothing this function is for symmetry reasons only
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by movement generator
--! @memberof mgen_raster
--! @public
--
--! @param entity mob to initialize dynamic data
--! @param now current time
-------------------------------------------------------------------------------
function mgen_raster.init_dynamic_data(entity,now)
local pos = entity.object:getpos()
local data = {
target = nil,
guardspawnpoint = true,
time_travelled = 0,
helper_mgen = getMovementGen(follow_mov_gen),
max_distance = 0.1,
}
entity.dynamic_data.movement = data
end
-------------------------------------------------------------------------------
-- name: onground(entity,now)
--
--! @brief check if mob is touching ground
--! @memberof mgen_raster
--! @public
--
--! @param entity mob to initialize dynamic data
--! @param now current time
--! @param basepos position of feet
-------------------------------------------------------------------------------
function mgen_raster.onground(entity,now,basepos)
local posbelow = { x=basepos.x, y=basepos.y-1,z=basepos.z}
for dx=-1,1 do
for dz=-1,1 do
local fp0 = posbelow
local fp = { x= posbelow.x + (0.5*dx),
y= posbelow.y,
z= posbelow.z + (0.5*dz) }
local n = minetest.get_node(fp)
if mobf_is_walkable(n) then
return true
end
end
end
end
-------------------------------------------------------------------------------
-- name: set_target(entity,target)
--
--! @brief set target for movgen
--! @memberof mgen_raster
--! @private
--
--! @param entity mob to apply to
--! @param target to set
-------------------------------------------------------------------------------
function mgen_raster.set_target(entity,target)
return false
end
--register this movement generator
registerMovementGen(mgen_raster.name,mgen_raster)

View File

@ -0,0 +1,721 @@
-------------------------------------------------------------------------------
-- 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 mob_state.lua
--! @brief component mob state transition handling
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup mob_state State handling functions
--! @brief a component to do basic changes to mob on state change
--! @ingroup framework_int
--! @{
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mob_state"))
--! @class mob_state
--! @brief state handling class
--! @}
mob_state = {}
mob_state.default_state_time = 30
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] initialize(entity,now)
--
--! @brief initialize state dynamic data
--! @memberof mob_state
--! @public
--
--! @param entity elemet to initialize state data
--! @param now current time
-------------------------------------------------------------------------------
function mob_state.initialize(entity,now)
dbg_mobf.mob_state_lvl3("MOBF: " .. entity.data.name
.. " initializing state dynamic data")
local state = {
current = "default",
time_to_next_change = 30,
locked = false,
enabled = false,
}
local sum_chances = 0
local state_count = 0
if entity.data.states ~= nil then
for s = 1, #entity.data.states , 1 do
sum_chances = sum_chances + entity.data.states[s].chance
if entity.data.states[s].name ~= "combat" and
entity.data.states[s].name ~= "default" then
state_count = state_count +1
end
end
end
--sanity check for state chances
if sum_chances > 1 then
minetest.log(LOGLEVEL_WARNING,"MOBF: Warning sum of state chances for mob "
.. entity.data.name .. " > 1")
end
--only enable state changeing if there is at least one state
if state_count > 0 then
state.enabled = true
end
entity.dynamic_data.state = state
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] get_state_by_name(entity,name)
--
--! @brief get a state by its name
--! @memberof mob_state
--! @public
--
--! @param entity elemet to look for state data
--! @param name of state
--!
--! @return state data or nil
-------------------------------------------------------------------------------
function mob_state.get_state_by_name(entity,name)
mobf_assert_backtrace(entity ~= nil and entity.data ~= nil)
for i=1, #entity.data.states, 1 do
if entity.data.states[i].name == name then
return entity.data.states[i]
end
end
return nil
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] lock(entity,value)
--
--! @brief disable random state changes for a mob
--! @memberof mob_state
--! @public
--
--! @param entity elemet to lock
--! @param value to set
-------------------------------------------------------------------------------
function mob_state.lock(entity,value)
if value ~= false and value ~= true then
return
end
if entity.dynamic_data.state == nil then
dbg_mobf.mob_state_lvl1("MOBF: unable to lock state for: "
.. entity.data.name .. " no state dynamic data present")
return
end
entity.dynamic_data.state.locked = value
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] callback(entity,now,dstep)
--
--! @brief callback handling state changes
--! @memberof mob_state
--! @public
--
--! @param entity elemet to look for state data
--! @param now current time
--! @param dstep time passed since last call
--
--! @return
-------------------------------------------------------------------------------
function mob_state.callback(entity,now,dstep)
--TODO find out if this needs to be replaced
-- if entity.dynamic_data.state == nil then
-- minetest.log(LOGLEVEL_ERRROR,"MOBF BUG: " .. entity.data.name
-- .. " mob state callback without mob dynamic data!")
-- mob_state.initialize(entity,now)
-- local default_state = mob_state.get_state_by_name(self,"default")
-- entity.dynamic_data.current_movement_gen = getMovementGen(default_state.movgen)
-- entity.dynamic_data.current_movement_gen.init_dynamic_data(entity,mobf_get_current_time())
-- entity = spawning.replace_entity(entity,entity.data.modname .. ":"..entity.data.name,true)
-- return true
-- end
--abort state change if current state is locked
if entity.dynamic_data.state.locked or
entity.dynamic_data.state.enabled == false then
dbg_mobf.mob_state_lvl3("MOBF: " .. entity.data.name
.. " state locked or no custom states definded ")
return true
end
entity.dynamic_data.state.time_to_next_change = entity.dynamic_data.state.time_to_next_change -dstep
--do only change if last state timed out
if entity.dynamic_data.state.time_to_next_change < 0 then
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name
.. " time to change state: " .. entity.dynamic_data.state.time_to_next_change
.. " , " .. dstep .. " entity=" .. tostring(entity))
local rand = math.random()
local maxvalue = 0
local state_table = {}
--fill table with available states
for i=1, #entity.data.states, 1 do
if (entity.data.states[i].HANDLER_precondition == nil or
entity.data.states[i].HANDLER_precondition(entity,entity.data.states[i])) and
--ignore states that are not supposed to be switched to
--by automatic state change handling e.g. fighting states or
--manual set states
( entity.data.states[i].state_mode == nil or
entity.data.states[i].state_mode == "auto") then
table.insert(state_table,entity.data.states[i])
end
end
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name .. " "
.. dump(#state_table) .. " states do pass pecondition check ")
--try to get a random state to change to
for i=1, #state_table, 1 do
local rand_state = math.random(#state_table)
local current_chance = 0
if type (state_table[rand_state].chance) == "function" then
current_chance = state_table[rand_state].chance(entity,now,dstep)
else
if state_table[rand_state].chance ~= nil then
current_chance = state_table[rand_state].chance
end
end
local random_value = math.random()
if random_value < current_chance then
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name
.. " switching to state " .. state_table[rand_state].name)
mob_state.change_state(entity,state_table[rand_state])
return true
else
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name
.. " not switching to state " .. state_table[rand_state].name
.. " rand was: " .. random_value)
table.remove(state_table,rand_state)
end
end
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name
.. " no specific state selected switching to default state ")
--switch to default state (only reached if no change has been done
mob_state.change_state(entity,mob_state.get_state_by_name(entity,"default"))
else
dbg_mobf.mob_state_lvl3("MOBF: " .. entity.data.name
.. " is not ready for state change ")
return true
end
return true
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] switch_switch_movgenentity(entity,state)
--
--! @brief helper function to swich a movement based on new state
--! @memberof mob_state
--! @private
--
--! @param entity to change movement gen
--! @param state to take new entity
-------------------------------------------------------------------------------
function mob_state.switch_movgen(entity,state)
mobf_assert_backtrace(entity ~= nil)
mobf_assert_backtrace(state ~= nil)
local mov_to_set = nil
--determine new movement gen
if state.movgen ~= nil then
mov_to_set = getMovementGen(state.movgen)
else
local default_state = mob_state.get_state_by_name(entity,"default")
mov_to_set = getMovementGen(default_state.movgen)
end
--check if new mov gen differs from old one
if mov_to_set ~= nil and
mov_to_set ~= entity.dynamic_data.current_movement_gen then
entity.dynamic_data.current_movement_gen = mov_to_set
--TODO initialize new movement gen
entity.dynamic_data.current_movement_gen.init_dynamic_data(entity,mobf_get_current_time())
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] change_state(entity,state)
--
--! @brief change state for an entity
--! @memberof mob_state
--! @public
--
--! @param entity to change state
--! @param state to change to
-------------------------------------------------------------------------------
function mob_state.change_state(entity,state)
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name
.. " state change called entity=" .. tostring(entity) .. " state:"
.. dump(state))
--check if custom precondition handler tells us to stop state change
if state ~= nil and
type(state.HANDLER_precondition) == "function" then
if not state.HANDLER_precondition(entity,state) then
dbg_mobf.mob_state_lvl1("MOBF: " .. entity.data.name
.. " custom precondition handler didn't meet ")
--don't assert but switch to default in this case
state = mob_state.get_state_by_name(entity,"default")
end
end
--switch to default state if no state given
if state == nil then
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name
.. " invalid state switch, switching to default instead of: "
.. dump(state))
state = mob_state.get_state_by_name(entity,"default")
end
local entityname = entity.data.name
local statename = state.name
dbg_mobf.mob_state_lvl2("MOBF: " .. entityname .. " switching state to "
.. statename)
if entity.dynamic_data.state == nil then
mobf_bug_warning(LOGLEVEL_WARNING,"MOBF BUG!!! mob_state no state dynamic data")
end
if entity.dynamic_data.state.current.name ~= state.name then
--call leave state handler for old state
if entity.dynamic_data.state.current.HANDLER_leave_state ~= nil and
type(entity.dynamic_data.state.current.HANDLER_leave_state) == "function" then
entity.dynamic_data.state.current.HANDLER_leave_state(entity,state)
end
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name
.. " different states now really changeing to " .. state.name)
mob_state.switch_movgen(entity,state)
entity.dynamic_data.state.time_to_next_change =
mob_state.getTimeToNextState(state.typical_state_time)
entity.dynamic_data.state.current = state
graphics.set_animation(entity,state.animation)
if state.HANDLER_enter_state ~= nil and
type(state.HANDLER_enter_state) == "function" then
state.HANDLER_enter_state(entity)
end
else
dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name
.. " switching to same state as before")
entity.dynamic_data.state.time_to_next_change = mob_state.getTimeToNextState(state.typical_state_time)
if state.HANDLER_enter_state ~= nil and
type(state.HANDLER_enter_state) == "function" then
state.HANDLER_enter_state(entity)
end
end
--update model on each state change
mob_state.switch_model(entity,state)
dbg_mobf.mob_state_lvl2("MOBF: time to next change = "
.. entity.dynamic_data.state.time_to_next_change)
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] getTimeToNextState(typical_state_time)
--
--! @brief helper function to calculate a gauss distributed random value
--! @memberof mob_state
--! @private
--
--! @param typical_state_time center of gauss
--!
--! @return a random value around typical_state_time
-------------------------------------------------------------------------------
function mob_state.getTimeToNextState(typical_state_time)
if typical_state_time == nil then
mobf_bug_warning(LOGLEVEL_WARNING,"MOBF MOB BUG!!! missing typical state time!")
typical_state_time = mob_state.default_state_time
end
local u1 = 2 * math.random() -1
local u2 = 2 * math.random() -1
local q = u1*u1 + u2*u2
local maxtries = 0
while (q == 0 or q >= 1) and maxtries < 10 do
u1 = math.random()
u2 = math.random() * -1
q = u1*u1 + u2*u2
maxtries = maxtries +1
end
--abort random generation
if maxtries >= 10 then
return typical_state_time
end
local p = math.sqrt( (-2*math.log(q))/q )
local retval = 2
--calculate normalized state time with maximum error or half typical time up and down
if math.random() < 0.5 then
retval = typical_state_time + ( u1*p * (typical_state_time/2))
else
retval = typical_state_time + ( u2*p * (typical_state_time/2))
end
-- ensure minimum state time of 2 seconds
if retval > 2 then
return retval
else
return 2
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] prepare_states(mob)
--
--! @brief prepare mobs states upon registration
--! @memberof mob_state
--! @public
--
--! @param mob a mob declaration
-------------------------------------------------------------------------------
function mob_state.prepare_states(mob)
local builtin_state_overrides = {}
--scan for states overriding mobf internal state definitions
if mob.states ~= nil then
for s = 1, #mob.states , 1 do
if mob.states[s].name == "combat" then
builtin_state_overrides["combat"] = true
end
--TODO patrol state
--hunger state
if mob.states[s].name == "RSVD_hunger" then
builtin_state_overrides["RSVD_hunger"] = true
end
end
else
mob.states = {}
end
--add a default combat state if no custom state is defined
if mob.combat ~= nil and builtin_state_overrides["combat"] ~= true then
table.insert(mob.states,
{
name = "combat",
HANDLER_precondition = nil,
movgen = "follow_mov_gen",
typical_state_time = -1,
chance = 0,
})
end
if mob.hunger ~= nil and builtin_state_overrides["RSVD_hunger"] ~= true then
table.insert(mob.states,
{
name = "RSVD_hunger",
HANDLER_precondition = mob_state.BuiltinHungerPrecondition(mob),
HANDLER_leave_state = mob_state.BuiltinHungerLeave(mob),
HANDLER_enter_state = mob_state.BuiltinHungerEnter(mob),
movgen = "mgen_path",
typical_state_time = mob.hunger.typical_walk_time or 45,
chance = mob.hunger.chance or 0.1,
animation = mob.hunger.animation or "walk"
})
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] switch_model(entity,state)
--
--! @brief switch model used for a entity
--! @memberof mob_state
--! @public
--
--! @param entity to switch model
--! @param state to change to
-------------------------------------------------------------------------------
function mob_state.switch_model(entity, state)
local new_graphics = graphics.graphics_by_statename(entity.data, state.name)
if type(new_graphics.texturelist) == "table" then
if entity.dynamic_data.textureidx ~= nil and
entity.dynamic_data.textureidx < #new_graphics.texturelist then
new_graphics.textures = new_graphics.texturelist[entity.dynamic_data.textureidx]
else
new_graphics.textures = new_graphics.texturelist[1]
end
end
local new_props = {
automatic_face_movement_dir = true,
textures = new_graphics.textures
}
if new_graphics.model_orientation_fix ~= nil then
new_props.automatic_face_movement_dir =
(new_graphics.model_orientation_fix / math.pi) * 360 + 90
end
--TODO apply new model too
entity.object:set_properties(new_props)
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] BuiltinHungerPrecondition(mob)
--
--! @brief prepare builtin hunger precondition handler
--! @memberof mob_state
--! @public
--
--! @param mob definition of mob
--! @return function to be called as precondition handler
-------------------------------------------------------------------------------
function mob_state.BuiltinHungerPrecondition(mob)
mobf_assert_backtrace(
(mob.hunger.target_nodes ~= nil) or
(mob.hunger.target_entities ~= nil))
mobf_assert_backtrace(mob.hunger.range ~= nil)
return function(entity,state)
mobf_assert_backtrace(state ~= nil)
mobf_assert_backtrace(state.name == "RSVD_hunger")
local pos = entity.object:getpos()
--mobf_print("MOBF: trying to find " ..
-- dump(mob.hunger.target_nodes) .. " or " ..
-- dump(mob.hunger.target_entities) ..
-- " around: " .. printpos(pos))
local lower_pos = {x=pos.x-mob.hunger.range,
y=pos.y-mob.hunger.range,
z=pos.z-mob.hunger.range}
local upper_pos = {x=pos.x+mob.hunger.range,
y=pos.y+mob.hunger.range,
z=pos.z+mob.hunger.range}
local target_nodes = nil
local target_entities = nil
if mob.hunger.target_nodes ~= nil then
target_nodes = minetest.find_nodes_in_area(lower_pos,
upper_pos,
mob.hunger.target_nodes)
end
if mob.hunger.target_entities ~= nil then
local objectlist = minetest.get_objects_inside_radius(pos,mob.hunger.range)
--mobf_print("MOBF: found: " .. #objectlist .. " objects around")
if objectlist ~= nil and #objectlist > 0 then
target_entities = {}
for i=1,#objectlist,1 do
local luaentity = objectlist[i]:get_luaentity()
if luaentity ~= nil and
mobf_contains(mob.hunger.target_entities,luaentity.name) then
table.insert(target_entities,objectlist[i])
end
end
end
end
local targets = {}
if target_nodes ~= nil then
for i=1,#target_nodes,1 do
table.insert(targets,target_nodes[i])
end
end
if target_entities ~= nil then
for i=1,#target_entities,1 do
table.insert(targets,target_entities[i])
end
end
if targets ~= nil then
dbg_mobf.mob_state_lvl3("MOBF: Hunger found " .. #targets .. " targets")
for i=1,5,1 do
if #targets == 0 then
break
end
local index = math.floor(math.random(1,#targets) + 0.5)
local target = targets[index]
table.remove(targets,index)
--target is a entity
if type(target) == "userdata" then
entity.dynamic_data.hunger = {}
entity.dynamic_data.hunger.target = target
--mobf_print("MOBF: saving hungerdata: " .. dump(entity.dynamic_data.hunger))
return true
else
local targetpos = target
targetpos.y = targetpos.y +1
--if mob is not in air try 1 above for pathfinding
local current_node = minetest.get_node(pos)
if current_node ~= nil and
current_node.name ~= "air" then
pos.y = pos.y+1
end
local path = mobf_path.find_path(pos,targetpos,5,1,1,"A*_noprefetch")
if path ~= nil then
entity.dynamic_data.hunger = {}
entity.dynamic_data.hunger.target = { x=targetpos.x,y=targetpos.y-1,z=targetpos.z}
entity.dynamic_data.hunger.path = path
--mobf_print("MOBF: Found new target: " .. printpos(targetpos) .. " Path: " .. dump(path))
--mobf_print("MOBF: saving hungerdata: " .. dump(entity.dynamic_data.hunger))
return true
else
dbg_mobf.mob_state_lvl2("MOBF: hunger no path to: " .. printpos(targetpos))
end
end
end
end
return false
end --end of handler
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] BuiltinHungerLeave(mob)
--
--! @brief prepare builtin hunger leave handler
--! @memberof mob_state
--! @public
--
--! @param mob definition of mob
--! @return function to be called as leave handler
-------------------------------------------------------------------------------
function mob_state.BuiltinHungerLeave(mob)
return function(entity,state)
--restore old stepheight
entity.object:set_properties({stepheight=entity.dynamic_data.hunger.old_stepheight})
entity.dynamic_data.hunger = nil
p_mov_gen.set_cycle_path(entity,nil)
p_mov_gen.set_path(entity,nil)
p_mov_gen.set_end_of_path_handler(entity,nil)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] BuiltinHungerEnter(mob)
--
--! @brief prepare builtin hunger enter handler
--! @memberof mob_state
--! @public
--
--! @param mob definition of mob
--! @return function to be called as enter handler
-------------------------------------------------------------------------------
function mob_state.BuiltinHungerEnter(mob)
return function(entity)
mobf_assert_backtrace(entity.dynamic_data.state.current.name == "RSVD_hunger")
mobf_assert_backtrace(entity.dynamic_data.hunger ~= nil)
--use stepheight 1 as we did look for a path by using this
entity.dynamic_data.hunger.old_stepheight = entity.stepheight
if (type(entity.dynamic_data.hunger.target) == "userdata") then
if not p_mov_gen.set_target(entity,entity.dynamic_data.hunger.target) then
dbg_mobf.mob_state_lvl1("MOBF: EnterHungerState, failed to set target")
end
else
entity.object:set_properties({stepheight=1})
p_mov_gen.set_path(entity,entity.dynamic_data.hunger.path)
end
--p_mov_gen.set_cycle_path(entity,handler)
p_mov_gen.set_cycle_path(entity,false)
p_mov_gen.set_end_of_path_handler(entity,mob_state.BuiltinHungerTargetReached)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mob_state] BuiltinHungerTargetReached(entity)
--
--! @brief handle target reached
--! @memberof mob_state
--! @public
--
--! @param entity that reached the target
-------------------------------------------------------------------------------
function mob_state.BuiltinHungerTargetReached(entity)
--consume original target
if (entity.dynamic_data.hunger.target ~= nil) and
(entity.data.hunger.keep_food == nil or
entity.data.hunger.keep_food == false) then
if type(entity.dynamic_data.hunger.target) ~= "userdata" then
dbg_mobf.mob_state_lvl2("MOBF: consuming targetnode: " ..
printpos(entity.dynamic_data.hunger.target))
minetest.remove_node(entity.dynamic_data.hunger.target)
else
local targetentity = entity.dynamic_data.hunger.target:get_luaentity()
if targetentity ~= nil and
type(targetentity.mobf_hunger_interface) == "function" then
targetentity.mobf_hunger_interface(entity,"HUNGER_CONSUME")
dbg_mobf.mob_state_lvl2("MOBF: consuming targetentity")
end
end
end
local eating_state = mob_state.get_state_by_name(entity,"eating")
if eating_state ~= nil then
mob_state.change_state(entity,eating_state)
else
local default_state = mob_state.get_state_by_name(entity,"default")
mob_state.change_state(entity,default_state)
end
end

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,110 @@
-------------------------------------------------------------------------------
-- 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 mov_gen_none.lua
--! @brief a dummy movement gen
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mgen_none"))
--! @class mgen_none
--! @brief a movement generator doing nothing
mgen_none = {}
--!@}
--! @brief movement generator identifier
--! @memberof mgen_none
mgen_none.name = "none"
-------------------------------------------------------------------------------
-- name: callback(entity,now)
--
--! @brief main callback to do nothing
--! @memberof mgen_none
--
--! @param entity mob to generate movement for
--! @param now current time
-------------------------------------------------------------------------------
function mgen_none.callback(entity,now)
mobf_assert_backtrace(entity ~= nil)
local pos = entity.getbasepos(entity)
local current_state = environment.pos_is_ok(pos,entity)
if current_state == "in_water" or
current_state == "in_air" or
current_state == "above_water" then
spawning.remove(entity, "mgen none envcheck")
return
end
local speed = entity.object:getvelocity()
local default_y_acceleration = environment.get_default_gravity(pos,
entity.environment.media,
entity.data.movement.canfly)
entity.object:setacceleration({x=0,y=default_y_acceleration,z=0})
if default_y_acceleration ~= 0 then
entity.object:setvelocity({x=0,y=speed.y,z=0})
else
entity.object:setvelocity({x=0,y=0,z=0})
end
end
-------------------------------------------------------------------------------
-- name: initialize()
--
--! @brief initialize movement generator
--! @memberof mgen_none
--! @public
-------------------------------------------------------------------------------
function mgen_none.initialize(entity,now)
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by movement generator
--! @memberof mgen_none
--! @public
--
--! @param entity mob to initialize dynamic data
--! @param now current time
-------------------------------------------------------------------------------
function mgen_none.init_dynamic_data(entity,now)
local data = {
moving = false,
}
entity.dynamic_data.movement = data
end
-------------------------------------------------------------------------------
-- name: set_target(entity,target)
--
--! @brief set target for movgen
--! @memberof mgen_none
--! @public
--
--! @param entity mob to apply to
--! @param target to set
-------------------------------------------------------------------------------
function mgen_none.set_target(entity,target)
return false
end
--register this movement generator
registerMovementGen(mgen_none.name,mgen_none)

View File

@ -0,0 +1,74 @@
-------------------------------------------------------------------------------
-- 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_gen_registry.lua
--! @brief registry for movement generators
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("movement_generators"))
movement_generators = {}
-------------------------------------------------------------------------------
-- name: getMovementGen(id)
--
--! @brief get movement generator specified by id
--! @ingroup framework_int
--
--! @param id id of movementgenerator
--! @return module pointer for movementgenerator
-------------------------------------------------------------------------------
function getMovementGen(id)
if movement_generators[id] == nil then
local name = ""
if id ~= nil then
name = id
end
minetest.log(LOGLEVEL_ERROR,"MOBF: movement generator " .. dump(id) .. " not found!")
return nil
end
return movement_generators[id]
end
-------------------------------------------------------------------------------
-- name: registerMovementGen(name,generator)
--
--! @brief register a movement generator to mob framework
--! @ingroup framework_mob
--
--! @param name id to use for movement generator
--! @param generator pointer to movement generator
--! @return true/false successfully added generator
-------------------------------------------------------------------------------
function registerMovementGen(name,generator)
--some movement gen checks
if generator.init_dynamic_data == nil then
return false
end
if generator.callback == nil then
return false
end
if movement_generators[name] == nil then
movement_generators[name] = generator
minetest.log(LOGLEVEL_NOTICE,"\tRegistering movement generator ".. name)
if generator.initilize ~= nil then
generator.initialize()
end
return true
else
return false
end
end

View File

@ -0,0 +1,226 @@
-------------------------------------------------------------------------------
-- 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(not core.global_exists("movement_generic"))
movement_generic = {}
--!@}
-------------------------------------------------------------------------------
-- @function [parent=#movement_generic] get_accel_to(new_pos,entity,ymovement,accel)
--
--! @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, accel)
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)
local absolute_accel = nil
--find a new speed
if not accel then
absolute_accel = minaccel + (maxaccel - minaccel) * math.random()
else
absolute_accel = accel
end
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

View File

@ -0,0 +1,667 @@
-------------------------------------------------------------------------------
-- Mob Framework Settings 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 path.lua
--! @brief path support for mobf
--! @copyright Sapier
--! @author Sapier
--! @date 2013-02-09
--
-- Contact sapier a t gmx net
-- Boilerplate to support localized strings if intllib mod is installed.
local S
if (minetest.get_modpath("intllib")) then
dofile(minetest.get_modpath("intllib").."/intllib.lua")
S = intllib.Getter(minetest.get_current_modname())
else
S = function ( s ) return s end
end
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mobf_path"))
mobf_path = {}
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] find_path()
--
--! @brief workaround for broken mintest find_path function
--! @ingroup mobf_path
-------------------------------------------------------------------------------
function mobf_path.find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm)
local interim_path = minetest.find_path(pos1, pos2, searchdistance,
max_jump, max_drop, algorithm)
if interim_path == nil then
return nil
end
local pos_n_minor_1 = nil
local pos_n_minor_2 = nil
local path_optimized = {}
table.insert(path_optimized, interim_path[1])
for i,v in ipairs(interim_path) do
if ( pos_n_minor_1 ~= nil ) and ( pos_n_minor_2 ~= nil) then
local x_pitch = pos_n_minor_2.x - v.x
local y_pitch = pos_n_minor_2.y - v.y
local z_pitch = pos_n_minor_2.z - v.z
local x_delta = (pos_n_minor_1.x - pos_n_minor_2.x) / x_pitch;
local y_delta = (pos_n_minor_1.y - pos_n_minor_2.y) / y_pitch;
local z_delta = (pos_n_minor_1.z - pos_n_minor_2.z) / z_pitch;
if (x_pitch ~= 0) and (y_pitch ~= 0) and (x_delta ~= y_delta ) then
table.insert(path_optimized, pos_n_minor_1)
elseif (y_pitch ~= 0) and (z_pitch ~= 0) and (y_delta ~= z_delta) then
table.insert(path_optimized, pos_n_minor_1)
elseif (x_pitch ~= 0) and (z_pitch ~= 0) and (y_delta ~= z_delta) then
table.insert(path_optimized, pos_n_minor_1)
end
end
pos_n_minor_2 = pos_n_minor_1
pos_n_minor_1 = v
end
table.insert(path_optimized, interim_path[#interim_path])
return path_optimized
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] init()
--
--! @brief initialize path subsystem
--! @ingroup mobf_path
-------------------------------------------------------------------------------
function mobf_path.init()
mobf_path.load()
if mobf_rtd.path_data == nil then
mobf_rtd.path_data = {}
end
if mobf_rtd.path_data.users == nil then
mobf_rtd.path_data.users = {}
end
--register path marker entity
minetest.register_entity("mobf:path_marker_entity",
{
physical = false,
collisionbox = {-0.5,-0.5,-0.5,0.5,1.5,0.5 },
visual = "mesh",
textures = { "mobf_path_marker.png" },
mesh = "mobf_path_marker.b3d",
initial_sprite_basepos = {x=0, y=0},
automatic_rotate = 2,
on_step = function(self,dtime)
if self.creationtime == nil then
self.creationtime = 0
end
self.creationtime = self.creationtime + dtime
if self.creationtime > 30 then
self.object:remove()
end
end
})
minetest.register_craftitem(":mobf:path_marker", {
description = S("Path marker tool"),
image = "mobf_path_marker_item.png",
on_place = function(item, placer, pointed_thing)
if pointed_thing.type == "node" then
local pos = pointed_thing.above
local entity =
spawning.spawn_and_check("mobf:path_marker_entity",
pos,"path_marker_click")
if entity ~= nil then
mobf_path.handle_path_marker_place(placer,pos)
end
return item
end
end
})
minetest.register_on_player_receive_fields(mobf_path.button_handler)
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] save()
--
--! @brief save all path data
--! @ingroup mobf_path
-------------------------------------------------------------------------------
function mobf_path.save()
mobf_set_world_setting("mobf_path_data",minetest.serialize(mobf_rtd.path_data))
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] load()
--
--! @brief save all path data
--! @ingroup mobf_path
-------------------------------------------------------------------------------
function mobf_path.load()
local paths_raw = mobf_get_world_setting("mobf_path_data")
if paths_raw ~= nil then
mobf_rtd.path_data = minetest.deserialize(mobf_get_world_setting("mobf_path_data"))
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] handle_path_marker_place(placer,pos)
--
--! @brief initialize path subsystem
--! @ingroup mobf_path
--
--! @param placer player object placing the path marker
--! @param pos position placed at
-------------------------------------------------------------------------------
function mobf_path.handle_path_marker_place(placer,pos)
mobf_assert_backtrace(placer ~= nil)
mobf_assert_backtrace(pos ~= nil)
if placer:is_player() then
local playername = placer:get_player_name()
local player_paths = mobf_path.get_editable_path_names(playername)
mobf_path.show_add_point_menu(playername,player_paths,pos)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] get_editable_path_names(playername)
--
--! @brief get list of pathnames for a player
--! @ingroup mobf_path
--
--! @param playername name of player to get paths for
--
--! @return list of names
-------------------------------------------------------------------------------
function mobf_path.get_editable_path_names(playername)
if mobf_rtd.path_data.users[playername] == nil then
return nil
end
if mobf_rtd.path_data.users[playername].paths == nil then
return nil
end
local retval = {}
for k,v in pairs(mobf_rtd.path_data.users[playername].paths) do
if not v.locked then
table.insert(retval,k)
end
end
if #retval > 0 then
return retval
end
return nil
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] add_point(playername,pathname,point)
--
--! @brief add a point to a path
--! @ingroup mobf_path
--
--! @param playername name of player to store point
--! @param pathname name of path to add point to
--! @param point point to add
-------------------------------------------------------------------------------
function mobf_path.add_point(playername,pathname,point)
if mobf_rtd.path_data.users[playername] == nil then
mobf_rtd.path_data.users[playername] = {}
end
if mobf_rtd.path_data.users[playername].paths == nil then
mobf_rtd.path_data.users[playername].paths = {}
end
if mobf_rtd.path_data.users[playername].paths[pathname] == nil then
mobf_rtd.path_data.users[playername].paths[pathname] = {}
mobf_rtd.path_data.users[playername].paths[pathname].locked = false
mobf_rtd.path_data.users[playername].paths[pathname].points = {}
end
table.insert(mobf_rtd.path_data.users[playername].paths[pathname].points,point)
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] show_add_point_menu(pathnames)
--
--! @brief show a menu containing all paths a point may be added to
--! @ingroup mobf_path
--
--! @param playername player to show menu
--! @param pathnames names of paths
--! @param point point to add
-------------------------------------------------------------------------------
function mobf_path.show_add_point_menu(playername,pathnames,point)
local buttons = ""
local y_pos = 0.25
local storage_id = mobf_global_data_store(point)
if pathnames ~= nil then
for i = 1, #pathnames, 1 do
buttons = buttons .. "button_exit[0," .. y_pos .. ";4.5,0.5;" ..
"mobfpath:existing:" .. storage_id ..
":" .. pathnames[i] .. ";" .. pathnames[i] .. "]"
y_pos = y_pos + 0.75
end
end
local y_size = y_pos + 3 * 0.75 - 0.25
--add new path element
local formspec = "size[4.5," .. y_size .. "]" ..
buttons ..
"label[0," .. y_pos .. ";-----------------------------]" ..
"field[0.25," .. (y_pos + 1) .. ";4.5,0.5;new_path_name;;]" ..
"button_exit[1.5," .. (y_pos + 1.5) .. ";1.5,0.5;mobfpath:addnew:" ..
storage_id .. ";new path]"
--show formspec
minetest.show_formspec(playername,"mobf:path:path_name_menu",formspec)
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] button_handler(player, formname, fields)
--
--! @brief handle button click in mobf_path menu
--! @ingroup mobf_path
--
--! @param player player issuing click
--! @param formname name of form beeing clicked
--! @param fields data submitted to form
--
--! @return true/false event has been handled by this handler
-------------------------------------------------------------------------------
function mobf_path.button_handler(player, formname, fields)
local playername = player:get_player_name()
mobf_assert_backtrace(playername ~= nil)
if formname == "mobf:path:path_name_menu" then
dbg_mobf.path_lvl2("MOBF: Path marker rightclick path selected")
for k,v in pairs(fields) do
local parts = string.split(k,":")
if parts[1] == "mobfpath" then
local point = mobf_global_data_get(parts[3])
local pathname = parts[4]
if parts[2] == "addnew" then
pathname = fields.new_path_name
end
if point ~= nil and
pathname ~= nil and
pathname ~= "" then
mobf_path.add_point(playername,pathname,point)
mobf_path.save()
end
end
end
return true
end
if formname == "mobf:path:add_path_to_entity" then
dbg_mobf.path_lvl2("MOBF: Adding path to an entity")
for k,v in pairs(fields) do
local parts = string.split(k,":")
if parts[1] == "mobfpath" then
local entity = mobf_global_data_get(parts[2])
local pathname = parts[3]
if entity ~= nil and
pathname ~= nil and
mobf_rtd.path_data.users[playername].paths[pathname] ~= nil and
entity.data.patrol ~= nil then
--switch to guard state
mobf_path.switch_patrol(entity,playername,pathname)
end
end
end
return true
end
--not handled by this callback
return false
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] delete_path(ownername,pathname)
--
--! @brief show path markers
--! @ingroup mobf_path
--
--! @param ownername name of path owner
--! @param pathname name of path
--
-------------------------------------------------------------------------------
function mobf_path.delete_path(ownername, pathname)
dbg_mobf.path_lvl1("MOBF: delete path issued: "
.. pathname .. " owner: " .. ownername)
mobf_rtd.path_data.users[ownername].paths[pathname] = nil
mobf_path.save()
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] show_pathmarkers(ownername,pathname)
--
--! @brief show path markers
--! @ingroup mobf_path
--
--! @param ownername name of path owner
--! @param pathname name of path
--
-------------------------------------------------------------------------------
function mobf_path.show_pathmarkers(ownername,pathname)
for i,v in ipairs(mobf_rtd.path_data.users[ownername].paths[pathname].points) do
local objects = minetest.get_objects_inside_radius(v,0.5)
dbg_mobf.path_lvl3("MOBF: got " .. #objects ..
" around pos checking for marker")
local found = false;
for i=1,#objects,1 do
local luaentity = objects[i]:get_luaentity()
dbg_mobf.path_lvl3("MOBF: checking: " .. dump(luaentity))
if luaentity.name == "mobf:path_marker_entity" then
found = true
break
end
end
local node_at = minetest.get_node(v)
if not found and
node_at.name ~= nil and
node_at.name ~= "ignore" then
spawning.spawn_and_check("mobf:path_marker_entity",v,"mark_path")
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] get_pathlist(playername,isadmin)
--
--! @brief get a list of paths for a specific player
--! @ingroup mobf_path
--
--! @param playername name of player to get paths
--! @param isadmin does this player have admin rights?
--
--! @return list of paths
-------------------------------------------------------------------------------
function mobf_path.get_pathlist(playername,isadmin)
local retval = {}
if isadmin then
for local_playername,userdata in pairs(mobf_rtd.path_data.users) do
for pathname,path in pairs(userdata.paths) do
dbg_mobf.path_lvl3("MOBF: Adding path: " .. pathname ..
" data:" .. dump(path))
local toadd = {
ownername = local_playername,
pathname = pathname
}
dbg_mobf.path_lvl3("MOBF: Adding path entry: " .. dump(toadd))
table.insert(retval,toadd)
end
end
else
if playername ~= nil and
mobf_rtd.path_data.users[playername] ~= nil then
for pathname,path in pairs(mobf_rtd.path_data.users[playername].paths) do
local toadd = {
ownername = playername,
pathname = pathname
}
table.insert(retval,toadd)
end
end
end
return retval
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] make_button_name(buttonid,data)
--
--! @brief create a button name
--! @ingroup mobf_path
--
--! @param buttonid id to use for this button
--! @param data information to add to this button
--
--! @return string containing data
-------------------------------------------------------------------------------
function mobf_path.make_button_name(buttonid,data)
local retval = buttonid .. ":"
if data.pathname ~= nil then
retval = retval .. data.pathname .. ":"
else
retval = retval .. ":"
end
if data.ownername ~= nil then
retval = retval .. data.ownername .. ":"
else
retval = retval .. ":"
end
return retval
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] parse_button_name(datastring)
--
--! @brief get data from button name
--! @ingroup mobf_path
--
--! @param datastring name to parse
--
--! @return parsed data
-------------------------------------------------------------------------------
function mobf_path.parse_button_name(datastring)
mobf_assert_backtrace(datastring ~= nil)
local data = {}
local parts = string.split(datastring,":")
data.buttonid = parts[1]
data.pathname = parts[2]
data.ownername = parts[3]
if data.pathname == "" then
data.pathname = nil
data.ownername = nil
end
return data
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] mob_rightclick_callback(entity,player)
--
--! @brief do rightclick action
--! @ingroup mobf_path
--
--! @param entity mobf rightclicked
--! @param player issuing rightclick
-------------------------------------------------------------------------------
function mobf_path.mob_rightclick_callback(entity,player)
local playername = player:get_player_name()
if entity.dynamic_data.spawning.spawner ~= playername then
core.show_formspec(playername,"mobf:path:add_path_to_entity",
"size[4,1]label[0,0;This is not your mob keep away!]" ..
"button_exit[1,0.75;2,0.5;btn_exit;Okay Okay!]")
return
end
if entity.dynamic_data.patrol_state_before ~= nil then
mobf_path.switch_patrol(entity,nil,nil)
else
local buttons = ""
local y_pos = 0.25
local storage_id = mobf_global_data_store(entity)
local playername = player:get_player_name()
local pathlist = mobf_path.get_pathlist(playername,false)
dbg_mobf.path_lvl2("MOBF: Pathlist contains: " .. dump(pathlist))
for i = 1, #pathlist, 1 do
buttons = buttons .. "button_exit[0," .. y_pos .. ";4.5,0.5;" ..
"mobfpath:" .. storage_id ..
":" .. pathlist[i].pathname .. ";" .. pathlist[i].pathname .. "]"
y_pos = y_pos + 0.75
end
local y_size = y_pos - 0.25
local formspec = "size[4.5," .. y_size .. "]" ..
buttons
--show formspec
core.show_formspec(playername,"mobf:path:add_path_to_entity",formspec)
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] config_check(entity)
--
--! @brief check if mob is configured as trader
--! @ingroup mobf_path
--
--! @param entity mob being checked
--! @return true/false if trader or not
-------------------------------------------------------------------------------
function mobf_path.config_check(entity)
if entity.data.patrol ~= nil then
return true
end
return false
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] buttontext(entity)
--
--! @brief return text for rightclick button
--! @ingroup mobf_path
--
--! @param entity to get text for
--! @return buttonname
-------------------------------------------------------------------------------
function mobf_path.buttontext(entity)
if entity.dynamic_data.patrol_state_before == nil then
return "Select path"
end
return "Disable path"
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] switch_patrol(entity,points)
--
--! @brief check if mob is configured as trader
--! @ingroup mobf_path
--
--! @param entity mob being switched
--! @param pathowner owner of path to switch to
--! @param pathname path to switch to
-------------------------------------------------------------------------------
function mobf_path.switch_patrol(entity,pathowner,pathname)
if pathowner ~= nil and
pathname ~= nil and
entity.data.patrol.state ~= nil then
if entity.dynamic_data.patrol_state_before == nil then
if entity.dynamic_data.state.current ~= entity.data.patrol.state then
entity.dynamic_data.patrol_state_before = entity.dynamic_data.state.current
else
entity.dynamic_data.patrol_state_before = "default"
end
mob_state.lock(entity,true)
end
local new_state = mob_state.get_state_by_name(entity,entity.data.patrol.state)
mobf_assert_backtrace(new_state ~= nil)
mob_state.change_state(entity,new_state)
entity.dynamic_data.p_movement.pathowner = pathowner
entity.dynamic_data.p_movement.pathname = pathname
entity.dynamic_data.p_movement.path =
mobf_rtd.path_data.users[pathowner].paths[pathname].points
entity.dynamic_data.p_movement.next_path_index = 1
else
if entity.dynamic_data.patrol_state_before ~= nil then
local new_state = mob_state.get_state_by_name(entity,entity.dynamic_data.patrol_state_before)
if new_state == nil then
new_state = mob_state.get_state_by_name(entity,"default")
end
mobf_assert_backtrace(new_state ~= nil)
mob_state.change_state(entity,new_state)
entity.dynamic_data.patrol_state_before = nil
mob_state.lock(entity,false)
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#mobf_path] getpoints(owner,name)
--
--! @brief get a path by owner and name
--! @ingroup mobf_path
--
--! @param pathowner player owning the path
--! @param pathname name of path
--! @return list of points
-------------------------------------------------------------------------------
function mobf_path.getpoints(pathowner,pathname)
if mobf_rtd.path_data.users[pathowner] == nil then
dbg_mobf.path_lvl2("MOBF: no paths for " .. dump(pathowner) .. " found")
return nil
end
if mobf_rtd.path_data.users[pathowner].paths[pathname] == nil then
dbg_mobf.path_lvl2(
"MOBF: no path " .. dump(pathname) ..
" found for owner " .. pathowner ..
" have paths: " .. dump(mobf_rtd.path_data.users[pathowner].paths))
return nil
end
return mobf_rtd.path_data.users[pathowner].paths[pathname].points
end

View File

@ -0,0 +1,199 @@
-------------------------------------------------------------------------------
-- 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 random_drop.lua
--! @brief component containing random drop features
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup randdrop Random Drop subcomponent
--! @brief Component handling all random drops
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("random_drop"))
--! @class random_drop
--! @brief random drop features e.g lay eggs
--!@}
random_drop = {}
-------------------------------------------------------------------------------
-- @function [parent=#random_drop] callback(entity)
--
--! @brief random drop periodic callback
--! @memberof random_drop
--
--! @param entity mob calling it
--! @param now current time
-------------------------------------------------------------------------------
function random_drop.callback(entity,now)
if entity.data.random_drop ~= nil and
entity.dynamic_data.random_drop ~= nil and
entity.data.random_drop.result ~= "" then
dbg_mobf.random_drop_lvl3("MOBF: random drop for ".. entity.data.name .." is set")
if entity.dynamic_data.random_drop.ts_last_drop + entity.data.random_drop.min_delay < now then
dbg_mobf.random_drop_lvl3("MOBF: enough time passed give drop a chance")
if math.random() < entity.data.random_drop.chance then
entity.dynamic_data.random_drop.ts_last_drop = now
local entitybasepos = entity.getbasepos(entity)
--find pos around
local toput = environment.get_suitable_pos_same_level(entitybasepos,1,entity)
if toput ~= nil then
minetest.add_entity(toput,entity.data.random_drop.result.."_ent")
dbg_mobf.random_drop_lvl3("MOBF: adding random drop for "..entity.data.name .. ": "..entity.data.random_drop.result.."_ent" .. " at " .. printpos(toput))
if entity.data.sound ~= nil then
sound.play(entitybasepos,entity.data.sound.random_drop)
end
else
dbg_mobf.random_drop_lvl2("MOBF: didn't find a place to put random drop for ".. entity.data.name)
end
end
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#random_drop] register_random_drop(random_drop)
--
--! @brief register random drop item and entity
--! @memberof random_drop
--
--! @param random_drop configuration for random drop feature
-------------------------------------------------------------------------------
function random_drop.register(random_drop)
--get basename from random drop item name
local start_pos = 1
local end_pos = string.find(random_drop.result,":")
if end_pos == nil then
return
end
local drop_basename = string.sub(random_drop.result,start_pos,end_pos-1)
local drop_itemname = string.sub(random_drop.result,end_pos+1)
if drop_itemname == nil or
drop_basename == nil then
return
end
minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering random drop entity: "..":"..random_drop.result.."_ent"..
" item="..drop_itemname .. " basename=" .. drop_basename)
local ent_graphics = {}
local id = drop_basename .. "_" .. drop_itemname
if minetest.world_setting_get("mobf_disable_3d_mode") or
animalmaterialsdata[id] == nil or
animalmaterialsdata[id].graphics_3d == nil then
ent_graphics.visual = "sprite"
ent_graphics.textures = {drop_basename .. "_"..drop_itemname..".png"}
ent_graphics.collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5}
else
ent_graphics.visual = animalmaterialsdata[id].graphics_3d.visual
ent_graphics.mesh = animalmaterialsdata[id].graphics_3d.mesh
ent_graphics.textures = animalmaterialsdata[id].graphics_3d.textures
ent_graphics.collisionbox = animalmaterialsdata[id].graphics_3d.collisionbox
ent_graphics.visual_size = animalmaterialsdata[id].graphics_3d.visual_size
end
--Entity
minetest.register_entity(":"..random_drop.result.."_ent",
{
physical = true,
collisionbox = ent_graphics.collisionbox,
visual = ent_graphics.visual,
textures = ent_graphics.textures,
mesh = ent_graphics.mesh,
visual_size = ent_graphics.visual_size,
on_drop_callback= random_drop.on_drop_callback,
on_timeout_callback= random_drop.on_timeout_callback,
on_activate = function(self,staticdata)
self.object:setacceleration({x=0,y=-9.81,z=0})
local now = mobf_get_current_time()
if staticdata == "" then
self.dropped = now
if type(self.on_drop_callback) == "function" then
self:on_drop_callback()
end
else
self.dropped = tonumber(staticdata)
end
if self.dropped + self.random_drop_max_life < now then
dbg_mobf.random_drop_lvl2("MOBF: random drop entity timed out")
self.object:remove()
end
end,
on_punch = function(self, hitter)
hitter:get_inventory():add_item("main", random_drop.result.." 1")
self.object:remove()
end,
on_step = function(self,dtime)
if self.dropped + self.random_drop_max_life < mobf_get_current_time() then
dbg_mobf.random_drop_lvl2("MOBF: random drop entity timed out")
if type(self.on_timeout_callback) == "function" then
self:on_timeout_callback()
end
self.object:remove()
end
end,
get_staticdata = function(self)
return self.dropped
end,
random_drop_max_life = random_drop.min_delay/2,
dropped = 0,
})
end
-------------------------------------------------------------------------------
-- @function [parent=#random_drop] init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by random drop
--! @memberof random_drop
--
--! @param entity mob to add data
--! @param now current time
-------------------------------------------------------------------------------
function random_drop.init_dynamic_data(entity,now)
if entity.data.random_drop ~= nil and
entity.data.random_drop.min_delay > 5 then
entity.dynamic_data.random_drop = {
ts_last_drop = now + math.random(5,entity.data.random_drop.min_delay)
}
else
entity.dynamic_data.random_drop = {
ts_last_drop = now
}
end
end

View File

@ -0,0 +1,321 @@
-------------------------------------------------------------------------------
-- 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 ride.lua
--! @brief class containing mobf functions for riding
--! @copyright Sapier
--! @author Sapier
--! @date 2013-01-06
--
--
--! @defgroup mobf_ride Rideable mobs subcomponent
--! @brief a component containing all functions required to ride a mob
--! @ingroup framework_int
--! @{
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mobf_ride"))
--! @class mobf_ride
--! @brief contains all riding specific functions
--! @}
mobf_ride = {}
------------------------------------------------------------------------------
-- @function [parent=#mobf_ride] attach_player(entity,player)
--
--! @brief make a player ride this mob
--! @class mobf_ride
--! @private
--
--! @param entity entity to be ridden
--! @param player player riding
-------------------------------------------------------------------------------
function mobf_ride.attach_player(entity,player)
entity.dynamic_data.ride.is_attached = true
entity.dynamic_data.ride.player = player
entity.object:setacceleration({x=0,y=-9.81,z=0})
entity.object:setvelocity({x=0,y=-9.81,z=0})
local attacheoffset = {x=0,y=0.5,z=0}
if entity.data.ride ~= nil and
entity.data.ride.attacheoffset ~= nil then
attacheoffset = entity.data.ride.attacheoffset
end
player:set_attach(entity.object,"",attacheoffset, {x=0,y=90,z=0})
-- default always overrides animations even for attached players
-- if type(default.player_set_animation) == "function" then
-- default.player_set_animation(player, "sit")
-- end
if entity.data.ride.texturemod ~= nil then
entity.object:settexturemod(entity.data.ride.texturemod);
end
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_ride] dettach_player(entity,player)
--
--! @brief make a player ride this mob
--! @class mobf_ride
--! @private
--
--! @param entity entity to be ridden
-------------------------------------------------------------------------------
function mobf_ride.dettach_player(entity)
entity.dynamic_data.ride.is_attached = false
entity.dynamic_data.ride.player:set_detach()
entity.dynamic_data.ride.player = nil
entity.object:settexturemod("");
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_ride] on_step_callback(entity)
--
--! @brief make a player ride this mob
--! @class mobf_ride
--! @public
--
--! @param entity entity to be ridden
-------------------------------------------------------------------------------
function mobf_ride.on_step_callback(entity)
if entity.dynamic_data.ride.is_attached then
dbg_mobf.ride_lvl3("MOBF: have attached player")
local walkspeed = 3
local sneakspeed = 0.5
local jumpspeed = 30
local runspeed = walkspeed
if entity.data.ride ~= nil then
if entity.data.ride.walkspeed ~= nil then
walkspeed = entity.data.ride.walkspeed
end
if entity.data.ride.runspeed ~= nil then
runspeed = entity.data.ride.runspeed
end
if entity.data.ride.sneakspeed ~= nil then
sneakspeed = entity.data.ride.sneakspeed
end
if entity.data.ride.jumpspeed ~= nil then
jumpspeed = entity.data.ride.jumpspeed
end
end
local dir = entity.dynamic_data.ride.player:get_look_yaw()
local current_speed = entity.object:getacceleration()
local speed_to_set = {x=0,y=current_speed.y,z=0}
if dir ~= nil then
local playerctrl = entity.dynamic_data.ride.player:get_player_control()
if playerctrl ~= nil then
local setspeed = false
if playerctrl.jump and
entity.is_on_ground(entity) then
speed_to_set.y = jumpspeed
setspeed = true
end
--just set speed to playerview direction
if playerctrl.up then
setspeed = true
end
--invert playerview direction
if playerctrl.down then
dir = dir + math.pi
setspeed = true
end
if playerctrl.left then
if playerctrl.up then
dir = dir + math.pi/4
elseif playerctrl.down then
dir = dir - math.pi/4
else
dir = dir + math.pi/2
end
setspeed = true
end
if playerctrl.right then
if playerctrl.up then
dir = dir - math.pi/4
elseif playerctrl.down then
dir = dir + math.pi/4
else
dir = dir - math.pi/2
end
setspeed = true
end
local selected_speed = walkspeed
if playerctrl.sneak then
selected_speed = sneakspeed
end
if setspeed then
local speed_to_set_xz = mobf_calc_vector_components(dir,selected_speed)
speed_to_set.x = speed_to_set_xz.x
speed_to_set.z = speed_to_set_xz.z
if entity.data.ride.walk_anim ~= nil then
graphics.set_animation(entity,entity.data.ride.walk_anim)
end
else
if entity.data.ride.walk_anim ~= nil then
if entity.data.ride.stand_anim ~= nil then
graphics.set_animation(entity,entity.data.ride.stand_anim)
mob_state.change_state(entity,mob_state.get_state_by_name(entity,entity.data.ride.state_stand))
else
graphics.set_animation(entity,"stand")
end
end
end
entity.object:setvelocity(speed_to_set)
--fix switched model orientation
graphics.setyaw(entity,dir)
end
end
return true
else
return false
end
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_ride] on_punch_callback(entity,player)
--
--! @brief make a player ride this mob
--! @class mobf_ride
--! @public
--
--! @param entity entity to be ridden
--! @param player player riding
-------------------------------------------------------------------------------
function mobf_ride.on_punch_callback(entity,player)
dbg_mobf.ride_lvl2("MOBF: ride on punch callback")
print("MOBF: ride on punch callback")
local saddle = "animalmaterials:saddle"
if entity.data.ride.saddle ~= nil then
saddle = entity.data.ride.saddle
end
--detache
if entity.dynamic_data.ride.is_attached ~= false then
dbg_mobf.ride_lvl2("MOBF: punched ridden mob")
if entity.dynamic_data.ride.player == player then
dbg_mobf.ride_lvl2("MOBF: detaching player")
mobf_ride.dettach_player(entity)
player:get_inventory():add_item("main",saddle .. " 1")
return true
end
else
--check if player has saddle
dbg_mobf.ride_lvl2("MOBF: punched free mob")
if player:get_wielded_item():get_name() == saddle then
dbg_mobf.ride_lvl2("MOBF: punching with saddle")
if player:get_inventory():contains_item("main",saddle .. " 1") then
dbg_mobf.ride_lvl2("MOBF: have saddle")
mobf_ride.attach_player(entity,player)
player:get_inventory():remove_item("main",saddle .. " 1")
return true
end
else
dbg_mobf.ride_lvl2("MOBF: not punching with saddle but: " .. player:get_wielded_item():get_name())
end
end
return false
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_ride] is_enabled(entity)
--
--! @brief check if riding is enabled for a mob
--! @class mobf_ride
--! @public
--
--! @param entity entity to be ridden
-------------------------------------------------------------------------------
function mobf_ride.is_enabled(entity)
if entity.data.ride ~= nil then
return true
end
dbg_mobf.ride_lvl2("riding of " .. entity.data.name .. " is disabled")
return false
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_ride] init(entity)
--
--! @brief initialize ride dynamic data
--! @class mobf_ride
--! @public
--
--! @param entity entity to be ridden
-------------------------------------------------------------------------------
function mobf_ride.init(entity)
local data = {
is_attached = false,
player = nil,
}
entity.dynamic_data.ride = data
end
-- special handler on leave
minetest.register_on_leaveplayer( function(player)
if player ~= nil and
player.object ~= nil then
local pos = player.object:getpos()
--print("MOBF: got player position: " ..printpos(pos) )
if pos ~= nil then
local objects = get_objects_inside_radius(pos, 5)
print("MOBF: found " .. dump(#objects) .. " objects around player")
if objects ~= nil then
for i=1,#objects,1 do
local entity = objects[i]:get_luaentity()
if entity ~= nil and
entity.dynamic_data ~= nil and
entity.dynamic_data.ride ~= nil and
entity.dynamic_data.ride.player == player then
print("MOBF: found player to be attached")
ride.dettach_player(entity)
break
end
end
end
end
end
end)

View File

@ -0,0 +1,205 @@
-------------------------------------------------------------------------------
-- 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 sound.lua
--! @brief component containing sound related functions
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup grp_sound Sound subcomponent
--! @brief Component handling all sound related actions
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("sound"))
--! @class sound
--! @brief sound selection and play functions
--!@}
sound = {}
-------------------------------------------------------------------------------
-- @function [parent=#sound] play(entity)
--
--! @brief play a sound at a specified position
--! @memberof sound
--
--! @param param1 position to play sound at or playername to play sound for
--! @param soundspec sound to play
-------------------------------------------------------------------------------
function sound.play(param1, soundspec)
local pos = nil
local playername = nil
if type(param1) == "string" then
playername = param1
else
pos = param1
end
if (soundspec ~= nil) then
local toplay = {
gain = soundspec.gain,
pos = pos,
to_player = playername,
max_hear_distance = soundspec.max_hear_distance,
loop = false,
}
minetest.sound_play(soundspec.name,toplay)
else
dbg_mobf.sound_lvl2("MOBF: no soundspec")
--todo add log entry
end
end
-------------------------------------------------------------------------------
-- @function [parent=#sound] play_random(entity,now)
--
--! @brief play a random sound for mob
--! @memberof sound
--
--! @param entity mob to do action
--! @param now current time
-------------------------------------------------------------------------------
function sound.play_random(entity,now)
if entity.dynamic_data == nil or
entity.dynamic_data.sound == nil then
mobf_bug_warning(LOGLEVEL_ERROR,
"MOBF BUG!!!: >" ..entity.data.name .. "< removed=" ..
dump(entity.removed) .. " entity=" .. tostring(entity) ..
" sound callback without dynamic data")
return
end
if entity.data.sound ~= nil and
entity.data.sound.random ~= nil then
-- check for old style random sound definition using chance + min_delta
if entity.data.sound.random.min_delta ~= nil and
entity.dynamic_data.sound.random_last
+ entity.data.sound.random.min_delta > now then
return
end
-- check for old style random sound definition using chance
if entity.data.sound.random.chance ~= nil and
math.random() > entity.data.sound.random.chance then
return
end
-- check for new style sounds done by gauss distribution
if entity.dynamic_data.sound.random_next ~= nil and
entity.dynamic_data.sound.random_next > now then
return
end
-- init variable to be passed to play
local toplay = nil
-- sound list mode
if entity.data.sound.random.list ~= nil then
-- select random sound from list
local current_random_sound =
math.floor(math.random(1,#entity.data.sound.random.list) + 0.5)
toplay = entity.data.sound.random.list[current_random_sound]
dbg_mobf.sound_lvl3("MOBF: selected random sound: " ..
current_random_sound .. "/" .. #entity.data.sound.random.list)
-- single sound mode
elseif entity.data.sound.random.name ~= nil then
toplay = entity.data.sound.random
else
dbg_mobf.sound_lvl1("MOBF: invalid random sound configuration")
end
if toplay ~= nil then
sound.play(entity.object:getpos(),toplay)
entity.dynamic_data.sound.random_last = now
dbg_mobf.sound_lvl3("MOBF: playing sound: " .. toplay.name)
end
if entity.dynamic_data.sound.random_next ~= nil then
if entity.data.sound.random.interval == nil or
entity.data.sound.random.max_interval_deviation == nil then
dbg_mobf.sound_lvl1("MOBF: invalid random sound configuration," ..
" missing \"interval\" or \"max_interval_deviation\"")
return
end
local delta_next = sound.calc_random_sound_delay(entity)
dbg_mobf.sound_lvl1("MOBF: next random sound in: " .. delta_next .. "s")
entity.dynamic_data.sound.random_next = now + delta_next
else
entity.dynamic_data.sound.random_last = now
end
end
end
-------------------------------------------------------------------------------
-- @function [parent=#sound] sound.calc_random_sound_delay(entity)
--
--! @brief calculate delay for random sound
--! @memberof sound
--
--! @param entity mob to calc for
-------------------------------------------------------------------------------
function sound.calc_random_sound_delay(entity)
local base_value = mobf_gauss(entity.data.sound.random.interval,
entity.data.sound.random.max_interval_deviation/20)
local delta_next = math.max(entity.data.sound.random.interval -
entity.data.sound.random.max_interval_deviation,
base_value)
delta_next = math.min(delta_next,
entity.data.sound.random.interval +
entity.data.sound.random.max_interval_deviation)
return delta_next
end
-------------------------------------------------------------------------------
-- @function [parent=#sound] sound.init_dynamic_data(entity, now)
--
--! @brief initialize all dynamic data for sound on activate
--! @memberof sound
--
--! @param entity mob to initialize
--! @param now current time
-------------------------------------------------------------------------------
function sound.init_dynamic_data(entity, now)
local data = {
random_last = now,
}
if entity.data.sound.random ~= nil and
entity.data.sound.random.interval ~= nil and
entity.data.sound.random.max_interval_deviation then
local delta_next = sound.calc_random_sound_delay(entity)
data.random_next = delta_next + now
dbg_mobf.sound_lvl2("MOBF: initalizing random_next to: " .. delta_next
.. " Interval: " .. entity.data.sound.random.interval .. " Deviation: "
.. entity.data.sound.random.max_interval_deviation)
elseif entity.data.sound.random ~= nil and
entity.data.sound.random.chance == nil then
dbg_mobf.sound_lvl1("MOBF: invalid random sound definition for \"" ..
entity.data.name .. "\" ... neither invterval nor probability mode configured")
end
entity.dynamic_data.sound = data
end

View File

@ -0,0 +1,661 @@
-------------------------------------------------------------------------------
-- 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 spawning.lua
--! @brief component containing spawning features
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup spawning Spawn mechanisms
--! @brief all functions and variables required for automatic mob spawning
--! @ingroup framework_int
--! @{
--
--! @defgroup spawn_algorithms (DEPRECATED) Spawn algorithms
--! @brief spawn algorithms provided by previous mob framework versions. New
--! mobs are strongly encouraged to use advanced spawning mob as spawning will
--! be removed from mobf as of version 2.5
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("spawning"))
--! @class spawning
--! @brief spawning features
spawning = {}
--!@}
mobf_assert_backtrace(not core.global_exists("mobf_spawn_algorithms"))
--! @brief registry for spawn algorithms
--! @memberof spawning
--! @private
mobf_spawn_algorithms = {}
-------------------------------------------------------------------------------
-- name: init()
-- @function [parent=#spawning] init
--
--! @brief initialize spawning data
--! @memberof spawning
--
-------------------------------------------------------------------------------
function spawning.init()
--read from file
local world_path = minetest.get_worldpath()
local file,error = io.open(world_path .. "/mobf_spawning_data","r")
if file ~= nil then
local data_raw = file:read("*a")
file:close()
if data_raw ~= nil then
spawning.mob_spawn_data = minetest.deserialize(data_raw)
end
end
if spawning.mob_spawn_data == nil then
spawning.mob_spawn_data = {}
end
--register spawndata persistent storer to globalstep
minetest.after(300,spawning.preserve_spawn_data,true)
--register cleanup handler
minetest.register_on_shutdown(function(dstep) spawning.preserve_spawn_data(false) end)
end
-------------------------------------------------------------------------------
-- name: preserve_spawn_data()
-- @function [parent=#spawning] preserve_spawn_data
--
--! @brief save data on regular base
--! @memberof spawning
--
--! @param cyclic if this is true spawn data is saved in cyclic intervals
-------------------------------------------------------------------------------
function spawning.preserve_spawn_data(cyclic)
local world_path = minetest.get_worldpath()
local file,error = io.open(world_path .. "/mobf_spawning_data","w")
if error ~= nil then
minetest.log(LOGLEVEL_ERROR,"MOBF: failed to spawning preserve file")
end
mobf_assert_backtrace(file ~= nil)
local serialized_data = minetest.serialize(spawning.mob_spawn_data)
file:write(serialized_data)
if cyclic then
minetest.after(300,spawning.preserve_spawn_data,cyclic)
end
end
-------------------------------------------------------------------------------
-- name: total_offline_mobs()
-- @function [parent=#spawning] total_offline_mobs
--
--! @brief count total number of offline mobs
--! @memberof spawning
--
--! @return number of mobs
-------------------------------------------------------------------------------
function spawning.total_offline_mobs()
local count = 0
for key,value in pairs(spawning.mob_spawn_data) do
for hash,v in pairs(value) do
count = count +1
end
end
return count
end
-------------------------------------------------------------------------------
-- name: count_deactivated_mobs(name,pos,range)
-- @function [parent=#spawning] count_deactivated_mobs
--
--! @brief count number of mobs of specific type within a certain range
--! @memberof spawning
--
--! @param name name of mob to count
--! @param pos to check distance to
--! @param range to check
--
--! @return number of mobs
-------------------------------------------------------------------------------
function spawning.count_deactivated_mobs(name,pos,range)
local count = 0
if spawning.mob_spawn_data[name] ~= nil then
for hash,v in pairs(spawning.mob_spawn_data[name]) do
local mobpos = mobf_hash_to_pos(hash)
local distance = vector.distance(pos,mobpos)
if distance < range then
local node = core.get_node(mobpos)
local notfound = true
-- if we are within active object range and
-- that position is loaded check if there's really a mob at that location
if node.name ~= "ignore" and distance < 32 then
local found = false
local objects_around = core.get_objects_inside_radius(mobpos, 1)
if objects_around and #objects_around > 0 then
for i,v in ipairs(objects_around) do
local luaentity = v:get_luaentity()
if luaentity ~= nil then
if luaentity.data ~= nil and
luaentity.data.name == name then
found = true
break
end
end
end
end
if not found then
dbg_mobf.spawning_lvl2(
"MOBF: clearing stall deactivated entry at: " ..
core.pos_to_string(mobpos))
notfound = false
spawning.mob_spawn_data[name][hash] = nil
end
end
if notfound then
count = count +1
end
end
end
end
return count
end
-------------------------------------------------------------------------------
-- name: deactivate_mob(entity)
-- @function [parent=#spawning] deactivate_mob
--
--! @brief add mob to deactivated list
--! @memberof spawning
--
--! @param name name of mob to be deactivated
--! @param pos position to deactivate mob at
-------------------------------------------------------------------------------
function spawning.deactivate_mob(name,pos)
if spawning.mob_spawn_data[name] == nil then
spawning.mob_spawn_data[name] = {}
end
local rounded_pos = vector.round(pos)
local hash = minetest.hash_node_position(rounded_pos)
--assert (mobf_pos_is_same(mobf_hash_to_pos(hash),rounded_pos))
spawning.mob_spawn_data[name][hash] = true
end
-------------------------------------------------------------------------------
-- name: activate_mob(name,pos)
-- @function [parent=#spawning] preserve_spawn_data
--
--! @brief save data on regular base
--! @memberof spawning
--
--! @param name name of mob to be activated
--! @param pos position to activate mob at
-------------------------------------------------------------------------------
function spawning.activate_mob(name,pos)
if spawning.mob_spawn_data[name] ~= nil then
local rounded_pos = vector.round(pos)
local hash = minetest.hash_node_position(rounded_pos)
--assert(mobf_pos_is_same(mobf_hash_to_pos(hash),rounded_pos))
spawning.mob_spawn_data[name][hash] = nil
end
end
-------------------------------------------------------------------------------
-- name: remove_uninitialized(entity,staticdata)
-- @function [parent=#spawning] remove_uninitialized
--
--! @brief remove a spawn point based uppon staticdata supplied
--! @memberof spawning
--
--! @param entity to remove
--! @param staticdata of mob
-------------------------------------------------------------------------------
function spawning.remove_uninitialized(entity, staticdata)
--entity may be known in spawnlist
if staticdata ~= nil then
local permanent_data = mobf_deserialize_permanent_entity_data(staticdata)
if (permanent_data.spawnpoint ~= nil) then
--prepare information required to remove entity
entity.dynamic_data = {}
entity.dynamic_data.spawning = {}
entity.dynamic_data.spawning.spawnpoint = permanent_data.spawnpoint
entity.dynamic_data.spawning.player_spawned = permanent_data.playerspawned
entity.dynamic_data.spawning.spawner = permanent_data.spawner
spawning.remove(entity,"remove uninitialized")
end
else
dbg_mobf.spawning_lvl1("MOBF: remove uninitialized entity=" .. tostring(entity))
--directly remove it can't be known to spawnlist
entity.object:remove()
end
end
-------------------------------------------------------------------------------
-- name: remove(entity)
-- @function [parent=#spawning] remove
--
--! @brief remove a mob
--! @memberof spawning
--
--! @param entity mob to remove
--! @param reason text to log as reason for removal
-------------------------------------------------------------------------------
function spawning.remove(entity,reason)
local pos = entity.object:getpos()
dbg_mobf.spawning_lvl3("MOBF: --> remove " .. printpos(pos))
if entity ~= nil then
entity.removed = true
dbg_mobf.spawning_lvl1("MOBF: remove entity=" .. tostring(entity))
if minetest.world_setting_get("mobf_log_removed_entities") then
if reason == nil then
reason = "unknown"
end
minetest.log(LOGLEVEL_NOTICE,"MOBF: removing " .. entity.data.name ..
" at " .. printpos(pos) .. " due to: " .. reason)
end
mobf.preserve_removed(entity,reason)
if entity.lifebar ~= nil then
mobf_lifebar.del(entity.lifebar)
end
entity.object:remove()
else
minetest.log(LOGLEVEL_ERROR,"Trying to delete an an non existant mob")
end
dbg_mobf.spawning_lvl3("MOBF: <-- remove")
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity)
-- @function [parent=#spawning] init_dynamic_data
--
--! @brief initialize dynamic data required for spawning
--! @memberof spawning
--
--! @param entity mob to initialize dynamic data
--! @param now current time
-------------------------------------------------------------------------------
function spawning.init_dynamic_data(entity,now)
local player_spawned = false
if entity.dynamic_data.spawning ~= nil and
entity.dynamic_data.spawning.player_spawned then
player_spawned = true
end
local data = {
player_spawned = player_spawned,
ts_dense_check = now,
spawnpoint = entity.object:getpos(),
original_spawntime = now,
spawner = nil,
density = spawning.population_density_get_min(entity),
}
entity.removed = false
entity.dynamic_data.spawning = data
end
-------------------------------------------------------------------------------
-- name: population_density_check(mob)
-- @function [parent=#spawning] population_density_check
--
--! @brief check and fix if there are too many mobs within a specific range
--! @memberof spawning
--
--! @param entity mob to check
--! @param now current time
-------------------------------------------------------------------------------
function spawning.population_density_check(entity,now)
if entity == nil or
entity.dynamic_data == nil or
entity.dynamic_data.spawning == nil then
mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!! " .. entity.data.name ..
" pop dense check called for entity with missing spawn data entity=" ..
tostring(entity))
return false
end
--only check every 5 seconds
if entity.dynamic_data.spawning.ts_dense_check + 5 > now then
return true
end
-- don't check if mob is player spawned
if entity.dynamic_data.spawning.player_spawned == true then
dbg_mobf.spawning_lvl1("MOBF: mob is player spawned skipping pop dense check")
return true
end
--don't do population check while fighting
if entity.dynamic_data.combat ~= nil and
entity.dynamic_data.combat.target ~= nil and
entity.dynamic_data.combat.target ~= "" then
dbg_mobf.spawning_lvl1(
"MOBF: fighting right now skipping pop dense check: " ..
dump(entity.dynamic_data.combat.target))
return true
end
entity.dynamic_data.spawning.ts_dense_check = now
local entitypos = mobf_round_pos(entity.object:getpos())
--mob either not initialized completely or a bug
if mobf_pos_is_zero(entitypos) then
dbg_mobf.spawning_lvl1("MOBF: can't do a sane check")
return true
end
local secondary_name = ""
if entity.data.harvest ~= nil then
secondary_name = entity.data.harvest.transform_to
end
local check_density = entity.dynamic_data.spawning.density
if entity.data.generic.population_density ~= nil then
check_density = entity.data.generic.population_density
end
mobf_assert_backtrace(check_density ~= nil)
local mob_count = mobf_mob_around(entity.data.modname..":"..entity.data.name,
secondary_name,
entitypos,
check_density,
true)
if mob_count > 5 then
dbg_mobf.spawning_lvl1("MOBF: " .. entity.data.name ..
mob_count .. " mobs of same type around")
entity.removed = true
minetest.log(LOGLEVEL_INFO,"MOBF: Too many ".. mob_count .. " "..
entity.data.name.." at one place dying: " ..
tostring(entity.dynamic_data.spawning.player_spawned))
spawning.remove(entity, "population density check")
return false
else
dbg_mobf.spawning_lvl2("MOBF: " .. entity.data.name ..
" density ok only "..mob_count.." mobs around")
return true
end
end
-------------------------------------------------------------------------------
-- name: replace_entity(pos,name,spawnpos,health)
-- @function [parent=#spawning] replace_entity
--
--! @brief replace mob at a specific position by a new one
--! @memberof spawning
--
--! @param entity mob to replace
--! @param name of the mob to add
--! @param preserve preserve original spawntime
--! @return entity added or nil on error
-------------------------------------------------------------------------------
function spawning.replace_entity(entity,name,preserve)
dbg_mobf.spawning_lvl3("MOBF: --> replace_entity("
.. entity.data.name .. "|" .. name .. ")")
if minetest.registered_entities[name] == nil then
minetest.log(LOGLEVEL_ERROR,"MOBF: replace_entity: Bug no "
..name.." is registred")
return nil
end
-- avoid switching to same entity
if entity.name == name then
minetest.log(LOGLEVEL_INFO,"MOBF: not replacing " .. name ..
" by entity of same type!")
return nil
end
-- get data to be transfered to new entity
local pos = mobf.get_basepos(entity)
local health = entity.object:get_hp()
local temporary_dynamic_data = entity.dynamic_data
local entity_orientation = graphics.getyaw(entity)
if preserve == nil or preserve == false then
temporary_dynamic_data.spawning.original_spawntime = mobf_get_current_time()
end
--calculate new y pos
if minetest.registered_entities[name].collisionbox ~= nil then
pos.y = pos.y - minetest.registered_entities[name].collisionbox[2]
end
--delete current mob
dbg_mobf.spawning_lvl2("MOBF: replace_entity: removing " .. entity.data.name)
--unlink dynamic data (this should work but doesn't due to other bugs)
entity.dynamic_data = nil
--removing is done after exiting lua!
spawning.remove(entity,"replaced")
--set marker to true to make sure activate handler knows it's replacing right now
spawning.replacing_NOW = true
local newobject = minetest.add_entity(pos,name)
spawning.replacing_NOW = false
local newentity = mobf_find_entity(newobject)
if newentity ~= nil then
if newentity.dynamic_data ~= nil then
dbg_mobf.spawning_lvl2("MOBF: replace_entity: " .. name)
newentity.dynamic_data = temporary_dynamic_data
newentity.object:set_hp(health)
graphics.setyaw(newentity, entity_orientation)
else
minetest.log(LOGLEVEL_ERROR,
"MOBF: replace_entity: dynamic data not set for "..name..
" maybe delayed activation?")
newentity.dyndata_delayed = {
data = temporary_dynamic_data,
health = health,
orientation = entity_orientation
}
end
else
minetest.log(LOGLEVEL_ERROR,
"MOBF: replace_entity 4 : Bug no "..name.." has been created")
end
dbg_mobf.spawning_lvl3("MOBF: <-- replace_entity")
return newentity
end
------------------------------------------------------------------------------
-- name: lifecycle_callback()
-- @function [parent=#spawning] lifecycle_callback
--
--! @brief check mob lifecycle_callback
--! @memberof spawning
--
--! @return true/false still alive dead
-------------------------------------------------------------------------------
function spawning.lifecycle_callback(entity,now)
if entity.dynamic_data.spawning.original_spawntime ~= nil then
if entity.data.spawning ~= nil and
entity.data.spawning.lifetime ~= nil then
local lifetime = entity.data.spawning.lifetime
local current_age = now - entity.dynamic_data.spawning.original_spawntime
if current_age > 0 and
current_age > lifetime then
dbg_mobf.spawning_lvl1("MOBF: removing animal due to limited lifetime")
spawning.remove(entity," limited mob lifetime")
return false
end
end
else
entity.dynamic_data.spawning.original_spawntime = now
end
return true
end
------------------------------------------------------------------------------
-- name: spawn_and_check(name,pos,text)
-- @function [parent=#spawning] spawn_and_check
--
--! @brief spawn an entity and check for presence
--! @memberof spawning
--! @param name name of entity
--! @param pos position to spawn mob at
--! @param text message used for log messages
--
--! @return spawned mob entity
-------------------------------------------------------------------------------
function spawning.spawn_and_check(name,pos,text)
mobf_assert_validpos(pos)
mobf_assert_backtrace(name ~= nil)
local newobject = minetest.add_entity(pos,name)
if newobject then
local newentity = mobf_find_entity(newobject)
if newentity == nil then
dbg_mobf.spawning_lvl3("MOBF BUG!!! no " .. name ..
" entity has been created by " .. text .. "!")
mobf_bug_warning(LOGLEVEL_ERROR,"BUG!!! no " .. name ..
" entity has been created by " .. text .. "!")
else
dbg_mobf.spawning_lvl2("MOBF: spawning "..name ..
" entity by " .. text .. " at position ".. printpos(pos))
minetest.log(LOGLEVEL_INFO,"MOBF: spawning "..name ..
" entity by " .. text .. " at position ".. printpos(pos))
return newentity
end
else
dbg_mobf.spawning_lvl3("MOBF BUG!!! no "..name..
" object has been created by " .. text .. "!")
mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!! no "..name..
" object has been created by " .. text .. "!")
end
return nil
end
------------------------------------------------------------------------------
-- name: population_density_get_min(entity)
-- @function [parent=#spawning] population_density_get_min
--
--! @brief get minimum density for this mob
--! @memberof spawning
--
--! @param entity the mob itself
--
--! @return minimum density over all spawners defined for this mob
-------------------------------------------------------------------------------
function spawning.population_density_get_min(entity)
if entity.data.spawning == nil then
return entity.data.generic.population_density
end
-- legacy code
if type(entity.data.spawning.primary_algorithms) == "table" then
local density = nil
for i=1 , #entity.data.spawning.primary_algorithms , 1 do
if density == nil or
entity.data.spawning.primary_algorithms[i].density < density then
density = entity.data.spawning.primary_algorithms[i].density
end
end
return density
else
return entity.data.spawning.density
end
end
-------------------------------------------------------------------------------
-- name: check_activation_overlap(entity,pos,preserved_data)
--
--! @brief check if a activating entity is spawned within some other entity
--
--! @param entity entity to check
--! @param pos position spawned at
--! @param preserved_data data loaded for entity
--! @return true
-------------------------------------------------------------------------------
function spawning.check_activation_overlap(entity,pos,preserved_data)
local cleaned_objectcount = mobf_objects_around(pos,0.25,{ "mobf:lifebar" })
--honor replaced marker
if (entity.replaced ~= true and cleaned_objectcount > 1) or
cleaned_objectcount > 2 then
------------------------------
-- debug output only
-- ---------------------------
local spawner = "unknown"
if preserved_data ~= nil and
preserved_data.spawner ~= nil then
spawner = preserved_data.spawner
mobf_bug_warning(LOGLEVEL_WARNING,
"MOBF: trying to activate mob \"" ..entity.data.name ..
" at " .. printpos(pos) .. " (" .. tostring(entity)
.. ")"..
"\" within something else!" ..
" originaly spawned by: " .. spawner ..
" --> removing")
objectlist = minetest.get_objects_inside_radius(pos,0.25)
for i=1,#objectlist,1 do
local luaentity = objectlist[i]:get_luaentity()
if luaentity ~= nil then
if luaentity.data ~= nil and
luaentity.data.name ~= nil then
dbg_mobf.mobf_core_helper_lvl3(
i .. " LE: " .. luaentity.name .. " (" .. tostring(luaentity) .. ") " ..
luaentity.data.name .. " " ..
printpos(objectlist[i]:getpos()))
else
dbg_mobf.mobf_core_helper_lvl3(
i .. " LE: " .. luaentity.name .. " (" .. tostring(luaentity) .. ") " ..
dump(luaentity))
end
else
dbg_mobf.mobf_core_helper_lvl3(
i .. " " .. tostring(objectlist[i]) ..
printpos(objectlist[i]:getpos()))
end
end
------------------------------
-- end debug output
-- ---------------------------
return false
end
end
return true
end

View File

@ -0,0 +1,98 @@
-------------------------------------------------------------------------------
-- 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 step_quota.lua
--! @brief class containing mobf step quota handling
--! @copyright Sapier
--! @author Sapier
--! @date 2013-11-30
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(not core.global_exists("mobf_step_quota"))
--! @class mobf_step_quota
--! @brief step_quota handling
mobf_step_quota = {}
------------------------------------------------------------------------------
-- @function [parent=#mobf_step_quota] is_exceeded
--
--! @brief check if quota is exceeded
--! @memberof mobf_step_quota
--! @public
--
--! @return true == exceeded false == not exceeded
-------------------------------------------------------------------------------
function mobf_step_quota.is_exceeded()
return mobf_step_quota.remaining_quota <= 0
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_step_quota] remaining()
--
--! @brief get remaining time this quota
--! @memberof mobf_step_quota
--! @public
--
--! @return time left
-------------------------------------------------------------------------------
function mobf_step_quota.remaining()
if mobf_step_quota.remaining_quota >= 0 then
return mobf_step_quota.remaining_quota
else
return 0
end
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_step_quota] initialize()
--
--! @brief initialize quota handling
--! @memberof mobf_step_quota
--! @public
--
-------------------------------------------------------------------------------
function mobf_step_quota.initialize()
--todo add setting
mobf_step_quota.reload_value = 50
minetest.register_globalstep(mobf_step_quota.reload)
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_step_quota] reload()
--
--! @brief reload current quota
--! @memberof mobf_step_quota
--! @public
--
-------------------------------------------------------------------------------
function mobf_step_quota.reload()
mobf_step_quota.remaining_quota = mobf_step_quota.reload_value
end
------------------------------------------------------------------------------
-- @function [parent=#mobf_step_quota] cosume(starttime)
--
--! @brief reduce remaining quota by time passed
--! @memberof mobf_step_quota
--! @public
--
--! @param starttime time this operation started
-------------------------------------------------------------------------------
function mobf_step_quota.consume(starttime)
local now = mobf_get_time_ms()
local passed = now - starttime
if passed >= 0 then
mobf_step_quota.remaining_quota = mobf_step_quota.remaining_quota - passed
else
--mobf_print("MOBF: error calculation consumed time: " .. starttime .. " --> " .. now)
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

View File

@ -0,0 +1,103 @@
-------------------------------------------------------------------------------
-- 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 data_storage.lua
--! @brief generic functions used in many different places
--! @copyright Sapier
--! @author Sapier
--! @date 2013-02-04
--!
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @defgroup gen_func Generic functions
--! @brief functions for various tasks
--! @ingroup framework_int
--! @{
-------------------------------------------------------------------------------
-- name: mobf_global_data_store(value)
--
--! @brief save data and return unique identifier
--
--! @param value to save
--
--! @return unique identifier
-------------------------------------------------------------------------------
local mobf_global_data_identifier = 0
local mobf_global_data = {}
mobf_global_data.cleanup_index = 0
mobf_global_data.last_cleanup = mobf_get_current_time()
function mobf_global_data_store(value)
local current_id = mobf_global_data_identifier
mobf_global_data_identifier = mobf_global_data_identifier + 1
mobf_global_data[current_id] = {
value = value,
added = mobf_get_current_time(),
}
return current_id
end
-------------------------------------------------------------------------------
-- name: mobf_global_data_store(value)
--
--! @brief pop data from global store
--
--! @param id to pop
--
--! @return stored value
-------------------------------------------------------------------------------
function mobf_global_data_get(id)
local dataid = tonumber(id)
if dataid == nil or
mobf_global_data[dataid] == nil then
dbg_mobf.generic_lvl1("MOBF: data not found, id: " .. dump(dataid))
return nil
end
local retval = mobf_global_data[dataid].value
mobf_global_data[dataid] = nil
return retval
end
-------------------------------------------------------------------------------
-- name: mobf_global_data_cleanup()
--
--! @brief periodic cleanup handler
--
-------------------------------------------------------------------------------
function mobf_global_data_cleanup()
if mobf_global_data.last_cleanup + 500 <
mobf_get_current_time() then
for i=1,50,1 do
if mobf_global_data[mobf_global_data.cleanup_index] ~= nil then
if mobf_global_data[mobf_global_data.cleanup_index].added <
mobf_get_current_time() - 300 then
mobf_global_data[mobf_global_data.cleanup_index] = nil
end
mobf_global_data.cleanup_index = mobf_global_data.cleanup_index +1
if mobf_global_data.cleanup_index > #mobf_global_data then
mobf_global_data.cleanup_index = 0
break
end
end
end
mobf_global_data.last_cleanup = mobf_get_current_time()
end
end
--!@}

View File

@ -0,0 +1,47 @@
-------------------------------------------------------------------------------
-- 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 allowed to pretend you have written it.
--
--! @file error_handling.lua
--! @brief code required to do error handling
--! @copyright Sapier
--! @author Sapier
--! @date 2013-05-010
--!
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--
-------------------------------------------------------------------------------
-- name: mobf_assert_backtrace(value)
--
--! @brief assert in case value is false
--
--! @param value to evaluate
-------------------------------------------------------------------------------
function mobf_assert_backtrace(value)
if value == false then
print(debug.traceback("Current Callstack:\n"))
assert(value)
end
end
-------------------------------------------------------------------------------
-- name: mobf_assert_validpos(pos)
--
--! @brief check if a pos is valid
--
--! @param pos to evaluate
-------------------------------------------------------------------------------
function mobf_assert_validpos(pos)
mobf_assert_backtrace(pos ~= nil)
mobf_assert_backtrace(type(pos) == "table")
mobf_assert_backtrace(pos.x ~= nil)
mobf_assert_backtrace(pos.y ~= nil)
mobf_assert_backtrace(pos.z ~= nil)
mobf_assert_backtrace(type(pos.x) == "number")
mobf_assert_backtrace(type(pos.y) == "number")
mobf_assert_backtrace(type(pos.z) == "number")
end

View File

@ -0,0 +1,800 @@
-------------------------------------------------------------------------------
-- 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 allowed to pretend you have written it.
--
--! @file generic_functions.lua
--! @brief generic functions used in many different places
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--!
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @defgroup gen_func Generic functions
--! @brief functions for various tasks
--! @ingroup framework_int
--! @{
-------------------------------------------------------------------------------
-- name: mobf_get_time_ms()
--
--! @brief get current time in ms
--
--! @return current time in ms
-------------------------------------------------------------------------------
function mobf_get_time_ms()
--this fct is overwritten on init with best timesource available atm
return os.clock() * 1000
end
-------------------------------------------------------------------------------
-- name: mobf_contains(cur_table,element)
--
--! @brief check if element is in table
--
--! @param cur_table table to look in
--! @param element element to look for
--! @return true/false
-------------------------------------------------------------------------------
function mobf_contains(cur_table,element)
if cur_table == nil then
return false
end
for i,v in ipairs(cur_table) do
if v == element then
return true
end
end
return false
end
-------------------------------------------------------------------------------
-- name: MIN(a,b)
--
--! @brief minimum of two numbers
--
--! @param a number 1
--! @param b number 2
--! @return minimum
-------------------------------------------------------------------------------
function MIN(a,b)
mobf_assert_backtrace(type(a) == "number")
mobf_assert_backtrace(type(b) == "number")
if a > b then
return b
else
return a
end
end
-------------------------------------------------------------------------------
-- name: MAX(a,b)
--
--! @brief maximum of two numbers
--
--! @param a number 1
--! @param b number 2
--! @return maximum
-------------------------------------------------------------------------------
function MAX(a,b)
mobf_assert_backtrace(type(a) == "number")
mobf_assert_backtrace(type(b) == "number")
if a > b then
return a
else
return b
end
end
-------------------------------------------------------------------------------
-- name: DELTA(a,b)
--
--! @brief delta of two numbers
--
--! @param a number 1
--! @param b number 2
--! @return delta
-------------------------------------------------------------------------------
function DELTA(a,b)
return math.abs(a-b)
end
-------------------------------------------------------------------------------
-- name: mobf_is_walkable(node)
--
--! @brief check if walkable flag is set for a node
--
--! @param node to check
--! @return true/false
-------------------------------------------------------------------------------
function mobf_is_walkable(node)
return (node and node.name and minetest.registered_nodes[node.name] and
minetest.registered_nodes[node.name].walkable == false)
end
-------------------------------------------------------------------------------
-- name: mobf_get_current_time()
--
--! @brief alias to get current time
--
--! @return current time in seconds
-------------------------------------------------------------------------------
function mobf_get_current_time()
if type(minetest.get_time) == "function" then
return minetest.get_time()
else
return os.time(os.date('*t'))
end
end
-------------------------------------------------------------------------------
-- name: mobf_round_pos(pos)
--
--! @brief calculate integer position
--
--! @param pos position to be rounded
--! @return rounded position
-------------------------------------------------------------------------------
function mobf_round_pos(pos)
if pos == nil then
return pos
end
return { x=math.floor(pos.x + 0.5),
y=math.floor(pos.y + 0.5),
z=math.floor(pos.z + 0.5)
}
end
-------------------------------------------------------------------------------
-- name: mobf_find_entity(newobject) DEPRECATED
--
--! @brief find entity by object reference
--
--! @param newobject r object reference
--! @return entity object reference points at or nil on error
-------------------------------------------------------------------------------
function mobf_find_entity(newobject)
return newobject:get_luaentity()
end
-------------------------------------------------------------------------------
-- name: mobf_max_light_around(pos,range,daytime)
--
--! @brief get maximum light level around specified position
--
--! @param pos center of area to search
--! @param distance radius of area
--! @param daytime time of day to check
--! @return highest detected light level
-------------------------------------------------------------------------------
function mobf_max_light_around(pos,distance,daytime)
mobf_assert_validpos(pos)
local max_light = 0
for y_run=pos.y-distance,pos.y+distance,1 do
for z_run=pos.z-distance,pos.z+distance,1 do
for x_run=pos.x-distance,pos.x+distance,1 do
local current_pos = {x=x_run,y=y_run,z=z_run }
local node = minetest.get_node(current_pos)
if node.name == "air" then
local current_light = minetest.get_node_light(current_pos,daytime)
if current_light > max_light then
max_light = current_light
end
end
end
end
end
return max_light
end
-------------------------------------------------------------------------------
-- name: mobf_min_light_around(pos,range,daytime)
--
--! @brief get minimum light level around specified position
--
--! @param pos center of area to search
--! @param distance radius of area
--! @param daytime time of day to check
--! @return highest detected light level
-------------------------------------------------------------------------------
function mobf_min_light_around(pos,distance,daytime)
mobf_assert_validpos(pos)
local min_light = LIGHT_MAX+1
for y_run=pos.y-distance,pos.y+distance,1 do
for z_run=pos.z-distance,pos.z+distance,1 do
for x_run=pos.x-distance,pos.x+distance,1 do
local current_pos = {x=x_run,y=y_run,z=z_run }
local node = minetest.get_node(current_pos)
if node.name == "air" then
local current_light = minetest.get_node_light(current_pos,daytime)
if current_light < min_light then
min_light = current_light
end
end
end
end
end
return min_light
end
-------------------------------------------------------------------------------
-- name: mobf_objects_around(pos,range,ignorelist)
--
--! @brief get number of objects within a certain range
--
--! @param pos position to look around
--! @param range range to check
--! @param ignorelist list of entitynames to ignore
--! @return count of objects
-------------------------------------------------------------------------------
function mobf_objects_around(pos,range,ignorelist)
local objectlist = minetest.get_objects_inside_radius(pos,range)
local cleaned_objectcount = 0
for i=1,#objectlist,1 do
local luaentity = objectlist[i]:get_luaentity()
if luaentity ~= nil then
if not luaentity.mobf_spawner and
not mobf_contains(ignorelist,luaentity.name) then
cleaned_objectcount = cleaned_objectcount + 1
end
else
cleaned_objectcount = cleaned_objectcount + 1
end
end
return cleaned_objectcount
end
-------------------------------------------------------------------------------
-- name: mobf_mob_around(mob_name,mob_transform_name,pos,range,)
--
--! @brief get number of mobs of specified type within range of pos
--
--! @param mob_name basic name of mob
--! @param mob_transform secondary name of mob
--! @param pos position to check
--! @param range range to check
--! @param ignore_playerspawned ignore mob spawned by players for check
--! @return number of mob found
-------------------------------------------------------------------------------
function mobf_mob_around(mob_name,mob_transform,pos,range,ignore_playerspawned)
local count = 0
local objectcount = 0
mobf_assert_backtrace(range ~= nil)
mobf_assert_backtrace(pos ~= nil)
local objectlist = minetest.get_objects_inside_radius(pos,range)
if mob_transform == nil then
mob_transform = ""
end
local objcount = 0
if objectlist ~= nil then
objcount = #objectlist
end
dbg_mobf.generic_lvl1("MOBF: entity at "..printpos(pos)..
" looking for: "..mob_name ..
" or " .. mob_transform ..
" within " .. objcount .. " objects" )
for index,value in pairs(objectlist) do
local entity = mobf_find_entity(value)
--any mob is required to have a name so we may use this to decide
--if an entity is an mob or not
if entity ~= nil and
entity.data ~= nil and
entity.dynamic_data ~= nil then
if entity.removed == false then
if entity.data.modname..":"..entity.data.name == mob_name or
entity.data.modname..":"..entity.data.name == mob_transform then
-- oops we don't yet know if this is playerspawned,
-- for performance reasons assume it isn't
if entity.dynamic_data.spawning == nil then
count = count + 1
else
if (ignore_playerspawned and entity.dynamic_data.spawning.player_spawned) or
ignore_playerspawned ~= false then
dbg_mobf.generic_lvl1("MOBF: Found "..mob_name.. " or "
..mob_transform .. " within specified range of "..range)
count = count + 1
end
end
end
end
end
objectcount = objectcount +1
end
dbg_mobf.generic_lvl2("MOBF: found " .. objectcount .. " within range "
.. count .. " of them are relevant mobs ")
return count
end
-------------------------------------------------------------------------------
-- name: mobf_spawner_around(mob_name,pos,range)
--
--! @brief get number of mobs of specified type within range of pos
--
--! @param mob_name basic name of mob
--! @param pos position to check
--! @param range range to check
--! @return number of mob found
-------------------------------------------------------------------------------
function mobf_spawner_around(mob_name,pos,range)
mobf_assert_validpos(pos)
dbg_mobf.generic_lvl2("MOBF: mobf_spawner_around param: ".. dump(mob_name)
.. " "..dump(pos).. " " .. dump(range))
local count = 0
local objectcount = 0
local objectlist = minetest.get_objects_inside_radius(pos,range)
for index,value in pairs(objectlist) do
local entity = value:get_luaentity()
dbg_mobf.generic_lvl3("MOBF: entity at: "..dump(value:getpos())..
" looking for: "..mob_name .. " " ..
dump(value) .. " " ..
dump(entity))
--any mob is required to have a name so we may use this to decide
--if an entity is an mob or not
if entity ~= nil and
entity.spawner_mob_name ~= nil then
if entity.spawner_mob_name == mob_name then
dbg_mobf.generic_lvl2("MOBF: Found "..mob_name
.. " within specified range of "..range)
count = count + 1
end
end
objectcount = objectcount +1
end
dbg_mobf.generic_lvl2("MOBF: found " .. objectcount .. " within range "
.. count .. " of them are relevant spawners ")
return count
end
-------------------------------------------------------------------------------
-- name: mobf_line_of_sight(pos1,pos2)
--
--! @brief is there a line of sight between two specified positions
--
--! @param pos1 start position of los check
--! @param pos2 end position of los check
--! @return: true/false
-------------------------------------------------------------------------------
function mobf_line_of_sight(pos1,pos2)
--print("Checking line of sight between "..printpos(pos1).." and "..printpos(pos2))
local distance = mobf_calc_distance(pos1,pos2)
local normalized_vector = { x=(pos2.x-pos1.x)/distance,
y=(pos2.y-pos1.y)/distance,
z=(pos2.z-pos1.z)/distance}
local line_of_sight = true
for i=1,distance, 1 do
local tocheck = { x=pos1.x + (normalized_vector.x * i),
y=pos1.y + (normalized_vector.y *i),
z=pos1.z + (normalized_vector.z *i)}
local node = minetest.env:get_node(tocheck)
if minetest.registered_nodes[node.name] == nil or
minetest.registered_nodes[node.name].sunlight_propagates ~= true then
line_of_sight = false
break
end
end
return line_of_sight
end
function mobf_line_of_sightX(pos1,pos2)
return minetest.line_of_sight(pos1,pos2)
end
-------------------------------------------------------------------------------
-- name: mobf_pos_is_zero(pos)
--
--! @brief check if position is (0,0,0)
--
--! @param pos position to check
--! @return true/false
-------------------------------------------------------------------------------
function mobf_pos_is_zero(pos)
if pos.x ~= 0 then return false end
if pos.y ~= 0 then return false end
if pos.z ~= 0 then return false end
return true
end
-------------------------------------------------------------------------------
-- name: mobf_air_above(pos,height)
--
--! @brief check if theres at least height air abov pos
--
--! @param pos position to check
--! @param height min number of air to check
--! @return true/false
-------------------------------------------------------------------------------
function mobf_air_above(pos,height)
mobf_assert_validpos(pos)
for i=0, height, 1 do
local pos_above = {
x = pos.x,
y = pos.y + 1,
z = pos.z
}
local node_above = minetest.get_node(pos_above)
if node_above.name ~= "air" then
return false
end
end
return true
end
-------------------------------------------------------------------------------
-- name: mobf_ground_distance(pos,media)
--
--! @brief get number of blocks above solid ground
--
--! @param pos_raw position to check
--! @param media table of blocks not considered to be ground
--! @param max_check_height abort looking for ground after this number of nodes
--
--! @return number of blocks to ground
-------------------------------------------------------------------------------
function mobf_ground_distance(pos_raw,media,max_check_height)
local pos = {
x=pos_raw.x,
y=math.floor(pos_raw.y + 0.5),
z=pos_raw.z
}
local node_to_check = minetest.get_node(pos)
local count = 0
if max_check_height == nil then
max_check_height = 32
end
while node_to_check ~= nil and mobf_contains(media,node_to_check.name) and
count < max_check_height do
count = count +1
pos = {x=pos.x,y=pos.y-1,z=pos.z};
node_to_check = minetest.get_node(pos)
end
return count
end
-------------------------------------------------------------------------------
-- name: mobf_surface_distance(pos)
--
--! @brief get number of blocks above surface (solid or fluid!)
--
--! @param pos position to check
--! @return number of blocks to ground
-------------------------------------------------------------------------------
function mobf_surface_distance(pos)
local node_to_check = minetest.get_node(pos)
local count = 0
while node_to_check ~= nil and
node_to_check.name == "air" and
count < 32 do
count = count +1
pos = {x=pos.x,y=pos.y-1,z=pos.z};
node_to_check = minetest.get_node(pos)
end
return count
end
-------------------------------------------------------------------------------
-- name: mobf_air_distance(pos)
--
--! @brief get number of blocks below waterline
--
--! @param pos position to check
--! @return number of blocks to air
-------------------------------------------------------------------------------
function mobf_air_distance(pos)
mobf_assert_validpos(pos)
local node_to_check = minetest.get_node(pos)
local count = 0
while node_to_check ~= nil and (
node_to_check.name == "default:water_source" or
node_to_check.name == "default:water_flowing") do
count = count +1
pos = {x=pos.x,y=pos.y+1,z=pos.z};
node_to_check = minetest.get_node(pos)
end
if node_to_check.name == "air" then
return count
else
return -1
end
end
-------------------------------------------------------------------------------
-- name: mobf_above_water(pos)
--
--! @brief check if next non-air block below mob is a water block
--
--! @param pos position to check
--! @return true/false
-------------------------------------------------------------------------------
function mobf_above_water(pos)
local node_to_check = minetest.get_node(pos)
while node_to_check ~= nil and
node_to_check.name == "air" do
pos = {x=pos.x,y=pos.y-1,z=pos.z};
node_to_check = minetest.get_node(pos)
end
if node_to_check.name == "default:water_source" or
node_to_check.name == "default:water_flowing" then
return true
end
return false
end
-------------------------------------------------------------------------------
-- name: get_sunlight_surface(x,z, min_y, max_y)
--
--! @brief get surface for x/z coordinates
--
--! @param x x-coordinate
--! @param z z-coordinate
--! @param min_y minimum y-coordinate to consider
--! @param max_y maximum y-coordinate to consider
--! @return y value of surface or nil
-------------------------------------------------------------------------------
function mobf_get_sunlight_surface(x,z, min_y, max_y)
for runy = min_y, max_y,1 do
local pos = { x=x,y=runy, z=z }
local node_to_check = minetest.get_node(pos)
if node_to_check.name == "default:dirt_with_grass" then
return pos.y
end
end
return nil
end
-------------------------------------------------------------------------------
-- name: get_surface(x,z, min_y, max_y)
--
--! @brief get surface for x/z coordinates
--
--! @param x x-coordinate
--! @param z z-coordinate
--! @param min_y minimum y-coordinate to consider
--! @param max_y maximum y-coordinate to consider
--! @return y value of surface (first air node) or nil
-------------------------------------------------------------------------------
function mobf_get_surface(x,z, min_y, max_y)
mobf_assert_backtrace(min_y ~= nil)
mobf_assert_backtrace(max_y ~= nil)
mobf_assert_backtrace(x ~= nil)
mobf_assert_backtrace(z ~= nil)
if type(minetest.get_surface) == "function" then
local basepos = {x=x,y=min_y,z=z}
local offset = max_y-min_y
local retval = minetest.get_surface(basepos,offset)
return retval
end
local last_node = minetest.get_node({ x=x,y=min_y, z=z })
for runy = min_y+1, max_y,1 do
local pos = { x=x,y=runy, z=z }
local node_to_check = minetest.get_node(pos)
if node_to_check.name == "air" and
last_node.name ~= "air" and
last_node.mame ~= "ignore" then
return pos.y
end
last_node = node_to_check
end
return nil
end
-------------------------------------------------------------------------------
-- name: entity_at_loaded_pos(entity.mobname)
--
--! @brief check if entity is activated at already loaded pos
--
--! @param pos to check
--! @param mobname name of mob
--! @return true/false
-------------------------------------------------------------------------------
function entity_at_loaded_pos(pos,mobname)
local current_node = minetest.get_node(pos)
if mobname == nil then
mobname = ""
end
if current_node ~= nil then
if current_node.name == "ignore" then
minetest.log(LOGLEVEL_WARNING,"MOBF: " ..mobname .. " spawned at unloaded pos! : "
.. dump(pos) .. " node: " .. dump(current_node))
return false
else
return true
end
end
minetest.log(LOGLEVEL_WARNING,"MOBF: spawned at invalid pos!")
return false
end
-------------------------------------------------------------------------------
-- name: mobf_random_direction()
--
--! @brief get a random (blocked) 3d direction
--
--! @return 3d dir value
-------------------------------------------------------------------------------
function mobf_random_direction()
local retval = {}
retval.x=math.random(-1,1)
retval.y=math.random(-1,1)
retval.z=math.random(-1,1)
return retval
end
-------------------------------------------------------------------------------
-- name: mobf_pos_is_same(pos1,pos2)
--
--! @brief check if two positions are equal
--
--! @param pos1
--! @param pos2
--
--! @return true/false
-------------------------------------------------------------------------------
function mobf_pos_is_same(pos1,pos2)
if pos1 == nil or
pos2 == nil then
return false
end
if pos1.x ~= pos2.x or
pos1.y ~= pos2.y or
pos1.z ~= pos2.z or
pos1.x == nil or
pos1.y == nil or
pos1.z == nil or
pos2.x == nil or
pos2.y == nil or
pos2.z == nil then
return false
end
return true
end
-------------------------------------------------------------------------------
-- name: mobf_is_pos(value)
--
--! @brief check if a given value is a position
--
--! @param value to check
--
--! @return true/false
-------------------------------------------------------------------------------
function mobf_is_pos(value)
if value == nil or
type(value) ~= "table" then
return false
end
if value.x == nil or
tonumber(value.x) == nil then
return false
end
if value.y == nil or
tonumber(value.y) == nil then
return false
end
if value.z == nil or
tonumber(value.z) == nil then
return false
end
return true
end
-------------------------------------------------------------------------------
-- name: mobf_hash_to_pos(hash)
--
--! @brief restore a position from a pos hash value
--
--! @param hash to restore pos from
--
--! @return posistion reconstructed from hash
-------------------------------------------------------------------------------
function mobf_hash_to_pos(hash)
local retval = {}
local raw_x = (hash % 65536)
local raw_y = ((hash - raw_x) % (65536*65536)) / 65536
local raw_z = ((hash - raw_x - raw_y) / 65536) / 65536
local mobpos = {}
retval.x = raw_x - 32768
retval.y = raw_y - 32768
retval.z = math.floor(raw_z - 32768)
return retval
end
--!@}

View File

@ -0,0 +1,358 @@
-------------------------------------------------------------------------------
-- 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 geometry.lua
--! @brief generic functions used in many different places
--! @copyright Sapier
--! @author Sapier
--! @date 2013-02-04
--!
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @defgroup gen_func Generic functions
--! @brief functions for various tasks
--! @ingroup framework_int
--! @{
-------------------------------------------------------------------------------
-- name: mobf_calc_distance(pos1,pos2)
--
--! @brief calculate 3d distance between to points
--
--! @param pos1 first position
--! @param pos2 second position
--! @retval scalar value, distance
-------------------------------------------------------------------------------
function mobf_calc_distance(pos1,pos2)
mobf_assert_backtrace(pos1 ~= nil)
mobf_assert_backtrace(pos2 ~= nil)
return math.sqrt( math.pow(pos1.x-pos2.x,2) +
math.pow(pos1.y-pos2.y,2) +
math.pow(pos1.z-pos2.z,2))
end
-------------------------------------------------------------------------------
-- name: mobf_calc_distance_2d(pos1,pos2)
--
--! @brief calculate 2d distance between to points
--
--! @param pos1 target position
--! @param pos2 start position
--! @return scalar value, distance
-------------------------------------------------------------------------------
function mobf_calc_distance_2d(pos1,pos2)
return math.sqrt( math.pow(pos1.x-pos2.x,2) +
math.pow(pos1.z-pos2.z,2))
end
-------------------------------------------------------------------------------
-- name: mobf_calc_direction(pos1,pos2)
--
--! @brief calculate direction angles from pos2 to pos1
--
--! @param start starting position
--! @param destination end position
--! @return anglexz,anglexy
-------------------------------------------------------------------------------
function mobf_calc_direction(start,destination)
mobf_assert_backtrace(start ~= nil)
mobf_assert_backtrace(destination ~= nil)
local xdelta = destination.x - start.x
local ydelta = destination.y - start.y
local zdelta = destination.z - start.z
local distance = math.sqrt(
math.pow(xdelta,2) +
math.pow(ydelta,2) +
math.pow(zdelta,2)
)
local anglexz = math.atan2(zdelta,xdelta)
local anglexy = math.acos(ydelta/distance)
return anglexz,anglexy
end
-------------------------------------------------------------------------------
-- name: mobf_calc_scalar_speed(speedx,speedz)
--
--! @brief calculate scalar speed
--
--! @param speedx speed in direction x
--! @param speedz speed in direction z
--
--! @return scalar speed value
-------------------------------------------------------------------------------
function mobf_calc_scalar_speed(speedx,speedz)
return math.sqrt(math.pow(speedx,2)+
math.pow(speedz,2))
end
-------------------------------------------------------------------------------
-- name: mobf_calc_vector_components(dir_radians,absolute_speed)
--
--! @brief calculate calculate x and z components of a directed speed
--
--! @param dir_radians direction of movement radians
--! @param absolute_speed speed in direction
--
--! @return {x,z}
-------------------------------------------------------------------------------
function mobf_calc_vector_components(dir_radians,absolute_speed)
local retval = {x=0,z=0}
retval.x = absolute_speed * math.cos(dir_radians)
retval.z = absolute_speed * math.sin(dir_radians)
return retval
end
-------------------------------------------------------------------------------
-- name: mobf_calc_vector_components_3d(xzplane_radians,xy_plane_radians,absolute_speed)
--
--! @brief calculate calculate x and z components of a directed speed
--
--! @param xz_plane_radians direction of movement within x-z plane radians
--! @param xy_plane_radians direction of movement within x-y plane radians
--! @param absolute_speed speed in direction
--
--! @return {x,z}
-------------------------------------------------------------------------------
function mobf_calc_vector_components_3d(xz_plane_radians,xy_plane_radians,absolute_speed)
local retval = {x=0,z=0,y=0}
retval.x= absolute_speed * math.sin(xy_plane_radians) * math.cos(xz_plane_radians)
retval.z= absolute_speed * math.sin(xy_plane_radians) * math.sin(xz_plane_radians)
retval.y= absolute_speed * math.cos(xy_plane_radians)
return retval
end
-------------------------------------------------------------------------------
-- name: mobf_get_direction(pos1,pos2)
--
--! @brief get normalized direction from pos1 to pos2
--
--! @param pos1 source point
--! @param pos2 destination point
--! @return xyz direction
-------------------------------------------------------------------------------
function mobf_get_direction(pos1,pos2)
local x_raw = pos2.x -pos1.x
local y_raw = pos2.y -pos1.y
local z_raw = pos2.z -pos1.z
local x_abs = math.abs(x_raw)
local y_abs = math.abs(y_raw)
local z_abs = math.abs(z_raw)
if x_abs >= y_abs and
x_abs >= z_abs then
y_raw = y_raw * (1/x_abs)
z_raw = z_raw * (1/x_abs)
x_raw = x_raw/x_abs
end
if y_abs >= x_abs and
y_abs >= z_abs then
x_raw = x_raw * (1/y_abs)
z_raw = z_raw * (1/y_abs)
y_raw = y_raw/y_abs
end
if z_abs >= y_abs and
z_abs >= x_abs then
x_raw = x_raw * (1/z_abs)
y_raw = y_raw * (1/z_abs)
z_raw = z_raw/z_abs
end
return {x=x_raw,y=y_raw,z=z_raw}
end
-------------------------------------------------------------------------------
-- name: mobf_calc_yaw(x,z)
--
--! @brief calculate radians value of a 2 dimendional vector
--
--! @param x vector component 1
--! @param z vector component 2
--
--! @return radians value
-------------------------------------------------------------------------------
function mobf_calc_yaw(x,z)
local direction = math.atan2(z,x)
while direction < 0 do
direction = direction + (2* math.pi)
end
while direction > (2*math.pi) do
direction = direction - (2* math.pi)
end
return direction
end
-------------------------------------------------------------------------------
-- name: mobf_assert_backtrace(value)
--
--! @brief assert in case value is false
--
--! @param heightdiff height difference between shooter and target
--! @param time time to reach target
--! @param acceleration acceleration set for object
--
--! @return y-velocity at start
-------------------------------------------------------------------------------
function mobf_balistic_start_speed(heightdiff,time,acceleration)
mobf_assert_backtrace(heightdiff ~= nil)
mobf_assert_backtrace(time ~= nil)
mobf_assert_backtrace(acceleration ~= nil)
return (heightdiff - (acceleration/2) * (time*time)) / time
end
-------------------------------------------------------------------------------
-- name: mobf_calc_travel_time(distance,velocity,acceleration)
--
--! @brief assert in case value is false
--
--! @param distance distance to target
--! @param velocity to start with
--! @param acceleration acceleratio to use
--
--! @return time to target
-------------------------------------------------------------------------------
function mobf_calc_travel_time(distance,velocity,acceleration)
mobf_assert_backtrace(distance ~= nil)
mobf_assert_backtrace(velocity ~= nil)
mobf_assert_backtrace(acceleration ~= nil)
if acceleration == 0 then
--print("no accel shortcut time calculation")
return distance/velocity
end
local a = acceleration/2
local b = velocity
local c = -distance
--print("a=" .. a .. " b=" .. b .. " c=" .. c)
local det = b*b - 4*a*c
--print("det=" .. det)
if det < 0 then
return nil
end
local ret1 = (-b + math.sqrt(det))/(2*a)
local ret2 = (-b - math.sqrt(det))/(2*a)
--print("x1=" .. ret1 .. " x2=" .. ret2)
if ret1 > 0 then
return ret1
end
return ret2
end
-------------------------------------------------------------------------------
-- name: mobf_same_quadrant(x1,z1,x2,z2)
--
--! @brief check if two points are in same quadrant
--
--! @param x1 x of point1
--! @param z1 z of point1
--! @param x2 x of point2
--! @param z2 z of point2
--
--! @return true/false
-------------------------------------------------------------------------------
function mobf_same_quadrant(x1,z1,x2,z2)
if x1 > 0 and x2 < 0 or
x1 < 0 and x2 >0 then
return false
end
if z1 > 0 and z2 < 0 or
z1 < 0 and z2 >0 then
return false
end
return true
end
-------------------------------------------------------------------------------
-- name: mobf_gauss(center)
--
--! @brief calc random value based uppon gauss distribution around a value
--
--! @param center center value for distribution
--! @param max_deviation maximum deviation from center
--
--! @return gauss randomized value
-------------------------------------------------------------------------------
function mobf_gauss(center,max_deviation)
local u1 = 2 * math.random() -1
local u2 = 2 * math.random() -1
local q = u1*u1 + u2*u2
local maxtries = 0
while (q == 0 or q >= 1) and maxtries < 10 do
u1 = math.random()
u2 = math.random() * -1
q = u1*u1 + u2*u2
maxtries = maxtries +1
end
--abort random generation
if maxtries >= 10 then
return center
end
local p = math.sqrt( (-2*math.log(q))/q )
local retval = nil
--calculate normalized value of max deviation
if math.random() < 0.5 then
retval = center + ( u1*p * (center*max_deviation))
else
retval = center + ( u2*p * (center*max_deviation))
end
return retval
end
--!@}

Some files were not shown because too many files have changed in this diff Show More