Initial commit

master
Perttu Ahola 2013-03-22 22:46:11 +02:00
commit 2f92e28e7a
139 changed files with 16801 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
## Generic ignorable patterns and files
*~
.*.swp
*bak*
tags
*.vim
*.orig
*.rej

8
README.txt Normal file
View File

@ -0,0 +1,8 @@
Dungeon (Minetest Game)
=======================
Implements a simple dungeon crawler on the Minetest Engine.
Stuff inside is licensed under WTFPL, CC0, and CC BY-SA; see mod directories
for more info.

2
game.conf Normal file
View File

@ -0,0 +1,2 @@
name = Dungeon
common_mods = default, fire, bucket

2
minetest.conf Normal file
View File

@ -0,0 +1,2 @@
# Default configuration for this game
mg_name = singlenode

View File

@ -0,0 +1,19 @@
Licenses
CC0, Author Temperest
models/dm.blend
CC0
models/animal_dm.b3d
sounds/animal_dm_fireball.ogg
Unknown (most likely CC0 but no guarantee)
sounds/animal_dm_die.ogg
sounds/animal_dm_hit.ogg
sounds/animal_dm_random.ogg
Unknown, Author 4aiman
models/DM.blend
Everything not mentioned:
CC-BY-SA, Author sapier

View File

@ -0,0 +1,3 @@
default
mobf
fire

160
mods/animal_dm/init.lua Normal file
View File

@ -0,0 +1,160 @@
-------------------------------------------------------------------------------
-- 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 init.lua
--! @brief dungeonmaster implementation
--! @copyright Sapier
--! @author Sapier
--! @date 2013-01-27
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
minetest.log("action","MOD: animal_dm loading ...")
local version = "0.0.15"
local dm_groups = {
not_in_creative_inventory=1
}
local selectionbox_dm = {-0.75, -1, -0.75, 0.75, 1, 0.75}
dm_prototype = {
name="dm",
modname="animal_dm",
generic = {
description="Dungeonmaster (MOBF)",
base_health=10,
kill_result="",
armor_groups= {
fleshy=1,
deamon=1,
},
groups = dm_groups,
envid="simple_air"
},
movement = {
min_accel=0.2,
max_accel=0.4,
max_speed=0.25,
pattern="stop_and_go",
canfly=false,
follow_speedup=5,
},
combat = {
angryness=0.99,
starts_attack=true,
sun_sensitive=true,
melee = {
maxdamage=3,
range=5,
speed=1,
},
distance = {
attack="mobf:fireball_entity",
range =15,
speed = 1,
},
self_destruct = nil,
},
spawning = {
rate=0.02,
density=750,
algorithm="shadows_spawner",
height=3,
respawndelay = 60,
},
sound = {
random = {
name="animal_dm_random_1",
min_delta = 30,
chance = 0.5,
gain = 0.5,
max_hear_distance = 5,
},
distance = {
name="animal_dm_fireball",
gain = 0.5,
max_hear_distance = 7,
},
die = {
name="animal_dm_die",
gain = 0.7,
max_hear_distance = 7,
},
melee = {
name="animal_dm_hit",
gain = 0.7,
max_hear_distance = 5,
},
},
animation = {
walk = {
start_frame = 31,
end_frame = 60,
},
stand = {
start_frame = 1,
end_frame = 30,
},
combat = {
start_frame = 61,
end_frame = 90,
},
},
states = {
{
name = "default",
movgen = "none",
chance = 0,
animation = "stand",
graphics_3d = {
visual = "mesh",
mesh = "animal_dm.b3d",
textures = {"animal_dm_mesh.png"},
collisionbox = selectionbox_dm,
visual_size= {x=1,y=1,z=1},
},
graphics = {
sprite_scale={x=4,y=4},
sprite_div = {x=6,y=1},
visible_height = 2,
},
typical_state_time = 30,
},
{
name = "walking",
movgen = "probab_mov_gen",
chance = 0.25,
animation = "walk",
typical_state_time = 180,
},
{
movgen="follow_mov_gen",
name = "combat",
chance = 0,
animation = "combat",
typical_state_time = 0,
},
},
}
dm_debug = function (msg)
--minetest.log("action", "mobs: "..msg)
--minetest.chat_send_all("mobs: "..msg)
end
local modpath = minetest.get_modpath("animal_dm")
dofile (modpath .. "/vault.lua")
--register with animals mod
minetest.log("action", "adding mob "..dm_prototype.name)
if mobf_add_mob(dm_prototype) then
dofile (modpath .. "/vault.lua")
end
minetest.log("action","MOD: animal_dm mod version " .. version .. " loaded")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,16 @@
--dm--
random:
random_1.ogg
hit:
hit.ogg
die:
die.ogg
melee:
hit.ogg
distance:
fireball.ogg

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

529
mods/animal_dm/vault.lua Normal file
View File

