Add "mobf" (MOB Framework core) mod.
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
||||
--!@}
|
|
@ -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,
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
default
|
||||
adv_spawning?
|
||||
intllib?
|
||||
factions?
|
|
@ -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?
|
|
@ -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
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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")
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,12 @@
|
|||
# Template
|
||||
|
||||
Path marker tool =
|
||||
|
||||
###Inventory.lua###
|
||||
Trader %s Inventory =
|
||||
Selling: =
|
||||
Selection =
|
||||
Price =
|
||||
or =
|
||||
Pay =
|
||||
Takeaway =
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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,
|
||||
-- }
|
||||
|
||||
--!@}
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
After Width: | Height: | Size: 305 B |
After Width: | Height: | Size: 180 B |
After Width: | Height: | Size: 377 B |
After Width: | Height: | Size: 260 B |
After Width: | Height: | Size: 267 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 271 B |
After Width: | Height: | Size: 271 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 269 B |
After Width: | Height: | Size: 269 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 271 B |
After Width: | Height: | Size: 271 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 266 B |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 812 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 814 B |
After Width: | Height: | Size: 232 B |
|
@ -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
|
||||
|
||||
--!@}
|
|
@ -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
|
|
@ -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
|
||||
|
||||
--!@}
|
|
@ -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
|
||||
--!@}
|