@ -0,0 +1,529 @@
--(c) Celeron55
local vault = {}
function vault.cmp(v, w)
return (
v.x == w.x and
v.y == w.y and
v.z == w.z
)
end
function vault.add(v, w)
return {
x = v.x + w.x,
y = v.y + w.y,
z = v.z + w.z,
}
end
function vault.sub(v, w)
return {
x = v.x - w.x,
y = v.y - w.y,
z = v.z - w.z,
}
end
vault.make_vault_part = function(p, part, seed)
local ns = nil
local top_y = 2
local mob = nil
--choose what to do at position
if part == 'w' then
ns = {
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
}
elseif part == 'W' then
top_y = 3
ns = {
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
}
elseif part == 'c' then
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'T' then
ns = {
{name='default:cobble'},
{name='default:torch'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'f' then
ns = {
{name='air'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'l' then
top_y = 3
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='air'},
{name='air'},
{name='default:lava_source'},
}
elseif part == 'm' then
top_y = 3
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
mob = "animal_dm:dm__default"
elseif part == 'C' then
top_y = 3
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'S' then
top_y = 3
ns = {
{name='default:cobble'},
{name='air'},
{name='default:bookshelf'},
{name='default:bookshelf'},
{name='default:cobble'},
}
elseif part == 'd' then
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'a' then
ns = {
nil,
{name='air'},
{name='air'},
nil,
}
elseif part == 'A' then
ns = {
nil,
{name='air'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 't' then
local invcontent = {}
local pr = PseudoRandom(seed)
if pr:next(1,4) == 1 then
table.insert(invcontent, 'default:apple '..tostring(pr:next(1,5)))
end
if pr:next(1,3) == 1 then
table.insert(invcontent, 'bucket:bucket_empty 1')
end
if pr:next(1,4) == 1 then
table.insert(invcontent, 'bucket:bucket_lava 1')
end
if pr:next(1,20) == 1 then
table.insert(invcontent, 'bucket:bucket_water 1')
end
if pr:next(1,34) == 1 then
table.insert(invcontent, 'bucket:nyancat 1')
table.insert(invcontent, 'bucket:nyancat_rainbow '..tostring(pr:next(1,6)))
end
if pr:next(1,2) == 1 then
table.insert(invcontent, 'default:gravel '..tostring(pr:next(1,10)))
end
if pr:next(1,3) == 1 then
table.insert(invcontent, 'default:bookshelf '..tostring(pr:next(1,2)))
end
if pr:next(1,8) == 1 then
table.insert(invcontent, 'default:cactus '..tostring(pr:next(1,2)))
end
if pr:next(1,4) == 1 then
table.insert(invcontent, 'default:rail '..tostring(pr:next(1,10)))
end
if pr:next(1,5) == 1 then
table.insert(invcontent, 'default:ladder '..tostring(pr:next(1,9)))
end
if pr:next(1,3) == 1 then
table.insert(invcontent, 'default:sign_wall 1')
end
if pr:next(1,6) == 1 then
table.insert(invcontent, 'default:steelblock '..tostring(pr:next(1,6)))
end
ns = {
{name='default:cobble'},
{name='air'},
{name='default:chest', inv=invcontent},
{name='default:cobble'},
}
else
return
end
--for all nodes in definition
for i=1,#ns do
local dy = top_y + 1 - i
local p2 = {x=p.x,y=p.y+dy,z=p.z}
--get node at position
local oldn = minetest.env:get_node(p2)
local n = ns[i]
--don't do anything if no new node is specified or
--old node is air
if n and oldn.name ~= "air" then
--possibly add mossy if it's a cobble node
if n.name == 'default:cobble' then
local perlin = minetest.env:get_perlin(123, 2, 0.8, 2.0)
if perlin:get3d(p2) >= 0.0 then
n.name = 'default:mossycobble'
end
end
--set node
--dm_debug("pos: " .. minetest.pos_to_string(p2) .. " replacing " .. oldn.name .. " by " .. n.name)
minetest.env:set_node(p2, ns[i])
--special handling for chests
if n.inv then
dm_debug("adding chest at " .. minetest.pos_to_string(p2) )
local meta = minetest.env:get_meta(p2)
local inv = meta:get_inventory()
for _,itemstring in ipairs(n.inv) do
inv:add_item('main', itemstring)
end
end
else
--dm_debug("pos: " .. minetest.pos_to_string(p) .. " not replacing " .. oldn.name)
end
end
if mob then
--dm_debug("adding dm at " .. minetest.pos_to_string({x=p.x,y=p.y+1,z=p.z}) )
local newobject = minetest.env:add_entity({x=p.x,y=p.y+1,z=p.z}, mob)
local newentity = mobf_find_entity(newobject)
if newentity and
newentity.dynamic_data and
newentity.dynamic_data.spawning then
newentity.dynamic_data.spawning.player_spawned = playerspawned
newentity.dynamic_data.spawning.spawner = "vault"
end
end
end
vault.generate_vault = function(vault_def, p, dir, seed)
local dim_z = #vault_def
assert(dim_z > 0)
local dim_x = #vault_def[1]
--don't generate vault if entrance dir not z +1
if not vault.cmp(dir, {x=0,y=0,z=1}) then
--dm_debug("entrance direction wrong: " .. minetest.pos_to_string(p))
return
else
dm_debug("generating vault at: " .. minetest.pos_to_string(p))
end
--print("Making vault at "..minetest.pos_to_string(p))
if dim_x >= 14 then
--dm_debug("Making large vault at "..minetest.pos_to_string(p))
else
--dm_debug("Making vault at "..minetest.pos_to_string(p))
end
-- Find door in definition
local def_door_p = nil
for dx=1,dim_x do
for dz=1,dim_z do
if vault_def[dim_z+1-dz][dx] == 'd' then
def_door_p = {x=dx,y=0,z=dz}
end
if def_door_p then break end
end
if def_door_p then break end
end
--print("Vault door found at "..minetest.pos_to_string(def_door_p).." in definition")
assert(def_door_p)
local randseed = seed
for dx=1,dim_x do
for dz=1,dim_z do
local p2 = vault.add(vault.sub(p, def_door_p), {x=dx, y=0, z=dz})
local part = vault_def[dim_z+1-dz][dx]
--dm_debug("Making vault part "..dump(part).." at "..minetest.pos_to_string(p2))
vault.make_vault_part(p2, part, randseed)
randseed = randseed + 1
end
end
end
-- Definition is for Z=up, X=right, dir={x=0,y=0,z=1}
vault.vault_defs = {
---[[
{
{'w','w','w','w','w','w','w','w','w','w'},
{'w','c','c','c','c','c','C','C','c','w'},
{'w','c','C','c','c','c','C','C','m','w'},
{'w','C','C','C','w','w','C','C','c','w'},
{'w','C','C','C','w','w','C','C','c','w'},
{'w','T','C','c','w','w','C','w','w','w'},
{'w','c','t','c','w','w','C','w','n','n'},
{'w','w','w','w','w','w','C','w','n','n'},
{'n','n','n','n','n','w','d','w','n','n'},
{'n','n','n','n','n','n','A','n','n','n'},
},
--]]
---[[
{
{'w','w','w','w','w','w','w','w'},
{'w','c','c','c','m','C','c','w'},
{'w','C','c','c','C','C','c','w'},
{'w','C','w','w','C','C','c','w'},
{'w','C','t','w','C','C','c','w'},
{'w','C','t','w','C','w','d','w'},
{'w','T','w','w','C','w','A','n'},
{'w','c','C','C','C','w','n','n'},
{'n','n','n','w','w','w','n','n'},
},
--]]
---[[
{
{'W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W'},
{'W','l','l','l','l','l','l','t','t','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','m','l','l','l','l','l','f','f','l','l','l','l','l','m','W'},
{'W','c','l','l','l','l','l','f','f','l','l','l','l','l','c','W'},
{'W','c','l','l','l','l','l','f','f','l','l','l','l','l','c','W'},
{'W','c','l','l','l','l','l','f','f','l','l','l','l','l','c','W'},
{'W','m','l','l','l','l','l','f','f','l','l','l','l','l','m','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','W','W','W','W','W','W','W','d','W','W','W','W','W','W','W'},
{'n','n','n','n','n','n','n','n','A','n','n','n','n','n','n','n'},
},
--]]
---[[
{
{'W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W'},
{'W','f','f','f','f','l','l','t','l','l','l','l','l','l','l','W'},
{'W','f','l','l','l','l','l','f','l','l','l','l','l','l','l','W'},
{'W','f','l','l','f','l','l','l','l','l','l','l','l','l','l','W'},
{'W','f','l','l','l','l','l','l','f','l','f','f','l','l','m','W'},
{'W','f','l','l','l','l','f','l','l','l','l','l','f','l','c','W'},
{'W','f','f','l','l','l','l','l','l','l','l','l','f','l','c','W'},
{'W','c','f','l','l','l','l','l','l','l','l','l','f','l','c','W'},
{'W','m','f','l','l','l','l','l','l','l','l','l','f','l','m','W'},
{'W','l','f','l','l','l','l','f','f','l','f','f','f','l','l','W'},
{'W','l','t','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','W','W','W','W','W','W','W','d','W','W','W','W','W','W','W'},
{'n','n','n','n','n','n','n','n','A','n','n','n','n','n','n','n'},
},
--]]
---[[
{
{'n','n','n','n','n','n','n','n','n','n','n','n','w','w','w','w','n','n','n','n'},
{'n','n','n','n','n','n','n','n','w','w','w','w','w','c','c','w','w','w','w','n'},
{'n','n','n','n','n','n','n','w','w','c','c','c','c','c','c','c','c','w','w','n'},
{'n','n','n','n','n','n','w','w','c','c','c','c','c','c','c','c','c','c','W','W'},
{'n','n','n','n','n','n','w','t','c','C','C','m','c','c','C','C','m','c','c','W'},
{'n','n','n','n','n','n','w','t','c','C','C','c','c','c','C','C','c','c','c','W'},
{'n','n','n','n','n','n','w','w','c','c','c','c','c','c','c','c','c','c','c','W'},
{'n','n','n','n','n','n','n','w','w','c','c','c','C','C','C','C','C','C','C','W'},
{'n','n','n','n','n','n','n','n','w','w','c','c','l','l','l','l','l','l','l','W'},
{'n','n','n','n','n','n','n','n','W','W','l','l','l','l','l','l','l','l','l','W'},
{'w','w','w','w','W','W','W','W','W','l','l','l','l','l','l','l','l','l','l','W'},
{'w','w','c','c','C','S','S','C','C','l','l','l','l','l','l','l','l','l','l','W'},
{'w','c','c','c','C','C','C','C','l','l','l','l','l','l','l','l','l','l','l','W'},
{'w','c','c','C','C','C','C','C','l','l','l','l','m','C','l','l','l','l','l','W'},
{'w','c','c','C','C','C','C','C','l','l','l','l','C','t','l','l','l','l','l','W'},
{'w','c','c','C','C','l','l','l','l','l','l','l','l','l','l','l','l','l','l','W'},
{'w','c','C','C','l','l','l','l','l','l','l','l','l','l','l','l','l','l','l','W'},
{'w','W','C','C','l','l','l','l','l','l','l','l','l','l','l','l','l','l','W','W'},
{'n','W','d','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','n'},
{'n','n','A','n','n','n','n','n','n','n','n','n','n','n','n','n','n','n','n','n'},
},
--]]
}
function vault.node_is_solid(n)
return (n and n.name and minetest.registered_nodes[n.name] and
minetest.registered_nodes[n.name].walkable)
end
vault.generate_random_vault = function(p, dir, seed)
--random select of vault template
local pr = PseudoRandom(seed+9322)
local vault_def = vault.vault_defs[pr:next(1, #vault.vault_defs)]
--generate vault by template
vault.generate_vault(vault_def, p, dir, seed)
end
minetest.register_on_generated(function(minp, maxp, seed)
--abort in case of dm disabled
if minetest.registered_entities["animal_dm:dm"] == nil then
return
end
--abort in case of above sea level
if maxp.y > -10 then
return
end
--calculate area of generated chunk
local area = (maxp.x-minp.x+1)*(maxp.z-minp.z+1)
--initialize dungeon level with 2
local dungeon_level_y = 2
--get random dungeon level within generated chunk
if maxp.y < 2 then
local pr = PseudoRandom(seed+932)
dungeon_level_y = pr:next(minp.y, maxp.y)
end
--initialize check values
local is_empty = false
local light = nil
local last_empty = false
local last_light = nil
local possible_entrances = {}
local d = 4
--loop through chunk using d frames in x direction
for x=minp.x/d+1,maxp.x/d-1 do
--set to unknown on start of x-loop (why?)
local last_node_known = false
--loop through cunk in z direction
for z=minp.z+1,maxp.z-1 do
--get information of current node to check
local p = {x=x*d, y=dungeon_level_y, z=z}
local n = minetest.env:get_node(p)
--save values from last loop
last_empty = is_empty
last_light = light
--get new values
is_empty = not vault.node_is_solid(n)
light = minetest.env:get_node_light(p, 0.5)
--if last node was known
if last_node_known then
--initialize values
local useful_light = nil
--save values
if is_empty then
useful_light = light
else
useful_light = last_light
end
--if current node isn't solid,
--last was solid and we're not at daylight
if is_empty and not last_empty and useful_light < 15 then
--calculate ??? positions next to current pos
local p1 = vault.add(p, {x=0, y=0, z=-1})
local p2 = vault.add(p1, {x=0, y=2, z=-1})
local p3 = vault.add(p1, {x=0, y=2, z=-8})
local p4 = vault.add(p1, {x=-3, y=1, z=-2})
local p5 = vault.add(p1, {x=3, y=1, z=-2})
--if all nodes are solid
if vault.node_is_solid(minetest.env:get_node(p2)) and
vault.node_is_solid(minetest.env:get_node(p3)) and
vault.node_is_solid(minetest.env:get_node(p4)) and
vault.node_is_solid(minetest.env:get_node(p5))
then
local entrance = {
p = p1,
dir = {x=0, y=0, z=-1},
}
--add to entrance table with direction z = -1
table.insert(possible_entrances, entrance)
end
end
--if current node isn't solid,
--last was solid and we're not at daylight
if last_empty and not is_empty and useful_light < 15 then
local p1 = vault.add(p, {x=0, y=0, z=0})
local p2 = vault.add(p1, {x=0, y=2, z=1})
local p3 = vault.add(p1, {x=0, y=2, z=8})
local p4 = vault.add(p1, {x=-3, y=1, z=2})
local p5 = vault.add(p1, {x=3, y=1, z=2})
--if all nodes are solid
if vault.node_is_solid(minetest.env:get_node(p2)) and
vault.node_is_solid(minetest.env:get_node(p3)) and
vault.node_is_solid(minetest.env:get_node(p4)) and
vault.node_is_solid(minetest.env:get_node(p5))
then
local entrance = {
p = p1,
dir = {x=0, y=0, z=1},
}
--add to entrance table with direction z = 1
table.insert(possible_entrances, entrance)
end
end
end
last_node_known = true
end
end
--for _,entrance in ipairs(possible_entrances) do
-- dm_debug("Possible entrance: "..dump(entrance))
--end
--calculate number of entrances to generate per area
local num_entrances_to_generate = area/200
--check if there are enough possible entrances
if num_entrances_to_generate > #possible_entrances then
num_entrances_to_generate = #possible_entrances
end
local pr = PseudoRandom(seed+9452)
--generate vault for max number of entraces to generate at random
--selected possible entrances
for entrances_generate_i=1,num_entrances_to_generate do
local entrance_i = pr:next(1, #possible_entrances)
local entrance = possible_entrances[entrance_i]
--dm_debug("Entrance: "..dump(entrance))
vault.generate_random_vault(entrance.p, entrance.dir, seed)
end
end)

View File

@ -0,0 +1,3 @@
default
animalmaterials
mobf

104
mods/animal_rat/init.lua Normal file
View File

@ -0,0 +1,104 @@
-------------------------------------------------------------------------------
-- 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 init.lua
--! @brief rat implementation
--! @copyright Sapier
--! @author Sapier
--! @date 2013-01-27
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
minetest.log("action","MOD: animal_rat loading ...")
local version = "0.0.8"
local selectionbox_rat = {-0.2, -0.0625, -0.2, 0.2, 0.125, 0.2}
local rat_groups = {
not_in_creative_inventory=1
}
rat_prototype = {
name="rat",
modname="animal_rat",
generic = {
description="Rat (Animals)",
base_health=2,
kill_result="",
armor_groups= {
fleshy=3,
},
groups = rat_groups,
envid="on_ground_1",
},
movement = {
default_gen="probab_mov_gen",
min_accel=0.4,
max_accel=0.6,
max_speed=2,
pattern="run_and_jump_low",
canfly=false,
},
catching = {
tool="animalmaterials:net",
consumed=true,
},
spawning = {
rate=0.02,
density=250,
algorithm="forrest_mapgen",
height=1
},
animation = {
walk = {
start_frame = 1,
end_frame = 40,
},
stand = {
start_frame = 41,
end_frame = 80,
},
},
states = {
{
name = "default",
movgen = "none",
chance = 0,
animation = "stand",
graphics_3d = {
visual = "mesh",
mesh = "animal_rat.b3d",
textures = {"animal_rat_mesh.png"},
collisionbox = selectionbox_rat,
visual_size= {x=1,y=1,z=1},
},
graphics = {
sprite_scale={x=1,y=1},
sprite_div = {x=6,y=1},
visible_height = 1,
visible_width = 1,
},
typical_state_time = 10,
},
{
name = "walking",
movgen = "probab_mov_gen",
chance = 0.75,
animation = "walk",
typical_state_time = 180,
},
},
}
--register mod
minetest.log("action","\tadding "..rat_prototype.name)
mobf_add_mob(rat_prototype)
minetest.log("action","MOD: animal_rat mod version " .. version .. " loaded")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

View File

@ -0,0 +1,8 @@
Licenses
CCO
sounds/animal_vombie_sun_damage.ogg
sounds/animal_vombie_random_1.ogg
Everything not mentioned:
CC-BY-SA, Author sapier

View File

@ -0,0 +1,2 @@
default
mobf

View File

@ -0,0 +1,67 @@
--! @class vombie_flame
--! @ingroup weapons
--! @brief a plasmaball weapon entity
vombie_flame = {
physical = false,
textures = {"animal_vombie_flames.png"},
visual = "sprite",
collisionbox = {0,0,0,0,0,0},
spritediv = {x=1,y=16},
--vizual_size = {x=0.1,y=0.1},
velocity = 1.0,
gravity = -0.1,
damage = 8,
leveltime = 2.5,
created = -1,
leveldtime = 0,
level = 0,
}
-------------------------------------------------------------------------------
-- name: vombie_flame.on_activate = function(self, staticdata)
--
--! @brief onactivate callback for plasmaball
--! @memberof vombie_flame
--! @private
--
--! @param self fireball itself
--! @param staticdata
-------------------------------------------------------------------------------
function vombie_flame.on_activate(self,staticdata)
self.created = mobf_get_current_time()
self.object:setsprite({x=0,y=self.level}, 1, 0, true)
self.object:setvelocity({x=0,y=self.velocity,z=0})
end
-------------------------------------------------------------------------------
-- name: vombie_flame.on_step = function(self, dtime)
--
--! @brief onstep callback for vombie flame
--! @memberof vombie_flame
--! @private
--
--! @param vombie_flame itself
--! @param dtime time since last callback
-------------------------------------------------------------------------------
function vombie_flame.on_step(self, dtime)
local pos = self.object:getpos()
local node = minetest.env:get_node(pos)
self.leveldtime = self.leveldtime + dtime
if (self.leveldtime *10) > math.random()*self.leveltime then
self.level = self.level +1
if (self.level < 16) then
self.object:setsprite({x=0,y=self.level}, 1, 0, true)
else
self.object:remove()
end
self.leveldtime = 0
end
end
minetest.register_entity(":animal_vombie:vombie_flame", vombie_flame)

184
mods/animal_vombie/init.lua Normal file
View File

@ -0,0 +1,184 @@
local version = "0.0.17"
minetest.log("action","MOD: loading animal_vombie ... ")
local vombie_groups = {
not_in_creative_inventory=1
}
local selectionbox_vombie = {-0.3, -1.2, -0.5, 0.3, 1, 0.5}
local modpath = minetest.get_modpath("animal_vombie")
dofile (modpath .. "/flame.lua")
function vombie_drop()
local result = {}
if math.random() < 0.05 then
table.insert(result,"animalmaterials:bone 2")
else
table.insert(result,"animalmaterials:bone 1")
end
table.insert(result,"animalmaterials:meat_undead 1")
return result
end
function vombie_on_step_handler(entity,now,dtime)
local pos = entity.getbasepos(entity)
local current_light = minetest.env:get_node_light(pos)
--print("vombie on step: current_light:" .. current_light .. " max light: " .. LIGHT_MAX .. " 3dmode:" .. dump(minetest.setting_getbool("disable_animals_3d_mode")))
if current_light ~= nil and
current_light > LIGHT_MAX and
minetest.setting_getbool("mobf_disable_3d_mode") ~= true and
minetest.setting_getbool("vombie_3d_burn_animation_enabled") == true then
local xdelta = (math.random()-0.5)
local zdelta = (math.random()-0.5)
--print("receiving sun damage: " .. xdelta .. " " .. zdelta)
local newobject=minetest.env:add_entity( { x=pos.x + xdelta,
y=pos.y,
z=pos.z + zdelta },
"animal_vombie:vombie_flame")
--add particles
end
end
function vombie_on_activate_handler(entity)
local pos = entity.object:getpos()
local current_light = minetest.env:get_node_light(pos)
if current_light == nil then
minetest.log(LOGLEVEL_ERROR,"ANIMALS: Bug!!! didn't get a light value for ".. printpos(pos))
return
end
--check if animal is in sunlight
if ( current_light > LIGHT_MAX) then
--don't spawn vombie in sunlight
spawning.remove(entity)
end
end
vombie_prototype = {
name="vombie",
modname="animal_vombie",
generic = {
description="Vombie",
base_health=8,
kill_result=vombie_drop,
armor_groups= {
fleshy=3,
daemon=1,
},
groups = vombie_groups,
envid="on_ground_1",
custom_on_step_handler = vombie_on_step_handler,
custom_on_activate_handler = vombie_on_activate_handler,
},
movement = {
min_accel=0.3,
max_accel=1.5,
max_speed=2,
pattern="stop_and_go",
canfly=false,
follow_speedup=10,
},
combat = {
angryness=1,
starts_attack=true,
sun_sensitive=true,
melee = {
maxdamage=2,
range=2,
speed=1,
},
distance = nil,
self_destruct = nil,
},
spawning = {
rate=0.05,
density=30,
algorithm="at_night_spawner",
height=2,
respawndelay=60,
},
sound = {
random = {
name="animal_vombie_random_1",
min_delta = 10,
chance = 0.5,
gain = 0.05,
max_hear_distance = 5,
},
sun_damage = {
name="animal_vombie_sun_damage",
gain = 0.25,
max_hear_distance = 7,
},
},
animation = {
stand = {
start_frame = 0,
end_frame = 80,
},
walk = {
start_frame = 168,
end_frame = 188,
},
attack = {
start_frame = 81,
end_frame = 110,
},
},
states = {
{
name = "default",
movgen = "none",
typical_state_time = 30,
chance = 0,
animation = "stand",
graphics = {
sprite_scale={x=4,y=4},
sprite_div = {x=6,y=2},
visible_height = 2.2,
visible_width = 1,
},
graphics_3d = {
visual = "mesh",
mesh = "animal_vombie_vombie.b3d",
textures = {"animal_vombie_vombie_mesh.png"},
collisionbox = selectionbox_vombie,
visual_size= {x=1,y=1,z=1},
},
},
{
name = "walking",
movgen = "probab_mov_gen",
typical_state_time = 120,
chance = 0.5,
animation = "walk",
},
{
name = "combat",
typical_state_time = 9999,
chance = 0.0,
animation = "attack",
movgen="follow_mov_gen",
},
}
}
--register with animals mod
minetest.log("action","\tadding mob "..vombie_prototype.name)
mobf_add_mob(vombie_prototype)
minetest.log("action","MOD: animal_vombie mod version " .. version .. " loaded")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,16 @@
--vombie--
random:
random_1.ogg
hit:
--missing--
die:
--missing--
melee:
hit.ogg
sun_damage:
sun_damage.ogg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1 @@
default

View File

@ -0,0 +1,299 @@
-------------------------------------------------------------------------------
-- 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 init.lua
--! @brief animalmaterials
--! @copyright Sapier
--! @author Sapier
--! @date 2013-01-27
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
minetest.log("action","MOD: animalmaterials loading ...")
local version = "0.0.15"
animalmaterialsdata = {}
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Node definitions
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Item definitions
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- deamondeath sword
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_tool("animalmaterials:sword_deamondeath", {
description = "Sword (Deamondeath)",
inventory_image = "default_tool_steelsword.png",
tool_capabilities = {
full_punch_interval = 0.50,
max_drop_level=1,
groupcaps={
fleshy={times={[1]=2.00, [2]=0.80, [3]=0.40}, uses=10, maxlevel=1},
snappy={times={[2]=0.70, [3]=0.30}, uses=40, maxlevel=1},
choppy={times={[3]=0.70}, uses=40, maxlevel=0},
deamon={times={[1]=0.25, [2]=0.10, [3]=0.05}, uses=20, maxlevel=3},
}
}
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- scissors
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_tool("animalmaterials:scissors", {
description = "Scissors",
inventory_image = "animalmaterials_scissors.png",
tool_capabilities = {
max_drop_level=0,
groupcaps={
wool = {uses=20,maxlevel=1}
}
},
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- lasso
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:lasso", {
description = "Lasso",
image = "animalmaterials_lasso.png",
stack_max=10,
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- net
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:net", {
description = "Net",
image = "animalmaterials_net.png",
stack_max=10,
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- saddle
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:saddle", {
description = "Saddle",
image = "animalmaterials_saddle.png",
stack_max=1
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- meat
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:meat_raw", {
description = "Raw meat",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(1),
groups = { meat=1, eatable=1 },
stack_max=25
})
minetest.register_craftitem("animalmaterials:meat_pork", {
description = "Pork (raw)",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(1),
groups = { meat=1, eatable=1 },
stack_max=25
})
minetest.register_craftitem("animalmaterials:meat_beef", {
description = "Beef (raw)",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(1),
groups = { meat=1, eatable=1 },
stack_max=25
})
minetest.register_craftitem("animalmaterials:meat_chicken", {
description = "Chicken (raw)",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(1),
groups = { meat=1, eatable=1 },
stack_max=25
})
minetest.register_craftitem("animalmaterials:meat_lamb", {
description = "Lamb (raw)",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(1),
groups = { meat=1, eatable=1 },
stack_max=25
})
minetest.register_craftitem("animalmaterials:meat_venison", {
description = "Venison (raw)",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(1),
groups = { meat=1, eatable=1 },
stack_max=25
})
minetest.register_craftitem("animalmaterials:meat_undead", {
description = "Meat (not quite dead)",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(-2),
groups = { meat=1, eatable=1 },
stack_max=5
})
minetest.register_craftitem("animalmaterials:meat_toxic", {
description = "Toxic Meat",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(-5),
groups = { meat=1, eatable=1 },
stack_max=5
})
minetest.register_craftitem("animalmaterials:meat_ostrich", {
description = "Ostrich Meat",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(-5),
groups = { meat=1, eatable=1 },
stack_max=5
})
minetest.register_craftitem("animalmaterials:fish_bluewhite", {
description = "Fish (bluewhite)",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(1),
groups = { meat=1, eatable=1 },
stack_max=25
})
minetest.register_craftitem("animalmaterials:fish_clownfish", {
description = "Fish (clownfish)",
image = "animalmaterials_meat_raw.png",
on_use = minetest.item_eat(1),
groups = { meat=1, eatable=1 },
stack_max=25
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- feather
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:feather", {
description = "Feather",
image = "animalmaterials_feather.png",
stack_max=25
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- milk
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:milk", {
description = "Milk",
image = "animalmaterials_milk.png",
on_use = minetest.item_eat(1),
groups = { eatable=1 },
stack_max=10
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- egg
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:egg", {
description = "Egg",
image = "animalmaterials_egg.png",
stack_max=10
})
minetest.register_craftitem("animalmaterials:egg_big", {
description = "Egg (big)",
image = "animalmaterials_egg_big.png",
stack_max=5
})
animalmaterialsdata["animalmaterials_egg"] = {
graphics_3d = {
visual = "mesh",
mesh = "animalmaterials_egg_ent.b3d",
textures = { "animalmaterials_egg_ent_mesh.png" },
collisionbox = { -0.12,-0.125,-0.12,0.12,0.125,0.12 },
visual_size = {x=1,y=1,z=1},
}
}
animalmaterialsdata["animalmaterials_egg_big"] = {
graphics_3d = {
visual = "mesh",
mesh = "animalmaterials_egg_ent.b3d",
textures = { "animalmaterials_egg_ent_mesh.png" },
collisionbox = { -0.24,-0.25,-0.24,0.24,0.25,0.24 },
visual_size = {x=2,y=2,z=2},
}
}
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- bone
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:bone", {
description = "Bone",
image = "animalmaterials_bone.png",
stack_max=25
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- furs
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:fur", {
description = "Fur",
image = "animalmaterials_fur.png",
stack_max=25
})
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- scale
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
minetest.register_craftitem("animalmaterials:scale_golden", {
description = "Scale (golden)",
image = "animalmaterials_scale_golden.png",
stack_max=25
})
minetest.register_craftitem("animalmaterials:scale_white", {
description = "Scale (white)",
image = "animalmaterials_scale_white.png",
stack_max=25
})
minetest.register_craftitem("animalmaterials:scale_grey", {
description = "Scale (grey)",
image = "animalmaterials_scale_grey.png",
stack_max=25
})
minetest.register_craftitem("animalmaterials:scale_blue", {
description = "Scale (blue)",
image = "animalmaterials_scale_blue.png",
stack_max=25
})
minetest.log("action","MOD: animalmaterials mod version " .. version .. " loaded")

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

1
mods/dungeon/depends.txt Normal file
View File

@ -0,0 +1 @@
default

560
mods/dungeon/init.lua Normal file
View File

@ -0,0 +1,560 @@
-- minetest/dungeon: dungeon
local DUNGEON_Y = -1000
-- Define this so ores don't get placed on walls
minetest.register_node("dungeon:stone", {
description = "Dungeon Stone",
tiles = {"default_stone.png"},
groups = {},
legacy_mineral = true,
sounds = default.node_sound_stone_defaults(),
})
minetest.register_alias("mapgen_singlenode", "dungeon:stone")
-- Make chests not pickable up
local def = minetest.registered_nodes["default:chest"]
def.groups = {}
minetest.register_node(":default:chest", def)
local v3 = {}
function v3.new(x, y, z)
if x == nil then
return {
x = 0,
y = 0,
z = 0
}
end
if type(x) == "table" then
return {
x = x.x,
y = x.y,
z = x.z,
}
end
return {
x = x,
y = y,
z = z,
}
end
function v3.floor(v)
return {
x = math.floor(v.x),
y = math.floor(v.y),
z = math.floor(v.z),
}
end
function v3.cmp(v, w)
return (
v.x == w.x and
v.y == w.y and
v.z == w.z
)
end
function v3.add(v, w)
return {
x = v.x + w.x,
y = v.y + w.y,
z = v.z + w.z,
}
end
function v3.sub(v, w)
return {
x = v.x - w.x,
y = v.y - w.y,
z = v.z - w.z,
}
end
function v3.mul(v, a)
return {
x = v.x * a,
y = v.y * a,
z = v.z * a,
}
end
function v3.len(v)
return math.sqrt(
math.pow(v.x, 2) +
math.pow(v.y, 2) +
math.pow(v.z, 2)
)
end
function v3.norm(v)
return v3.mul(v, 1.0 / v3.len(v))
end
function v3.distance(v, w)
return math.sqrt(
math.pow(v.x - w.x, 2) +
math.pow(v.y - w.y, 2) +
math.pow(v.z - w.z, 2)
)
end
mobs = {}
mobs.make_vault_part = function(p, part, pr)
local ns = nil
local top_y = 2
local mob_y = 0
local mob = nil
local item = nil
if part == 'w' then
ns = {
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
}
elseif part == 'W' then
top_y = 3
ns = {
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
{name='default:cobble'},
}
elseif part == 'c' then
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'f' then
ns = {
{name='air'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'l' then
top_y = 3
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='air'},
{name='air'},
{name='default:lava_source'},
}
elseif part == 'm' then
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
local a = pr:next(1,2)
if a == 1 then
mob = "animal_dm:dm__default"
else
mob = "animal_vombie:vombie__default"
end
elseif part == 'r' then
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
mob = "animal_rat:rat__default"
elseif part == 'C' then
top_y = 3
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'd' then
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'a' then
ns = {
nil,
{name='air'},
{name='air'},
nil,
}
elseif part == 'A' then
ns = {
nil,
{name='air'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
elseif part == 'i' then
ns = {
{name='default:cobble'},
{name='air'},
{name='air'},
{name='default:cobble'},
}
if pr:next(1,4) == 1 then
item = 'default:torch '..tostring(pr:next(1,15))
elseif pr:next(1,4) == 1 then
item = 'default:apple '..tostring(pr:next(1,3))
elseif pr:next(1,6) == 1 then
item = 'default:sword_stone '..tostring(pr:next(2,5)*100)
end
elseif part == 't' then
local invcontent = {}
if pr:next(1,4) == 1 then
table.insert(invcontent, 'default:apple '..tostring(pr:next(1,5)))
end
if pr:next(1,3) == 1 then
table.insert(invcontent, 'default:cobble '..tostring(pr:next(1,5)))
end
if pr:next(1,3) == 1 then
table.insert(invcontent, 'default:torch '..tostring(pr:next(1,20)))
end
if pr:next(1,3) == 1 then
table.insert(invcontent, 'default:sword_stone '..tostring(pr:next(400,655)*100))
end
if pr:next(1,10) == 1 then
table.insert(invcontent, 'default:sword_steel '..tostring(pr:next(0,655)*100))
end
if pr:next(1,6) == 1 then
table.insert(invcontent, 'bucket:bucket_empty 1')
end
if pr:next(1,8) == 1 then
table.insert(invcontent, 'bucket:bucket_lava 1')
end
if pr:next(1,20) == 1 then
table.insert(invcontent, 'bucket:bucket_water 1')
end
if pr:next(1,34) == 1 then
table.insert(invcontent, 'bucket:nyancat 1')
table.insert(invcontent, 'bucket:nyancat_rainbow '..tostring(pr:next(1,6)))
end
if pr:next(1,2) == 1 then
table.insert(invcontent, 'default:gravel '..tostring(pr:next(1,10)))
end
if pr:next(1,30) == 1 then
table.insert(invcontent, 'default:bookshelf '..tostring(pr:next(1,2)))
end
if pr:next(1,8) == 1 then
table.insert(invcontent, 'default:cactus '..tostring(pr:next(1,2)))
end
if pr:next(1,40) == 1 then
table.insert(invcontent, 'default:rail '..tostring(pr:next(1,10)))
end
if pr:next(1,5) == 1 then
table.insert(invcontent, 'default:ladder '..tostring(pr:next(1,9)))
end
if pr:next(1,30) == 1 then
table.insert(invcontent, 'default:sign_wall 1')
end
if pr:next(1,60) == 1 then
table.insert(invcontent, 'default:steelblock '..tostring(pr:next(1,6)))
end
ns = {
{name='default:cobble'},
{name='air'},
{name='default:chest', inv=invcontent},
{name='default:cobble'},
}
else
return
end
for i=1,#ns do
local dy = top_y + 1 - i
local p2 = v3.new(p)
p2.y = p2.y + dy
local oldn = minetest.env:get_node(p2)
local n = ns[i]
if n and oldn.name ~= "air" then
if n.name == 'default:cobble' then
local perlin = minetest.env:get_perlin(123, 2, 0.8, 2.0)
if perlin:get3d(p2) >= 0.0 then
n.name = 'default:mossycobble'
end
end
minetest.env:set_node(p2, ns[i])
if n.inv then
local meta = minetest.env:get_meta(p2)
local inv = meta:get_inventory()
for _,itemstring in ipairs(n.inv) do
inv:add_item('main', itemstring)
end
end
end
end
if mob then
minetest.env:add_entity(v3.add(p, v3.new(0, mob_y, 0)), mob)
end
if item then
minetest.env:add_item(v3.add(p, v3.new(0, mob_y, 0)), item)
end
end
mobs.generate_vault = function(vault_def, p, dir, seed)
local dim_z = #vault_def
assert(dim_z > 0)
local dim_x = #vault_def[1]
if not v3.cmp(dir, v3.new(0,0,1)) then return end
--print("Making vault at "..minetest.pos_to_string(p))
--if dim_x >= 14 then
-- mobs.debug("Making large vault at "..minetest.pos_to_string(p))
--else
-- mobs.debug("Making vault at "..minetest.pos_to_string(p))
--end
-- Find door in definition
local def_door_p = nil
for dx=1,dim_x do
for dz=1,dim_z do
if vault_def[dim_z+1-dz][dx] == 'd' then
def_door_p = v3.new(dx,0,dz)
end
if def_door_p then break end
end
if def_door_p then break end
end
--print("Vault door found at "..minetest.pos_to_string(def_door_p).." in definition")
assert(def_door_p)
local pr = PseudoRandom(seed)
local randseed = seed
for dx=1,dim_x do
for dz=1,dim_z do
local p2 = v3.add(v3.sub(p, def_door_p), v3.new(dx, 0, dz))
local part = vault_def[dim_z+1-dz][dx]
--print("Making vault part "..dump(part).." at "..minetest.pos_to_string(p2))
mobs.make_vault_part(p2, part, pr)
randseed = randseed + 1
end
end
end
-- Definition is for Z=up, X=right, dir={x=0,y=0,z=1}
mobs.vault_defs = {
{
{'w','w','w','w','w','w','w','w','w','w'},
{'w','c','c','c','c','c','C','C','c','w'},
{'w','c','C','c','c','c','C','C','m','w'},
{'w','C','C','C','w','w','C','C','c','w'},
{'w','C','C','C','w','w','C','C','c','w'},
{'w','r','C','c','w','w','C','w','w','w'},
{'w','c','c','c','w','w','C','w',nil,nil},
{'w','w','w','w','w','w','C','w',nil,nil},
{nil,nil,nil,nil,nil,'w','d','w',nil,nil},
{nil,nil,nil,nil,nil,nil,'A',nil,nil,nil},
},
{
{'w','w','w','w','w','w','w','w'},
{'w','c','c','c','c','C','c','w'},
{'w','C','c','c','C','C','c','w'},
{'w','C','c','c','C','C','c','w'},
{'w','C','t','w','C','C','r','w'},
{'w','C','c','w','C','w','d','w'},
{'w','w','w','w','C','w','A',nil},
{'w','w','w','w','C','w',nil,nil},
{nil,nil,nil,'w','w','w',nil,nil},
},
{
{'W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W'},
{'W','l','l','l','l','l','l','c','i','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','c','l','l','l','l','l','f','f','l','l','l','l','l','m','W'},
{'W','c','l','l','l','l','l','f','f','l','l','l','l','l','c','W'},
{'W','c','l','l','t','f','f','f','f','l','l','l','l','l','c','W'},
{'W','c','l','l','l','l','l','f','f','l','l','l','l','l','c','W'},
{'W','m','l','l','l','l','l','f','f','l','l','l','l','l','c','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','l','l','l','l','l','l','f','f','l','l','l','l','l','l','W'},
{'W','W','W','W','W','W','W','W','d','W','W','W','W','W','W','W'},
{nil,nil,nil,nil,nil,nil,nil,nil,'A',nil,nil,nil,nil,nil,nil,nil},
},
{
{'w','w','w','w','w','w','w','w'},
{'w','c','c','c','m','C','c','w'},
{'w','C','c','c','C','C','c','w'},
{'w','C','C','C','C','C','C','w'},
{'w','C','C','C','C','C','C','w'},
{'w','C','C','C','C','C','C','w'},
{'w','C','C','C','C','C','C','w'},
{'w','C','c','c','C','C','r','w'},
{'w','C','c','w','C','w','d','w'},
{'w','c','w','w','C','w','A',nil},
{'w','c','C','C','C','w',nil,nil},
{nil,nil,nil,'w','w','w',nil,nil},
},
{
{'w','w','w','w','w','w','w','w'},
{'w','c','c','c','c','C','i','w'},
{'w','C','c','c','C','C','c','w'},
{'w','C','w','w','C','C','c','w'},
{'w','C','c','c','C','C','c','w'},
{'w','C','C','w','C','w','d','w'},
{'w','c','C','C','C','w','A',nil},
{'w','c','C','C','C','w',nil,nil},
{'w','C','C','C','C','C','C','w'},
{'w','C','C','C','C','C','C','w'},
{nil,nil,nil,'w','w','w',nil,nil},
},
{
{'w','w','w','w','w','w','w','w'},
{'w','c','c','c','c','C','c','w'},
{'w','C','c','c','w','C','c','w'},
{'w','C','w','w','w','w','c','w'},
{'w','C','C','C','C','C','c','w'},
{'w','C','c','w','C','w','d','w'},
{'w','i','w','w','C','w','A',nil},
{'w','c','C','C','C','w',nil,nil},
{nil,nil,nil,'w','w','w',nil,nil},
},
{
{'w','w','w','w','w','w','w','w'},
{'w','i','c','c','c','C','t','w'},
{'w','C','w','c','C','C','c','w'},
{'w','c','w','w','w','C','w','w'},
{'w','C','w','c','C','C','C','w'},
{'w','C','w','c','C','C','d','w'},
{'w','c','w','w','w','C','A',nil},
{'w','c','C','C','C','C',nil,nil},
{nil,nil,nil,'w','w','w',nil,nil},
},
{
{'W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','m','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','C','C','C','C','C','C','C','C','C','C','C','C','C','C','W'},
{'W','W','W','W','W','W','W','W','d','W','W','W','W','W','W','W'},
{nil,nil,nil,nil,nil,nil,nil,nil,'d',nil,nil,nil,nil,nil,nil,nil},
},
}
mobs.generate_random_vault = function(p, dir, pr)
seed = pr:next()
local vault_def = mobs.vault_defs[pr:next(1, #mobs.vault_defs)]
mobs.generate_vault(vault_def, p, dir, seed)
end
local generate_corridor = function(from, to, seed)
local pr = PseudoRandom(seed+92384)
local p = {x=from.x, y=from.y, z=from.z}
local step = 0
while p.x ~= to.x do
if step >= 5 and minetest.env:get_node(p).name == "air" then
return
end
step = step + 1
mobs.make_vault_part(p, 'C', pr)
if p.x > to.x then
p.x = p.x - 1
else
p.x = p.x + 1
end
end
local step = 0
while p.z ~= to.z do
if step >= 5 and minetest.env:get_node(p).name == "air" then
return
end
step = step + 1
mobs.make_vault_part(p, 'C', pr)
if p.z > to.z then
p.z = p.z - 1
else
p.z = p.z + 1
end
end
end
minetest.register_on_generated(function(minp, maxp, seed)
--[[if minp.x > maxp.x or minp.y > maxp.y or minp.z > maxp.z then
mobs.debug"foo")
return
end--]]
if minp.y > DUNGEON_Y or maxp.y < DUNGEON_Y then
return
end
local area = (maxp.x-minp.x+1)*(maxp.z-minp.z+1)
local possible_entrances = {}
local entrance = {
p = {x=0, y=-DUNGEON_Y, z=-5},
dir = {x=0, y=0, z=1},
}
table.insert(possible_entrances, entrance)
local pr = PseudoRandom(seed+931)
for i=0,area/300 do
local p1 = {
x = pr:next(minp.x, maxp.x),
y = DUNGEON_Y,
z = pr:next(minp.z, maxp.z),
}
local entrance = {
p = p1,
dir = {x=0, y=0, z=1},
}
table.insert(possible_entrances, entrance)
end
local pr = PseudoRandom(seed+9322)
local lastp = nil
if minp.x < 0 and maxp.x > 0 and minp.z < 0 and maxp.z > 0 then
lastp = {x=0, y=DUNGEON_Y, z=0}
end
for i,entrance in ipairs(possible_entrances) do
--mobs.debug("Entrance: "..dump(entrance))
mobs.generate_random_vault(entrance.p, entrance.dir, pr)
if lastp then
generate_corridor(lastp, entrance.p, pr:next())
end
lastp = entrance.p
end
if minp.x < 0 and maxp.x > 0 and minp.z < 0 and maxp.z > 0 then
p = {x=0, y=DUNGEON_Y+2, z=0}
minetest.env:set_node(p, {name="default:torch"})
end
end)
local function give_initial_stuff(player)
player:get_inventory():add_item('main', 'default:torch 99')
--player:get_inventory():add_item('main', 'default:shovel_steel')
--player:get_inventory():add_item('main', 'default:sword_steel')
--player:get_inventory():add_item('main', 'default:cobble 99')
end
minetest.register_on_newplayer(function(player)
player:setpos({x=0, y=DUNGEON_Y, z=0})
give_initial_stuff(player)
end)
minetest.register_on_respawnplayer(function(player)
player:setpos({x=0, y=DUNGEON_Y, z=0})
player:get_inventory():set_list("main", {})
player:get_inventory():set_list("craft", {})
give_initial_stuff(player)
return true
end)

632
mods/mobf/README Normal file
View File

@ -0,0 +1,632 @@
-------------------------------------------------------------------------------
Mob Framework Mod (former animals mod) provides a framework for creating mobs
(c) sapier (code,some graphics)
(c) rinoux (many 2D graphics)
Contact sapier a t gmx net
-------------------------------------------------------------------------------
Big thanks to rinoux for providing graphics and all others for their
suggestions too!
-------------------------------------------------------------------------------
No what is in?
-------------------------------------------------------------------------------
--hostile mobs--
Vombies
Hostile mobs spawning at night in unlighted areas. They can't withstand the
sun and will burn as sun rises
Dungeonmasters
Just dungenmasters throwing fireballs they spawn in dark stony areas too.
Big Red
They look like giant easter bunnys but beware of their plasma balls! Big red
spawns in dark stony areas
Boombomb
Don't get near they will explode
--slime--
will bounce in large caves below surface
--semi hostile mobs --
Wolf
A animal hiding in the woods that can be tamed to follow you
--friendly mobs --
Chicken
Cicken droping egs now and then
Sheep
Sheep that can be sheered, their wool will grow again. They randomly will
spawn on willows
Cow
Cows will spawn on willows and produce milk.
Deer
Will spawn below trees.
Rat
Just a small animal jumping around
Blue White Fish
Just a swimming animal
Gull
flys around over your head!
Clownfish
A funny small quick fish swiming around
--npc trader--
will sell goods
What can you do with them?
-------------------------------------------------------------------------------
Sheep -> take scissors and sheer them
Cow -> take an empty glass and get milk
Chicken -> collect eggs
Vombie -> drops bone on kill
Clownfish -> drops golden scale on kill
Wolf -> tame it!
NPC trader -> trading of course
How to catch mobs? Take a lasso or net to catch them, create a farm. Sheep,
chicken and cow will even breed if there's a filled barn nearby
Recieps:
-------------------------------------------------------------------------------
Lasso
none wool none
wool none wool
none wool none
Scissors
none iron_ingot none
none iron_ingot none
stick none stick
Net
wool none wool
none wool none
wool none wool
Barn
stick stick stick
wood wood wood
Small barn
stick stick
wood wood
Vombie trap
wood wood wood
wood scale_golden wood
wood wood wood
Settings (it's recommended to use mobf_settings command to change settings):
-------------------------------------------------------------------------------
mobf_enable_socket_trace = true/false
-->enable socket mod tracing
mobf_disable_3d_mode = true/false
-->disable 3d models
disable_vombie_3d_burn_animation = true/false
-->VOMBIE ONLY disable 3d burn animation
mobf_disable_animal_spawning = true/false
-->disable spawning of animals
mobf_blacklist = <serialized table of animal names>
-->disable all animals in table e.g.
mobf_blacklist = return {animal_sheep:sheep}
mobf_animal_spawning_secondary = true/false
-->enable/disable secondary spawning mechanism
NOTE secondary mechanism may cause serious lag issues sometimes
mobf_log_removed_entities = true/false
log removed mobs with reason of removal
vombie_3d_burn_animation_enabled = true/false
show vombie burn animation (may cause huge lag)
mobf_log_bug_warnings = true/false
show all mobf bug warnings
mobf_delete_disabled_mobs = true/false
delete mobs and spawners if a mob is disabled
Changelog:
-------------------------------------------------------------------------------
Changes 2.0.5
-fix bug in mobf_settings using settings_save instead of mobf_setting_save
Changes 2.0.4
-fixed bug trader selling without pay if price1 or price 2 not set
Changes 2.0.3
-fixed crash on typical state time not available
-fixed bug mob_slime typical state time not set
Changes 2.0.2
-fix crash with invalid targetpos in follow movgen
Changes 2.0.1
-fix crash on teleport of wolf
Changes 2.0.0
-documentation update
-add more items for trader
-fix price update bug in trader
-new cow textures
-improve sheep model
Changes 1.9.15
-fixed crash when mob got spawned at unloaded pos
-make mobf_version command available to all users
-reduce chance for trader building by factor 5
-code cleanup
-add support for tracing mob removal reason
-add clownfish mesh
-fix fish blue white swiming in wrong direction
Changes 1.9.14
-add vombie 3d mesh
-fix crash when trying to access empty movgen
-fix old default movgen definition was still used instead of default state
movgen in state change handling
-fixed deer model flying when lying
-fix crash in changestate
-fixed bug mobs disapearing as soon as attacking if spawned in slightly wrong
positions
-fixed boombomb and npc to use current api
-added big_red mesh
-fixed big_red not being agressive
-fixed wolf missing combat movegen
-added backtrace support
Changes 1.9.13
-add gull 3d mesh
-fix bug tamed wolf removed immediatly
-fix bug ostich_f can't be spawned
-revert accidentaly changed wolf to be not agressive
-fix mobs following don't jump
-fix height level check for flying mobs not working
-stop movement of inactive mobs
Changes 1.9.12
-rename debug class to avoid naming conflicts
-improve environment definition
-add ride support
-fixed punch mob not pushing back animals correctly
-fixed mobs don't respawn within torch ...
-fixed crash on show npc inventory due to changed api
-fixed drop avoidance not working if dropping to water (due to another fix)
-improve house spawning
-add ostrich
-improve collision checking for pos_is_ok
-add fish blue_white mesh
-add chatcommand show version
-add wolf mesh
-add sheep mesh
-fixed crash with secondary spawning enabled
-fixed crash on listactivemobs
-fixed boombomb still using abm based spawner
-updated dm model to 4aiman version
Changes 1.9.11
-added calf mesh
-added rat mesh
-replaced .x meshes by .b3d meshes
-fixed bug 3d inventory mob not facing player correctly
-fixed crash in random drop handling with incorrect dynamic_data
-mobs will face player on combat now
-improved follow behaviour (you can't get away that easy anymore)
-reduced damage done by mobs
Changes 1.9.10
-fixed group support for mobs
-fixed sun damage handler resetting animation to "stand"
-fix orientation not updated correct for mobs following new default state schema
-fixed bug 2d sprites not shown correctly
-fixed doku
-added support for upright sprites
-added 3d mesh model for dm
-added 3d mesh models for chicken
-added sounds for some mod
-added license information
-added support for mobf without fire mod
-added non inventory_plus settings gui (requres core patch)
-replaced custom debug chatcommand handlers by official minetest ones
Changes 1.9.9
-added trader spawning
-multiple fixes related to new state only modes
-fixed doku
Changes 1.9.8
-added trader support
-fixed sheep not being sheered
-added support for on_rightclick callbacks
Changes 1.9.7
-fixed bug big_willow spawner not enabled
-fixed bug steer not using big_willow spawner
-make mobs more smart in avoiding dropping
-make mobs more smart in avoiding falling into water
Changes 1.9.6
-catch misspelled blacklist string
-fix bug in barn crashing game
-fix bug custom on step handler making minetest unresponsive
-fixed crash on invalid blacklist
-added check to warn on unrealistic count of callbacks
-added statistics feature for mobf callback times
-make vombies and boombomb spawn in desert too
Changes 1.9.5
-fixed height level controling for fish and gulls (has been brocken for ages)
-added spawner based spawning for any abm based spawning
-"default" state is now mandatory for any mob!
Changes 1.9.4
-fixed sound name collisions
-fixed stereo->mono for some sounds
-added jordan4ibanez movement generator
Changes 1.9.3
-added lots of security checks to callback handlers
-added new tracing facilitys mobf_core and mobf_core_helper
-fixed additional bugs in entity replacement code of state changer
-fixed bug mobf crashing on no good position beeing known for mob when needed
Changes 1.9.2
-added workaround for lost dynamic data on saving of permanent data
-don't drown mobs but move to last known good position
-added animated 3d mesh cow and steer
Changes 1.9.1
-increased spawn rates of wolf
-drop correct meat
-add support for multiple drops
-fix lots of bugs in state change handling
-add animation support
-added experimental mob npc to demonstrate animation handling
Changes 1.9.0
-preserve orientation on replacement of entity
-add state support
-don't generate vaults if dm is disabled
-fix bug vaults generated above sea level
-added support for mesh 3d models
-use vessels drinking glass -> cow depends on vessels now!
-add support for removing disabled animals unknown objects
Changes 1.4.6
-added 3d dm
-added 3d big red
-added calfs
-added chicks
-added 3d Boombomb (was Creeper)
-added 3d clownfish
-added support for secondary spawning mechanisms
-change animal behaviour in case of unable to find safe new accel
-added on death callback support
-added function to get mob description
Changes 1.4.5
-added mobf registration of steer
-added get version api
-added support for blacklisting of animals
Changes 1.4.4
-fixed bug in mob orientation calculation
-improoved random movement generator
->don't invert speed on collision
->new random acceleration is dependent of old yaw now
->mpattern stop and go reduce start stop chances drastically
-added steer
-added rooster
-added breeding of cow and chicken
Changes 1.4.3
-fixed bug in deep caves spawn algorithm tried to print nil value maxlight
-fixed bug in walking through walkable nodes
-added new config file option mobf_disable_animal_spawning to disable automatic animal spawning
-added mapgen based willow spawn algorithm
-added mapgen based in_forrest spawn algorithm
Changes 1.4.2
-readded lost trap
-fixed bug in inventory check
Changes 1.4.1
-hopefully (haven't tested) fixed bug with mob killed by non player actor
-added dropping of results in case of player inventory full
-fixed bug in distance attack if distance attack is invalid
-fixed dm having invalid distance attack
-fixed big_red having invalid distance attack
Changes 1.4.0
-rename core to MOBF
-remove some dead code
-fixed bug in random drops entities lifetime not save
-fixed bug wrong parameter set to true on remove due to pop dens check
-fixed bug mobs running around like crazy after on_step frequency increase
-added spawn in deep caves spawn algorithm
-added special movement gen none only applying y acceleration but stopping mob
otherwise
Changes 1.3.5
-make vault generation much more quiet
-add function for adding movement pattern by external functions
Changes 1.3.4
-increase check frequency to 4/s
-fix jumpy cow and deer
-fixed dm and big_red spawning in lighted areas
-fixed bug harvest delay not enforced
-make cow a little bit more slim
-added vault generation code from celeron (slightly modified)
-reduce sound distance dm/cow/sheep/vombie
-fixed vombie model (was wrong direction)
-made vombie agressive again
-remove vombie at once if entity is activated at daytime
Changes 1.3.3
-disable luatrace
Changes 1.3.2
-split configuration of 2d 3d animals
-more 3d animals
-improoved base position checking
-added warnings for long abms
-added load/unload check
-removed spawn point registry
-removed minetest serialization
-readded remove animals script(non working atm)
Changes 1.3.1
-fixed regression unable to craft lasso and net
-added doxygen style comments
-added documentation
-fixed lot of code style breakages
-extracted environments from movement patterns
-moved prototype for path based movement generator to own folder
Changes 1.3.0
-added 3d fish blue white
-fixed another yaw bug
-fixed movement check bug
-fixed position prediction
-replaced animalmaterials:wool_? by wool:x
-added fire effect for fireball
-replaced custom serialization by new minetest function
-added fire for self destruct
-fix lasso and net recieps
-added wolf
Changes 1.2.93:
-added 3d barn
-fixed bug animals have wrong orientation
-fixed bug sheep get stuc in corner
-fixed cow model
Changes 1.2.92:
-added support for 3d animals
-fix bug in probabilistic movement gen when handling min speed
-fixed movement of stop_and_go animals
-made Sheep 3D
-made Cow 3D
Changes 1.2.91:
-fixed bugs in animals sheep
-updated animalmaterials to new syntax
Changes 1.2.90:
-added support for surface differentiation in good/ok/bad
-added support for different movement generators
-added fight mode movement generator change
-fixed bug barn depending on vombie instead of sheep
-reduced cpu load in normal movement loop
-adjusted probability values to new check cycle
Changes 1.2.1:
-use official health system
-add group support for all animals
-scissors get damaged by harvesting wool
-add footstep sounds
Changes 1.2.0:
-added sound support
-(non working) path based movement gen stub
Changes 1.1.0:
-support for prefered environment
-performance improvements (mainly in spawning algorithms)
-internal reorganization
-added on_hit_callback and on_kill_callback in fighting subsystem
Changes 1.0.0:
-added changes required for flying animals
-added lifetime support (it's only used for breeding right now)
-for any animal a item is created by default
-added gull
-added clownfish
-added vombie trap
-added barn (breed sheep!)
Changes 0.9.11:
-animal definitions have been moved to different mods
-player punch pushes back animal
Changes 0.9.9:
-MAJOR code reorganization
-fixed creeper
-added collision boxes with correct height
(width ain't possible as sprites are rotated automaticaly)
-added some missing item graphics
-fixed a lot of small bugs probably not even discovered
-improoved big red graphic
-added initial documentation
Changes 0.9.0:
-changed harvesting to LMB
-fixed bug making rats jump to high
-adjusted to new git 20120122
-make dungeon master more dangerous
-added glass for harvesting milk
Changes 0.8.9:
-added net
-added fish support
-added rat
Changes 0.8.3:
-added creeper
-added movement pattern support -> support for different movement styles
-added lua script "remove_animals.lua" (rename to init.lua to remove all animals from your world)
Changes 0.8.2:
-added cow graphics from rinoux
-added some item images from rinoux
-fixed unnecessary workaround in line of sight code by correct implementation
Changes 0.8.1:
-bugfix for release error
Changes 0.8.0:
-added deer graphics from rinoux
-added distance attack support (inspired by jeija mod throwing)
-added plasma and fireballs
-added dungeonmaster
Changes 0.7.3:
-fixed some issues with spawning to many animals in case of frequent server restarts
-added chicken (big thanks to rinoux)
-added new graphics for sheep (even more thanks to rinoux for providing them)
Changes 0.7.0:
-added support for animals following target while fighting
-make hostile animals even more agressive
-add vampire spawning all over the world during night ;-)
Changes 0.6.0:
-added support for looking directions
-added support for animals attacking player on their own
Latest git from 20120103 required!
Changes 0.5.0:
-fixed bug animals disapearing if more than one animal is at same position
-switched to new lua api
Latest build 20120102 required!
Changes 0.4.5:
-fixed spawn algorithms not correctly ensuring population density of animals
Changes 0.4.3:
-added lasso for catching animals
Changes 0.4.2:
-add scissors for harvesting wool
Changes 0.4.1:
-fixed bug mod not running on stock 0.4 dev
-big red now spawns in shadows only
Changes 0.4.0:
-added support for animals spawning
Changes 0.3.0:
-added support for animals fighting back
Changes 0.2.2:
-animal may jump 1 block but only if it's a "natural" surface (to avoid jumping over fences, walls e.g.). Natural surfaces are by now dirt, sand, stone and clay.
Changes 0.2.1:
-animal size increased
Changes 0.2.0:
-added cow
-added deer
-fixed bugs in base functionality used by deer and cow
Changes 0.1.1:
-fixed bug crashing server on rightclick
Changes 0.1.0:
-Major improovements in motion generation code
-switched to modular design (still needed to create a single file till next dev release of minetest, if you've already got the master minetest you may rename init_modular.lua to init)
-added support for:
*animals harvestable
*animals transforming on harvest
*animals retransforming after specified amount of time
-prepared for:
*animals harvestable when wearing special tool
*animals harvestable by consuming special tool
Links:
http://www.mediafire.com/?axsikpooji7hq16 Version 0.1.1 (there will be sheep)
http://www.mediafire.com/?yo6b8cyq0rt6b51 Version 0.2.0 (diversity growing)
http://www.mediafire.com/?v45fue5z6hidkjw Version 0.2.1 (no tiny animals)
http://www.mediafire.com/?1l4cfj35kadjf1l Version 0.2.2 (let em jump)
http://www.mediafire.com/?leyjbj5831gc6zm Version 0.3.0 (don't make them angry)
http://www.mediafire.com/?ilslao0gi2ulwev Version 0.4.1 (big red is hiding)
http://www.mediafire.com/?tkamh7a5r6mlsze Version 0.4.2 (let there be tools)
http://www.mediafire.com/?ib5ax24vp54h938 Version 0.4.3 (catch em all)
http://www.mediafire.com/?m9dfrqq6fdoamnz Version 0.4.5 (catch em all)
http://www.mediafire.com/?22ex6fn8d49deoy Version 0.5.0 (none disappearing)
http://www.mediafire.com/?a0g894d4b90acax Version 0.6.0 (be carefull)
http://www.mediafire.com/?mym9dyjbnb98cmo Version 0.7.0 ( do you fear the night?)
http://www.mediafire.com/?3lw27vuzxn7cyy8 Version 0.7.3 (still fear the night?)
http://www.mediafire.com/?3ah17kl2uhg0437 Version 0.8.1 (stay distant!)
http://www.mediafire.com/?j447z7q3rfim8iv Version 0.8.2 (stay distant!)
http://www.mediafire.com/?1b9rcez7dbdnbuu Version 0.8.3 (stay distant!)
http://www.mediafire.com/?r4jke39dfv218yj Version 0.8.9 (have a swim)
http://www.mediafire.com/?5w9a5vc55az2dqq Version 0.9.0 (left "hand" harvesting)
http://www.mediafire.com/?y8k42doeebo2c5o Version 0.9.9 (cleanup)
http://www.mediafire.com/?hl37vzqb5kzra05 Version 0.9.11 (cleanup)
http://www.mediafire.com/?zty2qzn9hjtxl1s Version 1.0.0 (let them breed)
http://www.mediafire.com/?xax85pwsno1incc Version 1.1.0 (speed it up)
http://www.mediafire.com/?ze274bvlxtjsk1c Version 1.1.1 (speed it up)
http://www.mediafire.com/?gn0f1e005wtmsom Version 1.2.0 (make some noise)
http://www.mediafire.com/?zkf54fckd7w2g3f Version 1.2.1 (hit them)
http://www.mediafire.com/?7hm2xm51aoytbua Version 1.2.90 (loose weight)
http://www.mediafire.com/?lnw1bcs5gq6au83 Version 1.2.91 (fix it)
http://www.mediafire.com/?axuhv41wc2ch7yl Version 1.2.92 (enter the 3rd dimension)
http://www.mediafire.com/?vldqym8hrasjbuk Version 1.2.93 (finding the right direction)
http://www.mediafire.com/?1ne46d88ej6qlus Version 1.3.0 (let there be pets)
http://www.mediafire.com/?vr9w97ac350u215 Version 1.3.1 (paperworks)
http://www.mediafire.com/?25an9a14ty4ifat Version 1.3.3 (no trace)
http://www.mediafire.com/?p8a3wkl3s936n6o Version 1.3.4 (finetuning)
http://www.mediafire.com/?jmm1g4j2j4hp8d4 Version 1.4.0 (slimy)
http://www.mediafire.com/?goyb9r7e94h8gwl Version 1.4.1 (dropper)
http://www.mediafire.com/?63o9qgycaccb3q3 Version 1.4.2 (dropper)
http://www.mediafire.com/?jcocc1vop19a3kj Version 1.4.3 (no respawn)
http://www.mediafire.com/?b8hw3fo3599wbtd Version 1.4.4 (more breeding)
http://www.mediafire.com/?5cuh2lz2m12915p Version 1.4.5 (more breeding)
http://www.mediafire.com/?ds4g46v3dhnkdg9 Version 1.9.0 (sleeping sheep) --experimental
http://www.mediafire.com/?umbcd1ptax9bbcb Version 1.9.1 (animating!) --experimental
http://www.mediafire.com/?66v84hi9p43hw7o Version 1.9.6 (faster?) --experimental
http://www.mediafire.com/?5z5wz2d329m849k Version 1.9.7 (frightened about water) --experimental

188
mods/mobf/api.lua Normal file
View File

@ -0,0 +1,188 @@
-------------------------------------------------------------------------------
-- 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
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- name: 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
-------------------------------------------------------------------------------
-- name: 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
-------------------------------------------------------------------------------
-- name: mobf_get_version()
--
--! @brief get version of mob framework
--! @ingroup framework_mob
--
--! @return mobf version
-------------------------------------------------------------------------------
function mobf_get_version()
return mobf_version
end
------------------------------------------------------------------------------
-- name: 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 mob.name == nil or
mob.modname == nil then
minetest.log(LOGLEVEL_ERROR,"MOBF: name and modname are mandatory for ALL mobs!")
return false
end
--check if mob may be added
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_mob_item(mob.name,mob.modname,mob.generic.description)
--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
--spawn mechanism handling
if not minetest.setting_getbool("mobf_disable_animal_spawning") then
--register spawn callback to world
if environment_list[mob.generic.envid] ~= nil then
local secondary_name = ""
if mob.harvest ~= nil then
secondary_name = mob.harvest.transforms_to
end
dbg_mobf.mobf_core_lvl3("MOBGF: Environment to use: " .. tostring(mob.generic.envid))
if mobf_spawn_algorithms[mob.spawning.algorithm] ~= nil and
type(mobf_spawn_algorithms[mob.spawning.algorithm].register_spawn) == "function" then
mobf_spawn_algorithms[mob.spawning.algorithm].register_spawn(mob.modname..":"..mob.name,
secondary_name,
mob.spawning,
environment_list[mob.generic.envid])
else
dbg_mobf.mobf_core_lvl2("MOBGF: " .. mob.name
.. " no primary spawn algorithm defined: "
.. tostring(mob.spawning.algorithm))
end
if minetest.setting_getbool("mobf_animal_spawning_secondary") then
if mob.spawning.algorithm_secondary ~= nil and
type(mobf_spawn_algorithms[mob.spawning.algorithm_secondary].register_spawn) == "function" then
mobf_spawn_algorithms[mob.spawning.algorithm_secondary].register_spawn(mob.modname..":"..mob.name,
secondary_name,
mob.spawning,
environment_list[mob.generic.envid])
end
end
else
minetest.log(LOGLEVEL_ERROR,"MOBF: specified mob >" .. mob.name
.. "< without environment!")
end
else
dbg_mobf.mobf_core_lvl3("MOBF: MOB spawning disabled!")
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
------------------------------------------------------------------------------
-- name: 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
------------------------------------------------------------------------------
-- name: 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
------------------------------------------------------------------------------
-- name: 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

View File

@ -0,0 +1,71 @@
-------------------------------------------------------------------------------
-- 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.env:remove_node(pos)
minetest.env: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.env:remove_node(pos)
minetest.env: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.env:remove_node(pos)
minetest.env: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.env:remove_node(pos)
minetest.env:add_node(pos,{name="wool:black"})
end
})
-------------------------------------------------------------------------------
-- compatibility functions to make transition to new name easier
-------------------------------------------------------------------------------
function animals_add_animal(animal)
mobf_add_mob(animal)
end

264
mods/mobf/debug.lua Normal file
View File

@ -0,0 +1,264 @@
-------------------------------------------------------------------------------
-- 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
-------------------------------------------------------------------------------
--! @defgroup debug_in_game In game debugging functions
--! @brief debugging functions to be called from in game
--! @ingroup framework_int
--! @{
mobf_debug = {}
-------------------------------------------------------------------------------
-- name: 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
-------------------------------------------------------------------------------
-- name: 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)
print("name: " .. name .. " param: " .. dump(param))
local parameters = param:split(" ")
if #parameters ~= 2 then
mobf_debug.print_usage(name,"spawnmob")
return
end
local pos_strings = parameters[2]:split(",")
if #pos_strings ~= 3 then
mobf_debug.print_usage(name,"spawmob")
return
end
if mobf_is_known_mob(parameters[1]) ~= true then
mobf_debug.print_usage(name,"ukn_mob", ">"..parameters[1].."<")
return true
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],"__default",spawnpoint,"mobf_debug_spawner")
end
-------------------------------------------------------------------------------
-- name: 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
-------------------------------------------------------------------------------
-- name: 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.env: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", "animalmaterials:glass 10")
end
end
-------------------------------------------------------------------------------
-- name: 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
-------------------------------------------------------------------------------
-- name: init()
--
--! @brief initialize debug commands chat handler
--
------------------------------------------------------------------------------
function mobf_debug.init()
minetest.register_chatcommand("spawnmob",
{
params = "<name> <pos>",
description = "spawn a mob at position" ,
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
})
if mobf_rtd.luatrace_enabled then
minetest.register_chatcommand("traceon",
{
params = "",
description = "start luatrace tracing" ,
privs = {mobfw_admin=true},
func = luatrace.tron()
})
minetest.register_chatcommand("traceon",
{
params = "",
description = "stop luatrace tracing" ,
privs = {mobfw_admin=true},
func = luatrace.troff()
})
end
end
-------------------------------------------------------------------------------
-- name: handle_spawnhouse(name,message)
--
--! @brief spawn small house
--
--! @param entity entity rightclicked
--! @param player player doing rightclick
------------------------------------------------------------------------------
function mobf_debug.rightclick_callback(entity,player)
local lifetime = mobf_get_current_time() - entity.dynamic_data.spawning.original_spawntime
print("MOBF: " .. entity.data.name .. " is alive for " .. lifetime .. " seconds")
print("MOBF: \tCurrent state: " .. entity.dynamic_data.state.current )
print("MOBF: \tCurrent movgen: " .. entity.dynamic_data.current_movement_gen.name )
if entity.dynamic_data.current_movement_gen.name == "follow_mov_gen" then
local basepos = entity.getbasepos(entity)
local targetpos = entity.dynamic_data.spawning.spawnpoint
if entity.dynamic_data.movement.guardspawnpoint ~= true then
targetpos = entity.dynamic_data.movement.target:getpos()
end
print("MOBF: \t\tmovement state: " .. mgen_follow.identify_movement_state(basepos,targetpos) )
end
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))
print("MOBF: \tCurrent accel: " .. printpos(entity.object:getacceleration()))
print("MOBF: \tCurrent speed: " .. printpos(entity.object:getvelocity()))
return false
end
--!@}

96
mods/mobf/debug_trace.lua Normal file
View File

@ -0,0 +1,96 @@
-------------------------------------------------------------------------------
-- 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,
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,
}

2
mods/mobf/depends.txt Normal file
View File

@ -0,0 +1,2 @@
default
animalmaterials

1774
mods/mobf/doc/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,46 @@
-------------------------------------------------------------------------------
-- 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-01-27
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @mainpage Mob Framework Mod 2.0.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

View File

@ -0,0 +1,361 @@
-------------------------------------------------------------------------------
-- 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 = nil,
--! @brief [OPTIONAL] custom on_step(entity) callback called after normal on_step handling is done
custom_on_step_handler = nil,
},
--! @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] 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] 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] configuration of distance attack
distance = {
--! @brief [MANDATORY] distance projectile to issue
attack="some_entity",
--! @brief [MANDATORY] distance to issue an attack
range=10,
--! @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] basename of file
name="random_1",
--! @brief [MANDATORY] minimum time between playing sounds
min_delta = 10,
--! @brief [MANDATORY] chance per second to play sound after minimum time
chance = 0.5,
--! @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] 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 [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
custom_preconhandler = 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] 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] 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] 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"},
}
}

604
mods/mobf/environment.lua Normal file
View File

@ -0,0 +1,604 @@
-------------------------------------------------------------------------------
-- 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 environment.lua
--! @brief component for environment related functions
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup environment Environment subcomponent
--! @brief Environment check functions used by different subcomponents
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
environment = {}
--! @brief list of known environments
--! @memberof environment
environment_list = {}
-------------------------------------------------------------------------------
-- name: get_suitable_pos_same_level(pos_raw,maxsearcharea,entity)
--
--! @brief find a position suitable around a specific position
--! @ingroup environment
--
--! @param pos_raw position to look at
--! @param maxsearcharea max range to look for suitable position
--! @param entity mob to look for position
--! @param accept_possible return position thats possible only too
--! @return {x,y,z} position found or nil
-------------------------------------------------------------------------------
function environment.get_suitable_pos_same_level(pos_raw,maxsearcharea,entity,accept_possible)
dbg_mobf.movement_lvl3("MOBF: --> get_suitable_pos_same_level "
.. printpos(pos_raw))
local pos = mobf_round_pos(pos_raw)
dbg_mobf.movement_lvl1("MOBF: Starting pos is "..printpos(pos)
.." max search area is "..maxsearcharea)
local e1 = "|"
local e2 = "|"
local e3 = "|"
local e4 = "|"
local possible_targets = {}
--search next position on solid ground
for search=1, maxsearcharea,1 do
--TODO randomize search order
--find along edge 1
for current=-search,search,1 do
local pos_tocheck = { x= pos.x + current,y=pos.y,z=pos.z -search}
local pos_state = environment.pos_is_ok(pos_tocheck,entity)
dbg_mobf.movement_lvl1("MOBF: state of "..printpos(pos_tocheck).." is "
.. pos_state)
if pos_state == "ok" then
dbg_mobf.movement_lvl1("found new pos")
table.insert(possible_targets, pos_tocheck)
elseif pos_state == "possible_surface" and
accept_possible then
table.insert(possible_targets, pos_tocheck)
-- elseif pos_state == "collision_jumpabe" then
-- dbg_mobf.movement_lvl1("found new pos above")
-- return {x=pos_tocheck.x,y=pos_tocheck.y+1,z=pos_tocheck.z}
else
e1 = e1..pos_state.."|"
end
end
--find along edge 2
for current=-(search-1),(search-1),1 do
local pos_tocheck = { x= pos.x + search,y=pos.y,z=pos.z + current}
local pos_state = environment.pos_is_ok(pos_tocheck,entity)
dbg_mobf.movement_lvl1("MOBF: state of "..printpos(pos_tocheck).." is "
.. pos_state)
if pos_state == "ok" then
dbg_mobf.movement_lvl1("found new pos")
table.insert(possible_targets, pos_tocheck)
elseif pos_state == "possible_surface" and
accept_possible then
table.insert(possible_targets, pos_tocheck)
else
e2 = e2..pos_state.."|"
end
end
--find along edge 3
for current=search,-search,-1 do
local pos_tocheck = { x= pos.x + current,y=pos.y,z=pos.z + search}
local pos_state = environment.pos_is_ok(pos_tocheck,entity)
dbg_mobf.movement_lvl1("MOBF: state of "..printpos(pos_tocheck).." is "
.. pos_state)
if pos_state == "ok" then
dbg_mobf.movement_lvl1("found new pos")
table.insert(possible_targets, pos_tocheck)
elseif pos_state == "possible_surface" and
accept_possible then
table.insert(possible_targets, pos_tocheck)
else
e3 = e3..pos_state.."|"
end
end
--find along edge 4
for current=(search-1),-(search-1),-1 do
local pos_tocheck = { x= pos.x -search,y=pos.y,z=pos.z + current}
local pos_state = environment.pos_is_ok(pos,entity)
dbg_mobf.movement_lvl1("MOBF: state of "..printpos(pos_tocheck).." is "
.. pos_state)
if pos_state == "ok" then
dbg_mobf.movement_lvl1("found new pos")
table.insert(possible_targets, pos_tocheck)
elseif pos_state == "possible_surface" and
accept_possible then
table.insert(possible_targets, pos_tocheck)
else
e4 = e4..pos_state.."|"
end
end
end
-- print("MOBF: Bug !!! didn't find a suitable position to place mob")
-- print("Surrounding of " .. printpos(pos_raw) .. "was:")
-- print(e1)
-- print(" " .. e2)
-- print(e4)
-- print(e3)
if #possible_targets > 0 then
local i = math.random(1, #possible_targets)
return possible_targets[i]
end
return nil
end
-------------------------------------------------------------------------------
-- name: is_media_element(nodename,environment)
--
--! @brief check if nodename is in environment
--! @ingroup environment
--
--! @param nodename name to check
--! @param media environment of mob
--! @return true/false
------------------------------------------------------------------------------
function environment.is_media_element( nodename, media )
--security check
if media == false then
mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!!! no environment specified!")
return false
end
for i,v in ipairs(media) do
if v == nodename then
return true
end
end
dbg_mobf.environment_lvl2("MOBF: " .. nodename .. " is not within environment list:")
for i,v in ipairs(media) do
dbg_mobf.environment_lvl3("MOBF: \t" .. v)
end
return false
end
-------------------------------------------------------------------------------
-- name: get_absolute_min_max_pos(env, pos)
--
--! @brief check if nodename is in environment
--! @ingroup environment
--
--! @param env environment mob should be
--! @param pos position it is currently
--! @return { minpos,maxpos }
------------------------------------------------------------------------------
function environment.get_absolute_min_max_pos(env,pos)
local node = minetest.env:get_node(pos)
--if is not within environment it should be return current position
--as min max
if environment.is_media_element(node.name,env.media) == false then
return pos.y,pos.y
end
local min_y = env.min_height_above_ground
local max_y = env.max_height_above_ground
--a fully generic check isn't possible here so we need to use media
--specific ways ... it's ugly but works
if node.name == "air" then
min_y = min_y + ( pos.y - mobf_surface_distance(pos))
max_y = max_y + ( pos.y - mobf_surface_distance(pos))
end
if node.name == "default:water" or
node.name == "defailt:water_flowing" then
-- water mobs do use min/max directly
end
if node.name == "default:lava" or
node.name == "default:lava_flowing" then
--TODO e.g. lava fish
--not implemented by now
end
return min_y,max_y
end
-------------------------------------------------------------------------------
-- name: is_jumpable_surface(name)
--
--! @brief check if name is a surface an mob may jump onto
--! @ingroup environment
--
--! @param name name to check
--! @return true/false
-------------------------------------------------------------------------------
function environment.is_jumpable_surface(name)
if name == "default:dirt" or
name == "default:dirt_with_grass" or
name == "default:stone" or
name == "default:sand" or
name == "default:clay"
then
return true
end
dbg_mobf.environment_lvl1("MOBF: is "..name.." a jumpable surface?")
return false
end
-------------------------------------------------------------------------------
-- name: checksurfacek(pos,surfaces)
--
--! @brief check if a position is suitable for an mob
--! @ingroup environment
--
--! @param pos position to check
--! @param surface surfaces valid
--! @return true on valid surface false if not
-------------------------------------------------------------------------------
function environment.checksurface(pos,surface)
--if no surfaces are specified any surface is treated as ok
if surface == nil then
return "ok"
end
local pos_below = {x=pos.x,y=pos.y-1,z=pos.z}
local node_below = minetest.env:get_node(pos_below)
if node_below == nil then
return "ok"
end
for i,v in ipairs(surface.good) do
if node_below.name == v then
return "ok"
end
end
if surface.possible ~= nil then
for i,v in ipairs(surface.possible) do
if node_below.name == v then
return "possible_surface"
end
end
end
return "wrong_surface"
end
-------------------------------------------------------------------------------
-- name: get_min_max_ground_dist(entity)
--
--! @brief calculate absolute minimum and maximum height for a mob
--! @ingroup environment
--
--! @param entity mob to check
--! @return min y val,max y val
-------------------------------------------------------------------------------
function environment.get_min_max_ground_dist(entity)
local min_ground_distance = 0
local max_ground_distance = 0
if entity.environment.max_height_above_ground ~= nil then
max_ground_distance = entity.environment.max_height_above_ground
end
if entity.environment.min_height_above_ground ~= nil then
min_ground_distance = entity.environment.min_height_above_ground
end
if entity.data.movement.canfly == nil or
entity.data.movement.canfly == false then
max_ground_distance = 1
end
return min_ground_distance,max_ground_distance
end
-------------------------------------------------------------------------------
-- name: pos_is_ok(pos,entity)
--
--! @brief check if a position is suitable for an mob
--! @ingroup environment
--
--! @param pos position to check
--! @param entity mob to check
--! @param dont_do_jumpcheck
--! @return suitability of position for mob values:
--! -ok -@>position is ok
--! -collision -@>position is within a node
--! -collision_jumpable -@>position is within a node that can be jumped onto
--! -drop -@>position is a drop
--! -drop_above_water -@>position is to far above water
--! -above_water -@>position is right over water
--! -in_water -@>position is within a water node(source or flow)
--! -in_air -@>position is in air
--! -above_limit -@>position is above level limit
--! -below_limit -@>position is below level limit
--! -wrong_surface -@>position is above surface mob shouldn't be
--! -invalid -@>unable to check position
-------------------------------------------------------------------------------
function environment.pos_is_ok(pos,entity,dont_do_jumpcheck)
local min_ground_distance,max_ground_distance = environment.get_min_max_ground_dist(entity)
local cornerpositions = {}
table.insert(cornerpositions,pos)
--read positions at corners
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 lastpos = nil
local retval = "temp_ok"
--check if mob at pos will be in correct environment
for i=1,#cornerpositions,1 do
if not mobf_pos_is_same(lastpos,cornerpositions[i]) then
local node_to_check = minetest.env:get_node(cornerpositions[i])
if node_to_check == nil then
mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!!! checking position with invalid node")
retval = "invalid"
break
end
if not environment.is_media_element(node_to_check.name,entity.environment.media) == true then
dbg_mobf.environment_lvl3("MOBF: " .. i .. ": " ..
printpos(cornerpositions[i]) .. " -- " .. printpos(pos) ..
" not within environment")
if mobf_pos_is_same(pos,cornerpositions[i]) then
if node_to_check.name == "default:water_source" or
node_to_check.name == "default:water_flowing" then
retval = "in_water"
break
end
if node_to_check.name == "air" then
retval = "in_air"
break
end
--TODO maybe replace by "invalid medium"
else
retval = "collision"
end
end
end
lastpos = cornerpositions[i]
end
--
if retval == "temp_ok" then
dbg_mobf.environment_lvl2("MOBF: \tin environment")
local ground_distance = mobf_ground_distance(pos,entity.environment.media)
--following return codes are only usefull for non flying
if entity.data.movement.canfly == nil or
entity.data.movement.canfly == false then
if mobf_above_water(pos) then
if ground_distance > max_ground_distance then
dbg_mobf.environment_lvl2("MOBF: \tdropping above water")
retval = "drop_above_water"
end
dbg_mobf.environment_lvl2("MOBF: \tabove water")
retval = "above_water"
end
if ground_distance > max_ground_distance then
dbg_mobf.environment_lvl2("MOBF: \tdropping "
.. ground_distance .. " / " .. max_ground_distance)
retval = "drop"
else
dbg_mobf.environment_lvl2("MOBF: \tsurface dependent")
retval = environment.checksurface(pos,entity.environment.surfaces)
end
else
local miny,maxy = environment.get_absolute_min_max_pos(entity.environment,pos)
dbg_mobf.environment_lvl2("MOBF: \tflying mob detected, min: "
.. miny .. " max: " .. maxy .. " current: " .. pos.y)
if pos.y < miny then
retval = "below_limit"
else if pos.y > maxy then
retval = "above_limit"
else
retval = environment.checksurface(pos,entity.environment.surfaces)
end end
end
end
if retval == "collision" and not dont_do_jumpcheck then
dbg_mobf.environment_lvl2("MOBF: check if pos is jumpable")
local upper_pos_state = environment.pos_is_ok({x=pos.x,
y=pos.y+1,
z=pos.z},
entity,true)
if upper_pos_state == "ok" then
retval = "collision_jumpable"
else
dbg_mobf.environment_lvl2("MOBF: upper pos state was: " .. upper_pos_state)
end
end
return retval
end
-------------------------------------------------------------------------------
-- name: get_default_gravity(pos,environment,canfly)
--
--! @brief get default acceleration depending on mobs medium and pos
--! @ingroup environment
--
--! @param pos position where to check gravity
--! @param media mobs movement medium
--! @param canfly is mob capable of flying?
--! @return y-acceleration
------------------------------------------------------------------------------
function environment.get_default_gravity(pos,media,canfly)
if pos == nil then
return nil
end
local node = minetest.env:get_node(pos)
--if an mob can't fly or isn't within it's medium default acceleration
-- for it's current medium is applied
if canfly == nil or
canfly == false or
environment.is_media_element(node.name,media) == false
then
if (node.name == "air") then
return -9.81
end
if node.name == "default:water_source" or
node.name == "default:water_flowing" then
return -2.5
end
if node.name == "default:lava" then
return 0.1
end
--mob is at invalid position thus returning default air acceleration
return -9.81
end
return 0
end
-------------------------------------------------------------------------------
-- name: fix_base_pos(entity, middle_to_bottom)
--
--! @brief fix the mobs y position according to model or sprite height
--! @ingroup environment
--
--! @param entity mob to fix base position
--! @param center_to_bottom distance from center of mob to its bottom (absolute value)
--! @return new position set by function
------------------------------------------------------------------------------
function environment.fix_base_pos(entity, center_to_bottom)
if center_to_bottom > 0.5 then
local pos = entity.object:getpos()
local node_pos = minetest.env:get_node(pos)
local pos_to_check = {x=pos.x,y=pos.y-center_to_bottom+0.1,z=pos.z}
local node_pos_check = minetest.env:get_node(pos_to_check)
if node_pos ~= nil and
node_pos_check ~= nil then
dbg_mobf.environment_lvl3("MOBF: fixing y position / base position required? "
.. node_pos.name .. " " .. node_pos_check.name)
if node_pos.name ~= node_pos_check.name then
distance_to_ground = mobf_surface_distance(pos)
pos.y = pos.y + (center_to_bottom - distance_to_ground +0.5)
dbg_mobf.environment_lvl2("MOBF: fixing y position of " .. entity.data.name
.. " got distance " .. center_to_bottom .. " moving to " ..printpos(pos))
entity.object:moveto(pos)
end
end
end
return entity.getbasepos(entity)
end
-------------------------------------------------------------------------------
-- name: register(name, environment)
--
--! @brief register an environment to mob framework
--! @ingroup environment
--
--! @param name id of environment
--! @param environment description of environment
--! @return true/false succesfully registred environment
-------------------------------------------------------------------------------
function environment.register(name, environment)
if environment_list[name] ~= nil then
return false
end
environment_list[name] = environment
return true
end
-------------------------------------------------------------------------------
-- name: pos_state_is_impossible(entity,pos)
--
--! @brief checks if a entity can be there (not if it would move there by its own)
--! @ingroup environment
--
--! @param entity entity to check
--! @param pos position to check
--! @return true entity may be there, entity can never be there
-------------------------------------------------------------------------------
function environment.possible_pos(entity,pos)
local state = environment.pos_is_ok(pos,entity)
if state == "collision" or
state == "collision_jumpable" or
state == "invalid" then
return false
end
return true
end
--!@}
dofile (mobf_modpath .. "/environments/general_env_sets.lua")
dofile (mobf_modpath .. "/environments/flight_1.lua")
dofile (mobf_modpath .. "/environments/meadow.lua")
dofile (mobf_modpath .. "/environments/on_ground_1.lua")
dofile (mobf_modpath .. "/environments/on_ground_2.lua")
dofile (mobf_modpath .. "/environments/open_waters.lua")
dofile (mobf_modpath .. "/environments/shallow_waters.lua")
dofile (mobf_modpath .. "/environments/simple_air.lua")

View File

@ -0,0 +1,33 @@
-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file flight_1.lua
--! @brief a environment description for in flight mobs
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup environments
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct env_flight_1
--! @brief flying mobs in height between 13-23 blocks above surface
env_flight_1 = {
media = {
"air"
},
surfaces = nil,
--ground is first node above/below not beeing of media type
max_height_above_ground = 23,
min_height_above_ground = 13
}
--!@}
environment.register("flight_1", env_flight_1)

View File

@ -0,0 +1,37 @@
-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file flight_1.lua
--! @brief a environment description for in flight mobs
--! @copyright Sapier
--! @author Sapier
--! @date 2013-01-06
--
--! @addtogroup environments
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @addtogroup predev_env_sets
--! @{
mobf_env_placable_items = {
"default:rail",
"default:ladder",
"default:torch",
"default:sign_wall",
}
mobf_env_plants = {
"default:junglegrass",
"default:papyrus",
"default:sapling",
"default:apple"
}
--@}
--@}

View File

@ -0,0 +1,48 @@
-------------------------------------------------------------------------------
-- 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 meadow.lua
--! @brief meadow environment description
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup environments
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct env_meadow
--! @brief environment for mobs that prefere green meadows but may walk on
--! dirt and sand too
env_meadow = {
media = {
"air",
},
surfaces = {
good = {
"default:dirt_with_grass"
},
possible = {
"default:dirt",
"default:sand",
},
},
}
--!@}
table.foreach(mobf_env_placable_items,
function(index)
table.insert(env_meadow.media,mobf_env_placable_items[index])
end)
table.foreach(mobf_env_plants,
function(index)
table.insert(env_meadow.media,mobf_env_plants[index])
end)
environment.register("meadow", env_meadow)

View File

@ -0,0 +1,38 @@
-------------------------------------------------------------------------------
-- 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 on_ground_1.lua
--! @brief a environment description for mobs on ground
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup environments
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct env_on_ground_1
--! @brief an environment for mobs capable of walking through junglegrass
env_on_ground_1 = {
media = {
"air",
}
}
--!@}
table.foreach(mobf_env_placable_items,
function(index)
table.insert(env_on_ground_1.media,mobf_env_placable_items[index])
end)
table.foreach(mobf_env_plants,
function(index)
table.insert(env_on_ground_1.media,mobf_env_plants[index])
end)
environment.register("on_ground_1", env_on_ground_1)

View File

@ -0,0 +1,54 @@
-------------------------------------------------------------------------------
-- 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 on_ground_2.lua
--! @brief a environment description for mobs on ground
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup environments
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct env_on_ground_2
--! @brief an environment for mobs capable of walking through junglegrass
--! and stay on natural surfaces
env_on_ground_2 = {
media = {
"air",
},
surfaces = {
good = {
"default:dirt_with_grass",
"default:dirt",
"default:stone"
},
},
--TODO add support for light checks
-- light = {
-- min_light = 0,
-- max_light = 0,
-- }
}
--!@}
table.foreach(mobf_env_placable_items,
function(index)
table.insert(env_on_ground_2.media,mobf_env_placable_items[index])
end)
table.foreach(mobf_env_plants,
function(index)
table.insert(env_on_ground_2.media,mobf_env_plants[index])
end)
environment.register("on_ground_2", env_on_ground_2)

View File

@ -0,0 +1,33 @@
-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file open_waters.lua
--! @brief open waters
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup environments
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct env_open_waters
--! @brief open waters from 4 to 30 blocks deep
env_open_waters = {
media = {
"default:water_source",
"default:water_flowing"
},
surfaces = nil,
--ground is first node above/below not beeing of media type
max_height_above_ground = -4,
min_height_above_ground = -30
}
--!@}
environment.register("open_waters", env_open_waters)

View File

@ -0,0 +1,33 @@
-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file shallow_waters.lua
--! @brief shallow waters
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup environments
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct env_shallow_waters
--! @brief shallow waters not deeper than 10 blocks
env_shallow_waters = {
media = {
"default:water_source",
"default:water_flowing"
},
surfaces = nil,
--ground is first node above/below not beeing of media type
max_height_above_ground = -1,
min_height_above_ground = -10
}
--!@}
environment.register("shallow_waters", env_shallow_waters)

View File

@ -0,0 +1,39 @@
-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file simple_air.lua
--! @brief a very basic environment definition
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup environments
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct env_simple_air
--! @brief simple environment only checking for air
env_simple_air = {
media = {
"air",
}
}
--!@}
table.foreach(mobf_env_placable_items,
function(index)
table.insert(env_simple_air.media,mobf_env_placable_items[index])
end)
table.foreach(mobf_env_plants,
function(index)
table.insert(env_simple_air.media,mobf_env_plants[index])
end)
environment.register("simple_air", env_simple_air)

878
mods/mobf/fighting.lua Normal file
View File

@ -0,0 +1,878 @@
-------------------------------------------------------------------------------
-- 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 fighting.lua
--! @brief component for fighting related features
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup fighting Combat subcomponent
--! @brief Component handling all fighting
--! @ingroup framework_int
--! @{
-- Contact: sapier a t gmx net
-------------------------------------------------------------------------------
--! @class fighting
--! @brief factor added to mob melee combat range to get its maximum agression radius
MOBF_AGRESSION_FACTOR = 5
--!@}
--! @brief fighting class reference
fighting = {}
--! @brief user defined on death callback
--! @memberof fighting
fighting.on_death_callbacks = {}
-------------------------------------------------------------------------------
-- name: register_on_death_callback(callback)
--
--! @brief register an additional callback to be called on death of a mob
--! @memberof fighting
--
--! @param callback function to call
--! @return true/false
-------------------------------------------------------------------------------
function fighting.register_on_death_callback(callback)
if type(callback) == "function" then
table.insert(fighting.on_death_callbacks,callback)
return true
end
return false
end
-------------------------------------------------------------------------------
-- name: do_on_death_callback(entity,hitter)
--
--! @brief call all registred on_death callbacks
--! @memberof fighting
--
--! @param entity to do callback for
--! @param hitter object doing last punch
-------------------------------------------------------------------------------
function fighting.do_on_death_callback(entity,hitter)
for i,v in ipairs(fighting.on_death_callbacks) do
v(entity.data.name,entity.getbasepos(),hitter)
end
end
-------------------------------------------------------------------------------
-- name: push_back(entity,player)
--
--! @brief move a mob backward if it's punched
--! @memberof fighting
--! @private
--
--! @param entity mobbeing punched
--! @param dir direction to push back
-------------------------------------------------------------------------------
function fighting.push_back(entity,dir)
--get some base information
local mob_pos = entity.object:getpos()
local mob_basepos = entity.getbasepos(entity)
local dir_rad = mobf_calc_yaw(dir.x,dir.z)
local posdelta = mobf_calc_vector_components(dir_rad,0.5)
--push back mob
local new_pos = {
x=mob_basepos.x + posdelta.x,
y=mob_basepos.y,
z=mob_basepos.z + posdelta.z
}
local pos_valid = environment.possible_pos(entity,new_pos)
new_pos.y = mob_pos.y
local line_of_sight = mobf_line_of_sight(mob_pos,new_pos)
dbg_mobf.fighting_lvl2("MOBF: trying to punch mob from " .. printpos(mob_pos)
.. " to ".. printpos(new_pos))
if pos_valid and line_of_sight then
dbg_mobf.fighting_lvl2("MOBF: punching back ")
entity.object:moveto(new_pos)
else
dbg_mobf.fighting_lvl2("MOBF: not punching mob: " .. dump(pos_valid) .. " " ..dump(line_of_sight))
end
end
-------------------------------------------------------------------------------
-- name: hit(entity,player)
--
--! @brief handler for mob beeing hit
--! @memberof fighting
--
--! @param entity mob being hit
--! @param player player/object hitting the mob
-------------------------------------------------------------------------------
function fighting.hit(entity,player)
if entity.data.generic.on_hit_callback ~= nil and
entity.data.generic.on_hit_callback(entity,player) == true
then
dbg_mobf.fighting_lvl2("MOBF: ".. entity.data.name .. " custom on hit handler superseeds generic handling")
return
end
--TODO calculate damage by players weapon
--local damage = 1
--dbg_mobf.fighting_lvl2("MOBF: ".. entity.data.name .. " about to take ".. damage .. " damage")
--entity.dynamic_data.generic.health = entity.dynamic_data.generic.health - damage
--entity.object:set_hp(entity.object:get_hp() - damage )
--get some base information
local mob_pos = entity.object:getpos()
local mob_basepos = entity.getbasepos(entity)
local playerpos = player:getpos()
local dir = mobf_get_direction(playerpos,mob_basepos)
--update mob orientation
if entity.mode == "3d" then
entity.object:setyaw(mobf_calc_yaw(dir.x,dir.z)+math.pi)
else
entity.object:setyaw(mobf_calc_yaw(dir.x,dir.z)-math.pi)
end
if entity.data.sound ~= nil then
sound.play(mob_pos,entity.data.sound.hit);
end
fighting.push_back(entity,dir)
-- make it die
if entity.object:get_hp() < 1 then
--if entity.dynamic_data.generic.health < 1 then
local result = entity.data.generic.kill_result
if type(entity.data.generic.kill_result) == "function" then
result = entity.data.generic.kill_result()
end
--call on kill callback and superseed normal on kill handling
if entity.data.generic.on_kill_callback == nil or
entity.data.generic.on_kill_callback(entity,player) == false
then
if entity.data.sound ~= nil then
sound.play(mob_pos,entity.data.sound.die);
end
if player:is_player() then
if type(result) == "table" then
for i=1,#result, 1 do
if player:get_inventory():room_for_item("main", result[i]) then
player:get_inventory():add_item("main", result[i])
end
end
else
if player:get_inventory():room_for_item("main", result) then
player:get_inventory():add_item("main", result)
end
end
else
--todo check if spawning a stack is possible
minetest.env:add_item(mob_pos,result)
end
spawning.remove(entity, "killed")
else
dbg_mobf.fighting_lvl2("MOBF: ".. entity.data.name
.. " custom on kill handler superseeds generic handling")
end
return
end
--dbg_mobf.fighting_lvl2("MOBF: attack chance is ".. entity.data.combat.angryness)
-- fight back
if entity.data.combat ~= nil and
entity.data.combat.angryness > 0 then
dbg_mobf.fighting_lvl2("MOBF: mob with chance of fighting back attacked")
--either the mob hasn't been attacked by now or a new player joined fight
local playername = player.get_player_name(player)
if entity.dynamic_data.combat.target ~= playername then
dbg_mobf.fighting_lvl2("MOBF: new player started fight")
--calculate chance of mob fighting back
if math.random() < entity.data.combat.angryness then
dbg_mobf.fighting_lvl2("MOBF: fighting back player "..playername)
entity.dynamic_data.combat.target = playername
fighting.switch_to_combat_state(entity,mobf_get_current_time(),player)
end
end
end
end
-------------------------------------------------------------------------------
-- name: switch_to_combat_state(entity,now,target)
--
--! @brief switch to combat state
--! @memberof fighting
--! @private
--
--! @param entity mob to switch state
--! @param now current time in seconds
--! @param target the target to attack
-------------------------------------------------------------------------------
function fighting.switch_to_combat_state(entity,now,target)
local combat_state = mob_state.get_state_by_name(entity,"combat")
if target == nil then
dbg_mobf.fighting_lvl2("MOBF: no target for combat state change specified")
return
end
if combat_state == nil then
dbg_mobf.fighting_lvl2("MOBF: no special combat state")
return
end
dbg_mobf.fighting_lvl2("MOBF: switching to combat state")
--make sure state is locked
mob_state.lock(entity,true)
--backup dynamic movement data
local backup = entity.dynamic_data.movement
backup.current_state = mob_state.get_state_by_name(entity,entity.dynamic_data.state.current)
--switch state
local newentity = mob_state.change_state(entity,combat_state)
if newentity ~= nil then
entity = newentity
end
--save old movement data to use on switching back
entity.dynamic_data.movement.backup = backup
--set target
entity.dynamic_data.movement.target = target
--make sure a fighting mob ain't teleporting to target
entity.dynamic_data.movement.teleportsupport = false
--make sure we do follow our target
entity.dynamic_data.movement.guardspawnpoint = false
end
-------------------------------------------------------------------------------
-- name: restore_previous_state(entity,now)
--
--! @brief restore default movement generator of mob
--! @memberof fighting
--! @private
--
--! @param entity mob to restore movement generator
--! @param now current time in seconds
-------------------------------------------------------------------------------
function fighting.restore_previous_state(entity,now)
--check if ther is anything we can restore
if entity.dynamic_data.movement.backup ~= nil then
local backup = entity.dynamic_data.movement.backup
local newentity = nil
if backup.current_state ~= nil then
newentity = mob_state.change_state(entity,backup.current_state)
else
minetest.log(LOGLEVEL_WARNING,"MOBF: unable to restore previous state switching to default")
newentity = mob_state.change_state(entity,mob_state.get_state_by_name(entity,"default"))
end
if newentity ~= nil then
entity = newentity
end
--restore old movement data
entity.dynamic_data.movement = backup
--make sure all remaining data is deleted
entity.dynamic_data.movement.backup = nil
entity.dynamic_data.movement.current_state = nil
end
--make sure state is unlocked
mob_state.lock(entity,false)
end
-------------------------------------------------------------------------------
-- name: combat(entity,now)
--
--! @brief periodic callback called to do mobs own combat related actions
--! @memberof fighting
--
--! @param entity mob to do action
--! @param now current time
-------------------------------------------------------------------------------
function fighting.combat(entity,now)
--handle self destruct mobs
if fighting.self_destruct_handler(entity,now) then
return
end
if entity.dynamic_data.combat ~= nil and
entity.dynamic_data.combat.target ~= "" then
dbg_mobf.fighting_lvl1("MOBF: attacking player: "..entity.dynamic_data.combat.target)
local player = minetest.env:get_player_by_name(entity.dynamic_data.combat.target)
--check if target is still valid
if player == nil then
dbg_mobf.fighting_lvl3("MOBF: not a valid player")
-- switch back to default movement gen
fighting.restore_previous_state(entity,now)
--there is no player by that name, stop attack
entity.dynamic_data.combat.target = ""
return
end
--calculate some basic data
local mob_pos = entity.object:getpos()
local playerpos = player:getpos()
local distance = mobf_calc_distance(mob_pos,playerpos)
fighting.self_destruct_trigger(entity,distance,now)
--find out if player is next to mob
if distance > entity.data.combat.melee.range * MOBF_AGRESSION_FACTOR then
dbg_mobf.fighting_lvl2("MOBF: " .. entity.data.name .. " player >"
.. entity.dynamic_data.combat.target .. "< to far away "
.. distance .. " > " .. (entity.data.combat.melee.range * MOBF_AGRESSION_FACTOR )
.. " stopping attack")
--switch back to default movement gen
fighting.restore_previous_state(entity,now)
--there is no player by that name, stop attack
entity.dynamic_data.combat.target = ""
return
end
--is mob near enough for any attack attack?
if (entity.data.combat.melee == nil or
distance > entity.data.combat.melee.range) and
(entity.data.combat.distance == nil or
distance > entity.data.combat.distance.range) then
if entity.data.combat.melee ~= nil or
entity.data.combat.distance ~= nil then
dbg_mobf.fighting_lvl2("MOBF: distance="..distance)
if entity.data.combat.melee ~= nil then
dbg_mobf.fighting_lvl2("MOBF: melee="..entity.data.combat.melee.range)
end
if entity.data.combat.distance ~= nil then
dbg_mobf.fighting_lvl2("MOBF: distance="..entity.data.combat.distance.range)
end
end
return
end
if fighting.melee_attack_handler(entity,player,now,distance) == false then
if fighting.distance_attack_handler(entity,playerpos,mob_pos,now,distance) then
-- mob did an attack so give chance to stop attack
local rand_value = math.random()
if rand_value > entity.data.combat.angryness then
dbg_mobf.fighting_lvl2("MOBF: rand=".. rand_value
.. " angryness=" .. entity.data.combat.angryness)
dbg_mobf.fighting_lvl2("MOBF: " .. entity.data.name .. " "
.. now .. " random aborting attack at player "
..entity.dynamic_data.combat.target)
entity.dynamic_data.combat.target = ""
end
end
end
end
--fight against generic enemy "sun"
fighting.sun_damage_handler(entity,now)
end
-------------------------------------------------------------------------------
-- name: get_target(entity)
--
--! @brief find and possible target next to mob
--! @memberof fighting
--! @private
--
--! @param entity mob to look around
--! @return target
-------------------------------------------------------------------------------
function fighting.get_target(entity)
local possible_targets = {}
if entity.data.combat.melee.range > 0 then
local objectlist = minetest.env:get_objects_inside_radius(entity.object:getpos(),
entity.data.combat.melee.range*MOBF_AGRESSION_FACTOR)
local count = 0
for i,v in ipairs(objectlist) do
local playername = v.get_player_name(v)
if playername ~= nil and
playername ~= "" then
count = count + 1
table.insert(possible_targets,v)
dbg_mobf.fighting_lvl3(playername .. " is next to a mob of type "
.. entity.data.name);
end
end
dbg_mobf.fighting_lvl3("Found ".. count .. " objects within attack range of "
.. entity.data.name)
end
local targets_within_sight = {}
for i,v in ipairs(possible_targets) do
local entity_pos = entity.object:getpos()
local target_pos = v:getpos()
--is there a line of sight between mob and possible target
--line of sight is calculated 1block above ground
if mobf_line_of_sight({x=entity_pos.x,y=entity_pos.y+1,z=entity_pos.z},
{x=target_pos.x,y=target_pos.y+1,z=target_pos.z}) then
table.insert(targets_within_sight,v)
end
end
local nearest_target = nil
local min_distance = -1
for i,v in ipairs(targets_within_sight) do
local distance = mobf_calc_distance(entity.object:getpos(),v:getpos())
if min_distance < 0 or
distance < min_distance then
nearest_target = v
min_distance = distance
end
end
return nearest_target
end
-------------------------------------------------------------------------------
-- name: aggression(entity)
--
--! @brief start attack in case of agressive mob
--! @memberof fighting
--
--! @param entity mob to do action
--! @param now current time
-------------------------------------------------------------------------------
function fighting.aggression(entity,now)
--if no combat data is specified don't do anything
if entity.data.combat == nil then
return
end
--mob is specified as self attacking
if entity.data.combat.starts_attack and
(entity.dynamic_data.combat.target == nil or
entity.dynamic_data.combat.target == "") 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 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
local targetname = target.get_player_name(target)
if targetname ~= entity.dynamic_data.combat.target then
entity.dynamic_data.combat.target = targetname
fighting.switch_to_combat_state(entity,now,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
-------------------------------------------------------------------------------
-- name: fighting.init_dynamic_data(entity)
--
--! @brief initialize all dynamic data on activate
--! @memberof fighting
--
--! @param entity mob to do action
--! @param now current time
-------------------------------------------------------------------------------
function fighting.init_dynamic_data(entity,now)
local targetstring = ""
local data = {
ts_last_sun_damage = now,
ts_last_attack = now,
ts_last_aggression_chance = now,
ts_self_destruct_triggered = -1,
target = targetstring,
}
entity.dynamic_data.combat = data
end
-------------------------------------------------------------------------------
-- name: self_destruct_trigger(entity,distance)
--
--! @brief handle self destruct features
--! @memberof fighting
--! @private
--
--! @param entity mob to do action
--! @param distance current distance to target
--! @param now current time
--! @return true/false if handled or not
-------------------------------------------------------------------------------
function fighting.self_destruct_trigger(entity,distance,now)
if entity.data.combat ~= nil and
entity.data.combat.self_destruct ~= nil then
dbg_mobf.fighting_lvl1("MOBF: checking for self destruct trigger " ..
distance .. " " ..
entity.dynamic_data.combat.ts_self_destruct_triggered ..
" " .. now)
--trigger self destruct
if distance <= entity.data.combat.self_destruct.range and
entity.dynamic_data.combat.ts_self_destruct_triggered == -1 then
dbg_mobf.fighting_lvl2("MOBF: self destruct triggered")
entity.dynamic_data.combat.ts_self_destruct_triggered = now
end
end
end
-------------------------------------------------------------------------------
-- name: self_destruct_handler(entity)
--
--! @brief handle self destruct features
--! @memberof fighting
--! @private
--
--! @param entity mob to do action
--! @param now current time
--! @return true/false if handled or not
-------------------------------------------------------------------------------
function fighting.self_destruct_handler(entity,now)
--self destructing mob?
if entity.data.combat ~= nil and
entity.data.combat.self_destruct ~= nil then
local pos = entity.object:getpos()
dbg_mobf.fighting_lvl1("MOBF: checking for self destruct imminent")
--do self destruct
if entity.dynamic_data.combat.ts_self_destruct_triggered > 0 and
entity.dynamic_data.combat.ts_self_destruct_triggered +
entity.data.combat.self_destruct.delay
<= now then
dbg_mobf.fighting_lvl2("MOBF: executing self destruct")
if entity.data.sound ~= nil then
sound.play(pos,entity.data.sound.self_destruct);
end
mobf_do_area_damage(pos,nil,
entity.data.combat.self_destruct.damage,
entity.data.combat.self_destruct.range)
--TODO determine block removal by damage and remove blocks
mobf_do_node_damage(pos,{},
entity.data.combat.self_destruct.node_damage_range,
1 - 1/entity.data.combat.self_destruct.node_damage_range)
if mobf_rtd.fire_enabled then
--Add fire
for i=pos.x-entity.data.combat.self_destruct.range/2,
pos.x+entity.data.combat.self_destruct.range/2, 1 do
for j=pos.y-entity.data.combat.self_destruct.range/2,
pos.y+entity.data.combat.self_destruct.range/2, 1 do
for k=pos.z-entity.data.combat.self_destruct.range/2,
pos.z+entity.data.combat.self_destruct.range/2, 1 do
local current = minetest.env:get_node({x=i,y=j,z=k})
if (current.name == "air") then
minetest.env:set_node({x=i,y=j,z=k}, {name="fire:basic_flame"})
end
end
end
end
else
minetest.log(LOGLEVEL_NOTICE,"MOBF: self destruct without fire isn't really impressive!")
end
spawning.remove(entity, "self destruct")
return true
end
end
return false
end
-------------------------------------------------------------------------------
-- name: melee_attack_handler(entity,now)
--
--! @brief handle melee attack
--! @memberof fighting
--! @private
--
--! @param entity mob to do action
--! @param player player to attack
--! @param now current time
--! @param distance distance to player
--! @return true/false if handled or not
-------------------------------------------------------------------------------
function fighting.melee_attack_handler(entity,player,now,distance)
if entity.data.combat.melee == nil then
dbg_mobf.fighting_lvl2("MOBF: no meele attack specified")
return false
end
local time_of_next_attack_chance = entity.dynamic_data.combat.ts_last_attack
+ entity.data.combat.melee.speed
--check if mob is ready to attack
if now < time_of_next_attack_chance then
dbg_mobf.fighting_lvl1("MOBF: to early for meele attack " ..
now .. " >= " .. time_of_next_attack_chance)
return false
end
if distance <= entity.data.combat.melee.range
then
--save time of attack
entity.dynamic_data.combat.ts_last_attack = now
if entity.data.sound ~= nil then
sound.play(entity.object:getpos(),entity.data.sound.melee);
end
--calculate damage to be done
local damage_done = math.floor(math.random(0,entity.data.combat.melee.maxdamage)) + 1
local player_health = player:get_hp()
--do damage
player:set_hp(player_health -damage_done)
dbg_mobf.fighting_lvl2("MOBF: ".. entity.data.name ..
" doing melee attack damage=" .. damage_done)
return true
end
dbg_mobf.fighting_lvl1("MOBF: not within meele range " ..
distance .. " > " .. entity.data.combat.melee.range)
return false
end
-------------------------------------------------------------------------------
-- name: distance_attack_handler(entity,now)
--
--! @brief handle distance attack
--! @memberof fighting
--! @private
--
--! @param entity mob to do action
--! @param playerpos position of target
--! @param mob_pos position of mob
--! @param now current time
--! @param distance distance between target and player
--! @return true/false if handled or not
-------------------------------------------------------------------------------
function fighting.distance_attack_handler(entity,playerpos,mob_pos,now,distance)
if entity.data.combat.distance == nil then
dbg_mobf.fighting_lvl2("MOBF: no distance attack specified")
return false
end
local time_of_next_attack_chance = entity.dynamic_data.combat.ts_last_attack
+ entity.data.combat.distance.speed
--check if mob is ready to attack
if now < time_of_next_attack_chance then
dbg_mobf.fighting_lvl1("MOBF: to early for distance attack " ..
now .. " >= " .. time_of_next_attack_chance)
return false
end
if distance <= entity.data.combat.distance.range
then
dbg_mobf.fighting_lvl2("MOBF: ".. entity.data.name .. " doing distance attack")
--save time of attack
entity.dynamic_data.combat.ts_last_attack = now
local dir = mobf_get_direction({ x=mob_pos.x,
y=mob_pos.y+1,
z=mob_pos.z
},
playerpos)
if entity.data.sound ~= nil then
sound.play(mob_pos,entity.data.sound.distance);
end
local newobject=minetest.env:add_entity({ x=mob_pos.x+dir.x,
y=mob_pos.y+dir.y+1,
z=mob_pos.z+dir.z
},
entity.data.combat.distance.attack
)
local thrown_entity = mobf_find_entity(newobject)
--TODO add random disturbance based on accuracy
if thrown_entity ~= nil then
local vel_trown = {
x=dir.x*thrown_entity.velocity,
y=dir.y*thrown_entity.velocity + math.random(0,0.25),
z=dir.z*thrown_entity.velocity
}
dbg_mobf.fighting_lvl2("MOBF: throwing with velocity: " .. printpos(vel_trown))
newobject:setvelocity(vel_trown)
newobject:setacceleration({x=0, y=-thrown_entity.gravity, z=0})
thrown_entity.owner = entity.object
dbg_mobf.fighting_lvl2("MOBF: distance attack issued")
else
minetest.log(LOGLEVEL_ERROR, "MOBF: unable to find entity for distance attack")
end
return true
end
dbg_mobf.fighting_lvl1("MOBF: not within distance range " ..
distance .. " > " .. entity.data.combat.distance.range)
return false
end
-------------------------------------------------------------------------------
-- name: sun_damage_handler(entity,now)
--
--! @brief handle damage done by sun
--! @memberof fighting
--! @private
--
--! @param entity mob to do action
--! @param now current time
-------------------------------------------------------------------------------
function fighting.sun_damage_handler(entity,now)
if entity.data.combat ~= nil and
entity.data.combat.sun_sensitive then
local pos = entity.object:getpos()
local current_state = mob_state.get_state_by_name(entity,entity.dynamic_data.state.current)
local current_light = minetest.env:get_node_light(pos)
if current_light == nil then
minetest.log(LOGLEVEL_ERROR,"MOBF: Bug!!! didn't get a light value for "
.. printpos(pos))
return
end
--check if mob is in sunlight
if ( current_light > LIGHT_MAX) then
dbg_mobf.fighting_lvl1("MOBF: " .. entity.data.name ..
" health at start:" .. entity.object:get_hp())
if current_state.animation ~= nil and
entity.data.animation ~= nil and
entity.data.animation[current_state.animation .. "__burning"] ~= nil then
graphics.set_animation(entity,current_state.animation .. "burning")
else
graphics.set_animation(entity,"burning")
end
if entity.dynamic_data.combat.ts_last_sun_damage +1 < now then
local damage = (1 + math.floor(entity.data.generic.base_health/15))
dbg_mobf.fighting_lvl1("Mob ".. entity.data.name .. " takes "
..damage .." damage because of sun")
entity.object:set_hp(entity.object:get_hp() - damage)
if entity.data.sound ~= nil then
sound.play(mob_pos,entity.data.sound.sun_damage);
end
if entity.object:get_hp() <= 0 then
--if entity.dynamic_data.generic.health <= 0 then
dbg_mobf.fighting_lvl2("Mob ".. entity.data.name .. " died of sun")
spawning.remove(entity,"died by sun")
return
end
entity.dynamic_data.combat.ts_last_sun_damage = now
end
else
--use last sun damage to avoid setting animation over and over even if nothing changed
if entity.dynamic_data.combat.ts_last_sun_damage ~= -1 and
current_state.animation ~= nil then
graphics.set_animation(entity,current_state.animation)
entity.dynamic_data.combat.ts_last_sun_damage = -1
end
end
end
end

View File

@ -0,0 +1,908 @@
-------------------------------------------------------------------------------
-- 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 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
--! @{
if minetest.setting_getbool("mobf_enable_socket_trace") then
require "socket"
end
-------------------------------------------------------------------------------
-- name: mobf_bug_warning()
--
--! @brief make bug warnings configurable
--
--! @param level bug severity level to use for minetest.log
--! @param text data to print to log
-------------------------------------------------------------------------------
function mobf_bug_warning(level,text)
if minetest.setting_getbool("mobf_log_bug_warnings") then
minetest.log(level,text)
end
end
-------------------------------------------------------------------------------
-- name: mobf_get_time_ms()
--
--! @brief get current time in ms
--
--! @return current time in ms
-------------------------------------------------------------------------------
function mobf_get_time_ms()
if minetest.setting_getbool("mobf_enable_socket_trace") then
return socket.gettime()*1000
else
return 0
end
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)
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)
if a > b then
return a
else
return b
end
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: printpos(pos)
--
--! @brief convert pos to string of type "(X,Y,Z)"
--
--! @param pos position to convert
--! @return string with coordinates of pos
-------------------------------------------------------------------------------
function printpos(pos)
if pos ~= nil then
if pos.y ~= nil then
return "("..pos.x..","..pos.y..","..pos.z..")"
else
return "("..pos.x..", ? ,"..pos.z..")"
end
end
return ""
end
-------------------------------------------------------------------------------
-- name: mobf_get_current_time()
--
--! @brief alias to get current time
--
--! @return current time in seconds
-------------------------------------------------------------------------------
function mobf_get_current_time()
return os.time(os.date('*t'))
--return minetest.get_time()
end
callback_statistics = {}
-------------------------------------------------------------------------------
-- name: mobf_warn_long_fct(starttime,fctname,facility)
--
--! @brief alias to get current time
--
--! @param starttime time fct started
--! @param fctname name of function
--! @param facility name of facility to add time to
--
--! @return current time in seconds
-------------------------------------------------------------------------------
function mobf_warn_long_fct(starttime,fctname,facility)
local currenttime = mobf_get_time_ms()
local delta = currenttime - starttime
if minetest.setting_getbool("mobf_enable_socket_trace_statistics") then
if facility == nil then
facility = "generic"
end
if callback_statistics[facility] == nil then
callback_statistics[facility] = {
upto_005ms = 0,
upto_010ms = 0,
upto_020ms = 0,
upto_050ms = 0,
upto_100ms = 0,
upto_200ms = 0,
more = 0,
valcount = 0,
sum = 0,
last_time = 0,
}
end
callback_statistics[facility].valcount = callback_statistics[facility].valcount +1
callback_statistics[facility].sum = callback_statistics[facility].sum + delta
if callback_statistics[facility].valcount == 1000 then
callback_statistics[facility].valcount = 0
local deltatime = currenttime - callback_statistics[facility].last_time
callback_statistics[facility].last_time = currenttime
minetest.log(LOGLEVEL_ERROR,"Statistics for: " .. facility .. ": " ..
callback_statistics[facility].upto_005ms .. "," ..
callback_statistics[facility].upto_010ms .. "," ..
callback_statistics[facility].upto_020ms .. "," ..
callback_statistics[facility].upto_050ms .. "," ..
callback_statistics[facility].upto_100ms .. "," ..
callback_statistics[facility].upto_200ms .. "," ..
callback_statistics[facility].more ..
" (".. callback_statistics[facility].sum .. " / " .. deltatime .. ") " ..
tostring(math.floor((callback_statistics[facility].sum/deltatime) * 100)) .. "%")
callback_statistics[facility].sum = 0
end
if delta < 5 then
callback_statistics[facility].upto_005ms = callback_statistics[facility].upto_005ms +1
return
end
if delta < 10 then
callback_statistics[facility].upto_010ms = callback_statistics[facility].upto_010ms +1
return
end
if delta < 20 then
callback_statistics[facility].upto_020ms = callback_statistics[facility].upto_020ms +1
return
end
if delta < 50 then
callback_statistics[facility].upto_050ms = callback_statistics[facility].upto_050ms +1
return
end
if delta < 100 then
callback_statistics[facility].upto_100ms = callback_statistics[facility].upto_100ms +1
return
end
if delta < 200 then
callback_statistics[facility].upto_200ms = callback_statistics[facility].upto_200ms +1
return
end
callback_statistics[facility].more = callback_statistics[facility].more +1
end
if delta >200 then
minetest.log(LOGLEVEL_ERROR,"MOBF: function " .. fctname .. " took too long: " .. delta .. " ms")
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_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)
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 first position
--! @param pos2 second 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_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)
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.env:get_node(current_pos)
if node.name == "air" then
local current_light = minetest.env: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_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
local objectlist = minetest.env:get_objects_inside_radius(pos,range)
if mob_transform == nil then
mob_transform = ""
end
for index,value in pairs(objectlist) do
local entity = mobf_find_entity(value)
dbg_mobf.generic_lvl1("MOBF: entity at "..printpos(pos)..
" looking for: "..mob_name ..
" or " .. mob_transform )
--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 and
entity.dynamic_data.spawning ~= 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
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
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)
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.env: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_sightX(pos1,pos2)
--
--! @brief is there a line of sight between two specified positions
-- TODO add code to minetest to get this working!
--
--! @param pos1 start position of los check
--! @param pos2 end position of los check
--! @return: true/false
-------------------------------------------------------------------------------
function mobf_line_of_sightX(pos1,pos2)
return minetest.env:get_line_of_sight(pos1,pos2)
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].sunlight_propagates ~= true then
line_of_sight = false
break
end
end
return line_of_sight
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_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)
for i=0, height, 1 do
local pos_above = {
x = pos.x,
y = pos.y + 1,
z = pos.z
}
local node_above = minetest.env: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 position to check
--! @param media table of blocks not considered to be ground
--! @return number of blocks to ground
-------------------------------------------------------------------------------
function mobf_ground_distance(pos,media)
local node_to_check = minetest.env:get_node(pos)
local count = 0
while node_to_check ~= nil and mobf_contains(media,node_to_check.name) and
count < 32 do
count = count +1
pos = {x=pos.x,y=pos.y-1,z=pos.z};
node_to_check = minetest.env: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.env: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.env: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)
local node_to_check = minetest.env: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.env: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.env: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.env: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_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.env: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 or nil
-------------------------------------------------------------------------------
function mobf_get_surface(x,z, min_y, max_y)
local last_node = minetest.env: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.env: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)
--
--! @brief check if entity is activated at already loaded pos
--
--! @param pos to check
--! @return true/false
-------------------------------------------------------------------------------
function entity_at_loaded_pos(pos)
local current_node = minetest.env:get_node(pos)
if current_node ~= nil then
if current_node.name == "ignore" then
minetest.log(LOGLEVEL_WARNING,"MOBF: spawned at unloaded pos! : "
.. dump(pos))
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_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_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_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_assert_backtrace(value)
--
--! @brief assert in case value is false
--
--! @param value to evaluate
-------------------------------------------------------------------------------
function mobf_assert_backtrace(value)
if minetest.assert_backtrace ~= nil then
minetest.assert_backtrace(value)
else
assert(value)
end
end
--!@}

232
mods/mobf/graphics.lua Normal file
View File

@ -0,0 +1,232 @@
-------------------------------------------------------------------------------
-- 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
-------------------------------------------------------------------------------
--! @class graphics
--! @brief graphic features
graphics = {}
-------------------------------------------------------------------------------
-- name: update_orientation_simple(entity,velocity)
--
--! @brief calculate direction of mob to face
--! @memberof graphics
--
--! @param entity mob to calculate direction
--! @param current_velocity data to calculate direction from
-------------------------------------------------------------------------------
function graphics.update_orientation_simple(entity,current_velocity)
local x_abs = math.abs(current_velocity.x)
local z_abs = math.abs(current_velocity.z)
if x_abs > z_abs then
if current_velocity.x > 0 then
entity.object:setyaw(0)
else
entity.object:setyaw(math.pi)
end
else
if current_velocity.z >0 then
entity.object:setyaw(math.pi/2)
else
entity.object:setyaw(math.pi * (3/2))
end
end
end
-------------------------------------------------------------------------------
-- name: update_orientation(entity)
--
--! @brief callback for calculating a mobs direction
--! @memberof graphics
--
--! @param entity mob to calculate direction
--! @param now current time
--! @param dtime current dtime
-------------------------------------------------------------------------------
function graphics.update_orientation(entity,now,dtime)
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) .. " graphics callback without dynamic data")
return
end
local new_orientation = 0
-- if entity.dynamic_data.movement.ts_orientation_upd + 1 < now and
if entity.dynamic_data.movement.orientation_fix_needed then
dbg_mobf.graphics_lvl3("MOBF: Updating orientation")
--entity.dynamic_data.movement.ts_orientation_upd = now
local current_velocity = entity.object:getvelocity()
local acceleration = entity.object:getacceleration()
local pos = entity.getbasepos(entity)
dbg_mobf.graphics_lvl3("MOBF: vel: (" .. current_velocity.x .. ",".. current_velocity.z .. ") " ..
"accel: (" ..acceleration.x .. "," .. acceleration.z .. ")")
--predict position mob will be in 0.25 seconds
--local predicted_pos = movement_generic.calc_new_pos(pos,acceleration,dtime,current_velocity)
--local delta_x = predicted_pos.x - pos.x
--local delta_z = predicted_pos.z - pos.z
local delta_x = current_velocity.x
local delta_z = current_velocity.z
--legacy 2d mode
if (entity.mode == "2d") then
graphics.update_orientation_simple(entity,{x=delta_x, z=delta_z})
-- 3d mode
else
if (delta_x ~= 0 ) and
(delta_z ~= 0) then
entity.object:setyaw(mobf_calc_yaw(delta_x,delta_z))
dbg_mobf.graphics_lvl3("MOBF: x-delta: " .. delta_x
.. " z-delta: " .. delta_z)
elseif (delta_x ~= 0) or
(delta_z ~= 0) then
dbg_mobf.graphics_lvl3("MOBF: at least speed for one direction is 0")
graphics.update_orientation_simple(entity,{x=delta_x,z=delta_z})
else
dbg_mobf.movement_lvl3("MOBF: not moving")
end
end
end
end
-------------------------------------------------------------------------------
-- name: 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)
end
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
}, nil, nil)
entity.dynamic_data.animation = name
end
return
end
mobf_bug_warning(LOGLEVEL_WARNING,"MOBF BUG!!: invalid graphics mode specified "
.. dump(entity.mode))
end
------------------------------------------------------------------------------
-- name: 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.setting_getbool("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

219
mods/mobf/harvesting.lua Normal file
View File

@ -0,0 +1,219 @@
-------------------------------------------------------------------------------
-- 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
-------------------------------------------------------------------------------
--! @class harvesting
--! @brief harvesting features
harvesting = {}
--!@}
-------------------------------------------------------------------------------
-- name: 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
-------------------------------------------------------------------------------
-- name: 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
-- 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)
--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!!!")
--handled but not ok so don't attack
return true
end
end
if entity.data.generic.addoncatch ~= nil then
player:get_inventory():add_item("main",
entity.data.generic.addoncatch.." 1")
else
player:get_inventory():add_item("main",
entity.data.modname ..":"..entity.data.name.." 1")
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
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 ~= "" 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
-------------------------------------------------------------------------------
-- name: 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)
end
end
end

350
mods/mobf/init.lua Normal file
View File

@ -0,0 +1,350 @@
-------------------------------------------------------------------------------
-- 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 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
-------------------------------------------------------------------------------
print("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
inventory_plus_enabled = false,
--!registry for movement patterns
movement_patterns = {},
--!registry of mobs
registred_mob = {},
--!registred mobs_data
registred_mob_data = {},
}
--!path of mod
mobf_modpath = minetest.get_modpath("mobf")
LOGLEVEL_INFO = "verbose"
LOGLEVEL_NOTICE = "info"
LOGLEVEL_WARNING = "action"
LOGLEVEL_ERROR = "error"
LOGLEVEL_CRITICAL = "error"
--include debug trace functions
dofile (mobf_modpath .. "/debug_trace.lua")
--include engine
dofile (mobf_modpath .. "/generic_functions.lua")
dofile (mobf_modpath .. "/environment.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 .. "/weapons.lua")
dofile (mobf_modpath .. "/fighting.lua")
dofile (mobf_modpath .. "/random_drop.lua")
dofile (mobf_modpath .. "/sound.lua")
dofile (mobf_modpath .. "/permanent_data.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")
--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 .. "/mov_gen_none.lua")
mobf_version = "2.0.5"
--! @brief define tools used for more than one mob
function mobf_init_basic_tools()
minetest.register_craft({
output = "animalmaterials:lasso 5",
recipe = {
{'', "wool:white",''},
{"wool:white",'', "wool:white"},
{'',"wool:white",''},
}
})
minetest.register_craft({
output = "animalmaterials:net 1",
recipe = {
{"wool:white",'',"wool:white"},
{'', "wool:white",''},
{"wool:white",'',"wool:white"},
}
})
minetest.register_craft({
output = 'animalmaterials:sword_deamondeath',
recipe = {
{'animalmaterials:bone'},
{'animalmaterials:bone'},
{'default:stick'},
}
})
end
--! @brief main initialization function
function mobf_init_framework()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initializing mob framework")
mobf_init_basic_tools()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Reading mob blacklist")
local mobf_mob_blacklist_string = minetest.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 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 weaponry..")
mobf_init_weapons()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initializing debug hooks..")
mobf_debug.init()
minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize mobf supplied modules..")
mobf_init_modules()
-- initialize luatrace if necessary
if mobf_rtd.luatrace_enabled then
luatrace = require("luatrace")
end
-- register privilege to change mobf settings
minetest.register_privilege("mobfw_admin",
{
description = "Player may change mobf settings",
give_to_singleplayer = true
})
print("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] == "inventory_plus" then
mobf_rtd.inventory_plus_enabled = 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
})
--attack hook
mobf.register_on_step_callback({
name = "aggression",
handler = fighting.aggression,
init = nil, -- already done by fighting.combat
configcheck = function(entity)
if 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.check_population_density,
init = spawning.init_dynamic_data,
configcheck = function(entity)
return true
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_orientation",
handler = graphics.update_orientation,
init = nil,
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
--Note debug needs to be registred FIRST!
mobf.register_on_rightclick_callback({
name = "debugcallback",
handler = mobf_debug.rightclick_callback,
configcheck = function(entity)
return true
end
})
mobf.register_on_rightclick_callback({
name = "tradercallback",
handler = mob_inventory.trader_callback,
configcheck = mob_inventory.config_check
})
end
mobf_init_framework()
dofile (mobf_modpath .. "/compatibility.lua")

534
mods/mobf/inventory.lua Normal file
View File

@ -0,0 +1,534 @@
-------------------------------------------------------------------------------
-- 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
-------------------------------------------------------------------------------
mob_inventory = {}
mob_inventory.trader_inventories = {}
mob_inventory.formspecs = {}
-------------------------------------------------------------------------------
-- name: allow_move(inv, from_list, from_index, to_list, to_index, count, player)
--
--! @brief check if there is enough at payroll
--! @ingroup mob_inventory
--
--! @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
return count
end
-------------------------------------------------------------------------------
-- name: allow_put(inv, listname, index, stack, player)
--
--! @brief check if there is enough at payroll
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: allow_take(inv, listname, index, stack, player)
--
--! @brief check if there is enough at payroll
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: on_move(inv, from_list, from_index, to_list, to_index, count, player)
--
--! @brief check if there is enough at payroll
--! @ingroup mob_inventory
--
--! @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 elements = moved.get_count(moved)
--if elements > 1 then
-- moved = moved.take_item(moved,elements-1)
-- inv.set_stack(inv,from_list, from_index, moved)
-- inv.set_stack(inv,to_list, to_index, moved)
--else
-- inv.set_stack(inv,from_list, from_index, moved)
--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
local goodname = moved.get_name(moved)
dbg_mobf.trader_inv_lvl1("MOBF: good selected: " .. goodname)
--get element put to selection
mob_inventory.fill_prices(entity,inv,goodname,moved.get_count(moved))
mob_inventory.update_takeaway(inv)
end
end
-------------------------------------------------------------------------------
-- name: on_put(inv, listname, index, stack, player)
--
--! @brief check if there is enough at payroll
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: on_take(inv, listname, index, stack, player)
--
--! @brief check if there is enough at payroll
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: update_takeaway(inv)
--
--! @brief update content of takeaway
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: check_pay(inv)
--
--! @brief check if there is enough at payroll
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: init_detached_inventories(entity,now)
--
--! @brief initialize dynamic data required by harvesting
--! @ingroup mob_inventory
--
--! @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;Trader " .. tradername .. " Inventory]" ..
"label[0,1;Selling:]" ..
"list[detached:" .. unique_entity_id .. ";goods;0,1.5;8,2;]" ..
"label[0,4.0;Selection]" ..
"list[detached:" .. unique_entity_id .. ";selection;0,4.5;1,1;]" ..
"label[1.25,4.75;-->]" ..
"label[2,4.0;Price]" ..
"list[detached:" .. unique_entity_id .. ";price_1;2,4.5;1,1;]" ..
"label[3,4.0;or]" ..
"list[detached:" .. unique_entity_id .. ";price_2;3,4.5;1,1;]" ..
"label[4.25,4.75;-->]" ..
"label[5,4.0;Pay]" ..
"list[detached:" .. unique_entity_id .. ";pay;5,4.5;1,1;]" ..
"label[6.25,4.75;-->]" ..
"label[6.75,4.0;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
-------------------------------------------------------------------------------
-- name: config_check(entity)
--
--! @brief check if mob is configured as trader
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: register_formspec(name,formspec)
--
--! @brief check if mob is configured as trader
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: callback(entity,player,now)
--
--! @brief callback handler for harvest by player
--! @ingroup mob_inventory
--
--! @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 mob_inventory.formspecs["formspec_" .. unique_entity_id] ~= nil then
--rotate mob to face player
local direction = mobf_get_direction(entity.object:getpos(),
player:getpos())
if entity.mode == "3d" then
entity.object:setyaw(mobf_calc_yaw(direction.x,direction.z)-math.pi/2)
else
entity.object:setyaw(mobf_calc_yaw(direction.x,direction.z)+math.pi/2)
end
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
-------------------------------------------------------------------------------
-- name: get_entity(inv)
--
--! @brief find entity linked to inventory
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: fill_prices(entity,inventory,goodname)
--
--! @brief fill price fields
--! @ingroup mob_inventory
--
--! @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
-------------------------------------------------------------------------------
-- name: add_goods(entity,trader_inventory)
--
--! @brief fill inventory with mobs goods
--! @ingroup mob_inventory
--
--! @param entity to look for prices
--! @param trader_inventory to put goods
-------------------------------------------------------------------------------
function mob_inventory.add_goods(entity,trader_inventory)
dbg_mobf.trader_inv_lvl3("MOBF: adding " .. #entity.data.trader_inventory.goods
.. " goods for trader")
for i=1,#entity.data.trader_inventory.goods,1 do
dbg_mobf.trader_inv_lvl3("MOBF:\tadding " .. entity.data.trader_inventory.goods[i][1])
trader_inventory.add_item(trader_inventory,"goods",
entity.data.trader_inventory.goods[i][1])
end
end
--!@}

View File

@ -0,0 +1,406 @@
-------------------------------------------------------------------------------
-- 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
--
--! @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)
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
--
--! @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
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 = 10
if entity.data.movement.follow_speedup ~= nil then
follow_speedup = entity.data.movement.follow_speedup
end
--check max speed limit
mgen_follow.checkspeed(entity)
--check environment
local basepos = entity.getbasepos(entity)
local state = environment.pos_is_ok(basepos,entity)
if state == "ok" 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 not environment.possible_pos(entity,basepos) or
state == "in_water" or
state == "above_water" or
state == "in_air" or
state == "drop_above_water" then
dbg_mobf.fmovement_lvl1("MOBF: followed to wrong place " .. state)
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
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
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 = entity.dynamic_data.spawning.spawnpoint
if entity.dynamic_data.movement.guardspawnpoint ~= true then
dbg_mobf.fmovement_lvl3("MOBF: moving target selected")
targetpos = entity.dynamic_data.movement.target:getpos()
end
if targetpos == nil then
minetest.log(LOGLEVEL_ERROR,"MOBF: " .. entity.data.name
.. " don't have targetpos "
.. "SP: " .. dump(entity.dynamic_data.spawning.spawnpoint)
.. " TGT: " .. dump(entity.dynamic_data.movement.target))
return
end
local distance = mobf_calc_distance_2d(basepos,targetpos)
local yaccel = environment.get_default_gravity(basepos,
entity.environment.media,
entity.data.movement.canfly)
dbg_mobf.fmovement_lvl3("MOBF: default gravity is " .. yaccel )
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)
if distance > max_distance then
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)
accel_to_set.y = yaccel
dbg_mobf.fmovement_lvl3("MOBF: setting acceleration to: " .. printpos(accel_to_set));
mgen_follow.set_acceleration(entity,accel_to_set,follow_speedup)
handled = true
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)
accel_to_set.y = yaccel
local current_velocity = entity.object:getvelocity()
local predicted_pos = movement_generic.predict_next_block(basepos,current_velocity,accel_to_set)
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)
end
dbg_mobf.fmovement_lvl3("MOBF: setting acceleration to: "
.. printpos(accel_to_set) .. " predicted_state: "
.. pos_state);
mgen_follow.set_acceleration(entity,accel_to_set,follow_speedup)
handled = true
end
if handled == false then
dbg_mobf.fmovement_lvl1("MOBF: \t Unexpected movement state: " .. current_state)
end
--nothing to do
else
dbg_mobf.fmovement_lvl3("MOBF: next to target")
entity.object:setvelocity({x=0,y=0,z=0})
entity.object:setacceleration({x=0,y=0,z=0})
entity.dynamic_data.movement.last_next_to_target = now
local dir = mobf_get_direction(basepos,targetpos)
--update mob orientation
if entity.mode == "3d" then
entity.object:setyaw(mobf_calc_yaw(dir.x,dir.z))
else
entity.object:setyaw(mobf_calc_yaw(dir.x,dir.z))
end
end
else
--TODO evaluate if this is an error case
end
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,
orientation_fix_needed = 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 = math.sqrt(math.pow(current_velocity.x,2)+
math.pow(current_velocity.z,2))
if (xzspeed > entity.data.movement.max_speed) then
--preserver orientation when correcting speed
local dir = mobf_calc_yaw(current_velocity.x,current_velocity.z)
local velocity_to_set = mobf_calc_vector_components(dir,entity.data.movement.max_speed * 0.25)
velocity_to_set.y=current_velocity.y
entity.object:setvelocity(velocity_to_set)
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
-------------------------------------------------------------------------------
function mgen_follow.set_acceleration(entity,accel,speedup)
entity.object:setacceleration({x=accel.x*speedup,
y=accel.y,
z=accel.z*speedup})
end
--register this movement generator
registerMovementGen(mgen_follow.name,mgen_follow)

View File

@ -0,0 +1,139 @@
-------------------------------------------------------------------------------
-- 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 = {}
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()
entity.object:setyaw(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.env: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.env: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.env:get_node(entity.object:getpos()).name
if is_in_door == "doors:door_wood_t_1" then
minetest.env: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.env: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,
orientation_fix_needed = false,
}
entity.dynamic_data.movement = data
end
--register this movement generator
registerMovementGen(mgen_jordan4ibanez.name,mgen_jordan4ibanez)

View File

@ -0,0 +1,204 @@
-------------------------------------------------------------------------------
-- 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 (NOT COMPLETED)
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup mgen_path_based MGEN: Path based movement generator (NOT COMPLETED)
--! @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 = {}
--!@}
--! @brief movement generator identifier
--! @memberof p_mov_gen
p_mov_gen.name = "mgen_path"
-------------------------------------------------------------------------------
-- name: validate_position(current_pos,origin,destination)
--
--! @brief check if current position is on movement path to destination
--! @memberof p_mov_gen
--! @private
--
--! @param current_pos
--! @param origin of movement
--! @param destination of movement
-------------------------------------------------------------------------------
function p_mov_gen.validate_path_position(current_pos,origin,destination)
end
-------------------------------------------------------------------------------
-- name: validate_position(current_pos,origin,destination)
--
--! @brief check if there's a direct path from pos1 to pos2 for this mob
--! @memberof p_mov_gen
--! @private
--
-- param1: mob to check
-- param2: position1
-- param3: position2
-- retval: -
-------------------------------------------------------------------------------
function p_mov_gen.direct_path_available(entity,pos1,pos2)
end
-------------------------------------------------------------------------------
-- name: find_destination(entity,current_pos)
--
--! @brief find a suitable destination for this mob
--! @memberof p_mov_gen
--! @private
--
-- param1: mob to get destination for
-- param2: current position
-- retval: -
-------------------------------------------------------------------------------
function p_mov_gen.find_destination(entity,current_pos)
--TODO
end
-------------------------------------------------------------------------------
-- name: set_speed(entity,destination)
--
--! brief set speed to destination for an mob
--! @memberof p_mov_gen
--! @private
--
-- param1: mob to get destination for
-- param2: destination of mob
-- retval: -
-------------------------------------------------------------------------------
function p_mov_gen.set_speed(entity,destination)
end
-------------------------------------------------------------------------------
-- name: fix_position(entity,current_pos)
--
--! @brief check if mob is in a valid position and fix it if necessary
--! @memberof p_mov_gen
--! @private
--
-- param1: mob to get destination for
-- param2: position of mob
-- retval: -
-------------------------------------------------------------------------------
function p_mov_gen.fix_position(entity,current_pos)
end
-------------------------------------------------------------------------------
-- name: update_movement(entity,now)
--
--! @brief check and update current movement state
--! @memberof p_mov_gen
--! @private
--
-- param1: mob to move
-- param2: current time
-- retval: -
-------------------------------------------------------------------------------
function p_mov_gen.update_movement(entity,now)
--position of base block (different from center for ground based mobs)
local pos = entity.getbasepos(entity)
local centerpos = entity.object:getpos()
--validate current position for mob
p_mov_gen.fix_position(entity,pos)
--validate position is on path
if p_mov_gen.validate_path_position(pos,
entity.dynamic_data.p_movement.origin,
entity.dynamic_data.p_movement.destination)
== false then
--validate target is reachable
if p_mov_gen.direct_path_available(entity,pos,entity.dynamic_data.p_movement.destination) then
--set new direction to target
p_mov_gen.set_speed(entity,dynamic_data.p_movement.destination)
else -- get new destination
dynamic_data.p_movement.destination = p_mov_gen.find_destination(entity,pos)
if dynamic_data.p_movement.destination ~= nil then
p_mov_gen.set_speed(entity,dynamic_data.p_movement.destination)
else
mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG !!! unable to find a destination for an mob!")
end
end
end
end
-------------------------------------------------------------------------------
-- 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)
-- mob is in movement do movement handling
if entity.dynamic_data.p_movement.in_movement then
p_mov_gen.update_movement(entity,now)
else
-- calculate start movement chance
--TODO
end
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
-- @brief initialize dynamic data required by movement generator
--! @memberof p_mov_gen
--
-- param1: entity to initialize
-- param2: current time
-- retval: -
-------------------------------------------------------------------------------
function p_mov_gen.init_dynamic_data(entity,now)
local pos = entity.object:getpos()
local data = {
origin = pos,
targetlist = nil,
eta = nil,
last_move_stop = now,
in_movement = false
}
entity.dynamic_data.p_movement = data
end

View File

@ -0,0 +1,359 @@
-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file 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 new_accel = direction_control.get_random_acceleration(entity.data.movement.min_accel,
entity.data.movement.max_accel,entity.object:getyaw(),0)
local pos_predicted = movement_generic.predict_next_block(pos,current_velocity,new_accel)
local maxtries = 5
local state = environment.pos_is_ok(pos_predicted,entity)
--below_limit and above_limit are ok too respective to movement handled by this component is xz only
while state ~= "ok" and
state == "above_limit" and
state == "below_limit"do
dbg_mobf.pmovement_lvl1("MOBF: predicted pos " .. printpos(pos_predicted) .. " isn't ok " .. maxtries .. " tries left, state: " .. state)
local done = false
--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
--in case mob is already at non perfect surface it's not that bad to get on it again
if not done and (state == "possible_surface") then
local current_state = environment.pos_is_ok(pos,entity)
local probab = math.random()
if current_state == "wrong_surface" or
current_state == "possible_surface" then
if probab < 0.3 then
done = true
end
else
if probab < 0.05 then
done = true
end
end
end
--first try to invert acceleration on collision
-- if not done and (state == "collision" or
-- state == "collision_jumpable") then
-- new_accel = { x= new_accel.x * -1,
-- y= new_accel.y,
-- z= new_accel.z * -1}
-- pos_predicted = movement_generic.predict_next_block(pos,current_velocity,new_accel)
-- if environment.pos_is_ok(pos_predicted,entity) == "ok" then
-- done = true
-- end
-- end
--generic way to find new acceleration
if not done then
new_accel = direction_control.get_random_acceleration(entity.data.movement.min_accel,
entity.data.movement.max_accel,entity.object:getyaw(),1.57)
pos_predicted = movement_generic.predict_next_block(pos,current_velocity,new_accel)
end
state = environment.pos_is_ok(pos_predicted,entity)
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 = 0
--randomize direction
for i=0, 100 do
if math.random() < 0.2 then
break
end
orientation_delta = orientation_delta +0.1
end
--calculate new acceleration
local new_direction = current_yaw + ((minrotation + orientation_delta) * direction)
local new_accel = {
x = math.sin(new_direction) *rand_accel,
y = nil,
z = math.cos(new_direction) *rand_accel
}
dbg_mobf.pmovement_lvl3(" new direction: " .. new_direction .. " old direction: " .. current_yaw .. " new accel: " .. printpos(new_accel))
return new_accel
end
-------------------------------------------------------------------------------
-- name: precheck_movement(entity,movement_state)
--
--! @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_state suitability state of predicted position
--! @return movement_state is changed!
-------------------------------------------------------------------------------
function direction_control.precheck_movement(entity,movement_state,pos_predicted,pos_predicted_state)
--next block mob is to be isn't a place where it can be so we need to change something
if pos_predicted_state ~= "ok" and
pos_predicted_state ~= "above_limit" and
pos_predicted_state ~= "below_limit" then
-- mob would walk onto water
if movement_state.changed == false and
( pos_predicted_state == "above_water" or
pos_predicted_state == "drop" or
pos_predicted_state == "drop_above_water")
then
dbg_mobf.pmovement_lvl1("MOBF: mob " .. entity.data.name .. " is going to walk on water or drop")
local new_pos = environment.get_suitable_pos_same_level(movement_state.basepos,1,entity)
--try to find at least a possible position
if new_pos == nil then
dbg_mobf.pmovement_lvl1("MOBF: mob " .. entity.data.name .. " no prefect fitting position found")
new_pos = environment.get_suitable_pos_same_level(movement_state.basepos,1,entity,true)
end
if new_pos ~= nil then
dbg_mobf.pmovement_lvl2("MOBF: redirecting to safe position .. " .. printpos(new_pos))
movement_state.accel_to_set = movement_generic.get_accel_to(new_pos,entity)
pos_predicted = movement_generic.predict_next_block( movement_state.basepos,
movement_state.current_velocity,
movement_state.accel_to_set)
pos_predicted_state = environment.pos_is_ok({x=pos_predicted.x,y=pos_predicted.y+1,z=pos_predicted.z},entity)
for i=0,10,1 do
if pos_predicted_state == "ok" or
pos_predicted_state == "possible_surface" then
break
end
movement_state.accel_to_set = movement_generic.get_accel_to(new_pos,entity)
pos_predicted = movement_generic.predict_next_block( movement_state.basepos,
movement_state.current_velocity,
movement_state.accel_to_set)
pos_predicted_state = environment.pos_is_ok({x=pos_predicted.x,y=pos_predicted.y+1,z=pos_predicted.z},entity)
end
if pos_predicted_state == "ok" or
pos_predicted_state == "possible_surface" then
dbg_mobf.pmovement_lvl1("MOBF: redirecting to safe position .. " .. printpos(new_pos) .. " failed!")
end
movement_state.changed = true
else
local current_state = environment.pos_is_ok(movement_state.basepos,entity)
--animal is safe atm stop it to avoid doing silly things
if current_state == "ok" or
current_state == "possible_surface" then
entity.object:setvelocity({x=0,y=0,z=0})
movement_state.accel_to_set = {x=0,y=nil,z=0}
movement_state.changed = true
dbg_mobf.pmovement_lvl2("MOBF: couldn't find acceleration but mob is safe where it is")
else
--apply random acceleration to avoid permanent stuck mobs maybe mobs should be deleted instead
movement_state.accel_to_set = direction_control.get_random_acceleration(entity.data.movement.min_accel,
entity.data.movement.max_accel,entity.object:getyaw(),0)
movement_state.changed = true
dbg_mobf.pmovement_lvl2("MOBF: couldn't find acceleration mob ain't safe either, just move on with random movement")
end
end
end
if movement_state.changed == false and pos_predicted_state == "collision_jumpable" then
dbg_mobf.movement_lvl1("mob is about to collide")
if environment.pos_is_ok({x=pos_predicted.x,y=pos_predicted.y+1,z=pos_predicted.z},entity) == "ok" then
if math.random() < ( entity.dynamic_data.movement.mpattern.jump_up * PER_SECOND_CORRECTION_FACTOR) then
local node_at_predicted_pos = minetest.env:get_node(pos_predicted)
dbg_mobf.pmovement_lvl2("MOBF: velocity is:" .. printpos(movement_state.current_velocity) .. " position is: "..printpos(pos) )
dbg_mobf.pmovement_lvl2("MOBF: estimated position was: "..printpos(pos_predicted))
dbg_mobf.pmovement_lvl2("MOBF: predicted node state is: " .. environment.pos_is_ok(pos_predicted,entity))
--if node_at_predicted_pos ~= nil then
--dbg_mobf.movement_lvl1("MOBF: jumping onto: " .. node_at_predicted_pos.name)
--end
movement_state.accel_to_set = { x=0,y=nil,z=0 }
movement_state.changed = true
--todo check if y pos is ok?!
local jumppos = {x=pos_predicted.x,y=movement_state.centerpos.y+1,z=pos_predicted.z}
dbg_mobf.pmovement_lvl2("MOBF: mob " ..entity.data.name .. " is jumping, moving to:" .. printpos(jumppos))
dbg_mobf.pmovement_lvl2("MOBF: target pos node state is: " .. environment.pos_is_ok(jumppos,entity))
entity.object:moveto(jumppos)
--TODO set movement state positions
--movement_state.basepos=
--movement_state.centerpos=
end
end
end
--redirect mob to block thats not above its current level
--or jump if possible
if movement_state.changed == false and pos_predicted_state == "collision" then
dbg_mobf.pmovement_lvl1("MOBF: mob is about to collide")
local new_pos = environment.get_suitable_pos_same_level(movement_state.basepos,1,entity)
--there is at least one direction to go
if new_pos ~= nil then
movement_state.accel_to_set = movement_generic.get_accel_to(new_pos,entity)
movement_state.changed = true
else
local jumppos = nil
--there ain't any acceptable pos at same level try to find one above current position
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
jumppos = {x=new_pos.x,y=movement_state.centerpos.y+1,z=new_pos.z}
end
--there ain't any acceptable pos at same level try to find one below current position
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
jumppos = {x=new_pos.x,y=movement_state.centerpos.y-1,z=new_pos.z}
end
if jumppos ~= nil then
dbg_mobf.pmovement_lvl2("MOBF: mob " ..entity.data.name .. " seems to be locked in, moving to:" .. printpos(jumppos))
entity.object:moveto(jumppos)
movement_state.accel_to_set = { x=0,y=nil,z=0 }
movement_state.changed = true
end
end
end
--generic try to solve situation eg wrong surface
if movement_state.changed == false then
dbg_mobf.pmovement_lvl1("MOBF: generic try to resolve state " .. pos_predicted_state .. " for mob " .. entity.data.name .. " "..printpos(movement_state.basepos))
movement_state.accel_to_set = direction_control.changeaccel(movement_state.basepos,
entity,movement_state.current_velocity)
if movement_state.accel_to_set ~= nil then
movement_state.changed = true
end
end
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_lvl1("MOBF: random movement handler called")
if movement_state.changed == false and
(math.random() < (entity.dynamic_data.movement.mpattern.random_acceleration_change * PER_SECOND_CORRECTION_FACTOR) 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))
end
end

View File

@ -0,0 +1,291 @@
-------------------------------------------------------------------------------
-- 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 class containing height level 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_state)
--
--! @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_state state of the next position
-------------------------------------------------------------------------------
function height_level_control.precheck_movement(entity,movement_state,pos_predicted,pos_predicted_state)
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_state == "above_limit" 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_state == "below_limit" then
local min_y,max_y = environment.get_absolute_min_max_pos(entity.environment,movement_state.basepos)
if (min_y - pos_predicted.y) > 10 then
movement_state.override_height_change_chance = 1
else
movement_state.override_height_change_chance = (min_y - pos_predicted.y)/10
end
end
end
end
-------------------------------------------------------------------------------
-- name: random_jump_fly(entity,movement_state)
--
--! @brief apply random jump for flying mobs
--! @memberof height_level_control
--! @private
--
--! @param entity mob to apply random jump
--! @param movement_state current movement state
--! @return movement_state is modified!
-------------------------------------------------------------------------------
function height_level_control.random_jump_fly(entity,movement_state)
--get some information
local accel_to_set = movement_state.current_acceleration
local current_state = environment.pos_is_ok(movement_state.basepos,entity)
--find direction
local ydirection = 1
if current_state == "ok" then
if math.random() <= 0.5 then
ydirection = -1
end
end
if current_state == "above_limit" then
ydirection = -1
end
--state "below_limit" is already handled by initialization value
--prepare basic information about what may be afterwards
local targetpos = {x=movement_state.basepos.x,y=movement_state.basepos.y+ydirection,z=movement_state.basepos.z}
local target_state = environment.pos_is_ok(targetpos,entity);
dbg_mobf.pmovement_lvl2("MOBF: " .. entity.data.name .." flying change y accel dir="..ydirection.." ypos="..movement_state.basepos.y..
" min="..entity.environment.min_height_above_ground..
" max="..entity.environment.max_height_above_ground..
" below="..target_state)
--really do level change
if (target_state == "ok") or
target_state == current_state then
local min_y, max_y = environment.get_absolute_min_max_pos(entity.environment,movement_state.basepos)
dbg_mobf.pmovement_lvl2("MOBF: check level borders current=".. movement_state.basepos.y ..
" min=" .. min_y ..
" max=" .. max_y)
movement_state.accel_to_set.y = ydirection * entity.data.movement.min_accel
entity.dynamic_data.movement.ts_random_jump = movement_state.now
entity.dynamic_data.movement.changing_levels = true
dbg_mobf.pmovement_lvl2("MOBF: " .. entity.data.name .. " " .. movement_state.now .. " " ..
" changing level within borders: " .. movement_state.accel_to_set.y)
movement_state.changed = true
end
end
-------------------------------------------------------------------------------
-- name: random_jump_ground(entity,movement_state)
--
--! @brief apply random jump for ground mobs
--! @memberof height_level_control
--! @private
--
--! @param entity mob to apply random jump
--! @param movement_state current movement state
--! @return movement_state is a modification!
-------------------------------------------------------------------------------
function height_level_control.random_jump_ground(entity,movement_state)
if movement_state.default_y_accel == 0 then
minetest.log(LOGLEVEL_ERROR,"MOBF BUG!!! ground mob can't have zero default acceleration!!!")
end
local random_jump_time = 2 * (entity.dynamic_data.movement.mpattern.random_jump_initial_speed /
math.abs(movement_state.default_y_accel))
local next_jump = entity.dynamic_data.movement.ts_random_jump +
entity.dynamic_data.movement.mpattern.random_jump_delay +
random_jump_time
dbg_mobf.movement_lvl2("MOBF: now=" .. movement_state.now .. " time of next jump=" .. next_jump ..
" | " .. random_jump_time .. " " .. entity.dynamic_data.movement.mpattern.random_jump_delay .. " " ..
entity.dynamic_data.movement.ts_random_jump .. " " .. movement_state.default_y_accel .. " " ..
entity.dynamic_data.movement.mpattern.random_jump_initial_speed)
if movement_state.now > next_jump then
local ground_distance = mobf_ground_distance(movement_state.basepos)
--we shouldn't be moving in y direction at that time so probably we are just dropping
--we can only jump while on solid ground!
if ground_distance <= 1 then
local current_speed = entity.object:getvelocity();
dbg_mobf.pmovement_lvl2("MOBF: " .. entity.data.name .." doing random jump "..
"speed: " .. entity.dynamic_data.movement.mpattern.random_jump_initial_speed ..
": ",entity)
current_speed.y = entity.dynamic_data.movement.mpattern.random_jump_initial_speed
entity.object:setvelocity(current_speed)
entity.dynamic_data.movement.ts_random_jump = movement_state.now
else
dbg_mobf.pmovement_lvl2("MOBF: " .. entity.data.name .. " Random jump but ground distance was:" .. ground_distance ..
" " ..movement_state.current_acceleration.y ..
" " ..movement_state.default_y_accel)
--this is a workaround for a bug
--setting y acceleration to 0 for ground born mobs shouldn't be possible
if movement_state.current_acceleration.y == 0 then
movement_state.accel_to_set.x = movement_state.current_acceleration.x
movement_state.accel_to_set.y = movement_state.default_y_accel
movement_state.accel_to_set.z = movement_state.current_acceleration.z
movement_state.changed = true
end
end
end
end
-------------------------------------------------------------------------------
-- name: random_movement_handler(entity,movement_state)
--
--! @brief generate a random y-movement
--! @memberof height_level_control
--
--! @param entity mob to apply random jump
--! @param movement_state current movement state
--! @return movement_state is a modified!
-------------------------------------------------------------------------------
function height_level_control.random_movement_handler(entity,movement_state)
--generate random y movement changes
if movement_state.changed == false and
entity.dynamic_data.movement.changing_levels == false and
(math.random() < entity.dynamic_data.movement.mpattern.random_jump_chance or
math.random() < movement_state.override_height_change_chance) then
dbg_mobf.pmovement_lvl1("MOBF: Random jump chance for mob " .. entity.data.name .. " "..
entity.dynamic_data.movement.ts_random_jump .. " "..
entity.dynamic_data.movement.mpattern.random_jump_delay .. " " .. movement_state.now
)
local to_set
--flying/swiming mobs do level change on random jump chance
if entity.data.movement.canfly then
height_level_control.random_jump_fly(entity,movement_state)
--ground mobs
else
height_level_control.random_jump_ground(entity,movement_state)
end
end
end

View File

@ -0,0 +1,572 @@
-------------------------------------------------------------------------------
-- 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)
print ("\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)
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
--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_lvl1("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_lvl1("MOBF: movement hard limits check for mob ".. entity.data.name .. " "..printpos(movement_state.basepos))
movement_gen.fix_runaway(entity,movement_state)
movement_gen.fix_to_slow(entity,movement_state)
---------------------------------------------------------------------------
---------------------------------------------------------------------------
-- --
-- DO / PREPARE CHANGES THAT NEED TO BE DONE BECAUSE OF MOB IS --
-- PREDICTED TO BE SOMEWHERE WHERE IT SHOULDN'T BE --
-- --
---------------------------------------------------------------------------
---------------------------------------------------------------------------
dbg_mobf.pmovement_lvl1("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)
local pos_predicted_state = environment.pos_is_ok(pos_predicted,entity)
dbg_mobf.pmovement_lvl3("MOBF: Pos predicted state ".. entity.data.name .. ": " .. pos_predicted_state)
-- Y-Movement
if movement_state.changed == false then
height_level_control.precheck_movement(entity,movement_state,pos_predicted,pos_predicted_state)
end
-- X/Z-Movement
if movement_state.changed == false then
direction_control.precheck_movement(entity,movement_state,pos_predicted,pos_predicted_state)
end
end
---------------------------------------------------------------------------
---------------------------------------------------------------------------
-- --
-- DO RANDOM CHANGES TO MOB MOVEMENT --
-- --
---------------------------------------------------------------------------
---------------------------------------------------------------------------
dbg_mobf.pmovement_lvl1("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_lvl1("MOBF: setting acceleration to " ..printpos(movement_state.accel_to_set) )
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],
orientation_fix_needed = true,
}
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 = math.sqrt(math.pow(movement_state.current_velocity.x,2)+
math.pow(movement_state.current_velocity.z,2))
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 newaccel = {x=0,y=movement_state.current_acceleration.y,z=0}
--calculate sign of acceleration
if movement_state.current_velocity.x > 0 and
movement_state.current_acceleration.x >= 0 then
newaccel.x = entity.data.movement.max_accel * -1
else
newaccel.x = entity.data.movement.max_accel
end
if movement_state.current_velocity.z > 0 and
movement_state.current_acceleration.z >= 0 then
newaccel.z = entity.data.movement.max_accel * -1
else
newaccel.z = entity.data.movement.max_accel
end
--calculate relative partition of acceleration based on velocity
if movement_state.current_velocity.x > 0 and
movement_state.current_velocity.z > 0 then
newaccel.x = newaccel.x * movement_state.current_velocity.x /
(movement_state.current_velocity.x+movement_state.current_velocity.z)
newaccel.z = newaccel.z * movement_state.current_velocity.z /
(movement_state.current_velocity.x+movement_state.current_velocity.z)
end
dbg_mobf.pmovement_lvl3("MOBF: fixed acceleration:" .. printpos(newaccel))
--set acceleration based on min acceleration
movement_state.accel_to_set = newaccel
movement_state.changed = true
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 = math.sqrt(math.pow(movement_state.current_velocity.x,2) +
math.pow(movement_state.current_velocity.z,2) )
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 then
dbg_mobf.pmovement_lvl3("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, 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 whater 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+1 --TODO use collision box
end
minetest.log(LOGLEVEL_WARNING,"MOBF: Your mob " .. 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 = movement_state.current_velocity.x/10
movement_state.current_velocity.z = 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 " .. 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.env:get_node( current_pos );
dbg_mobf.movement_lvl3( "MOBF: Mob collided with "..tostring( current_pos.x )..":"..
tostring( current_pos.y )..":"..
tostring( current_pos.z ).." Nodename:"..
tostring( current_node.name ).." walkable:"..
tostring( minetest.registered_nodes[current_node.name].walkable ))
if not mobf_is_walkable(current_node) then
local targetpos = environment.get_suitable_pos_same_level({x=current_pos.x,
y=current_pos.y,
z=current_pos.z},
1,
entity)
if targetpos == nil then
local 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_WARNING,"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
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
--register this movement generator
registerMovementGen(movement_gen.name,movement_gen)

View File

@ -0,0 +1,39 @@
-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file dont_move.lua
--! @brief movementpattern for immobile mob
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-10
--
--! @addtogroup mpatterns
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @struct dont_move_prototype
--! @brief a movement pattern resulting in a mob not moving at all
local dont_move_prototype = {
name ="dont_move",
jump_up =0,
random_jump_chance =0,
random_jump_initial_speed =0,
random_jump_delay =0,
random_acceleration_change =0,
--
-- --run towards player or run away? 1 <-> -1
-- player_attraction =0,
-- --maximum distance a player has an effect
-- player_attraction_range =-1,
}
--!@}
table.insert(mov_patterns_defined,dont_move_prototype)

View File

@ -0,0 +1,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 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,
--
-- --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,flight_pattern1_prototype)

View File

@ -0,0 +1,41 @@
-------------------------------------------------------------------------------
-- 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,
--
-- --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,run_and_jump_low_prototype)

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