commit 2f92e28e7adcd2a2a9706192abebe21938279b1f Author: Perttu Ahola Date: Fri Mar 22 22:46:11 2013 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3928b4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +## Generic ignorable patterns and files +*~ +.*.swp +*bak* +tags +*.vim +*.orig +*.rej + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..678fdb9 --- /dev/null +++ b/README.txt @@ -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. + diff --git a/game.conf b/game.conf new file mode 100644 index 0000000..9150e80 --- /dev/null +++ b/game.conf @@ -0,0 +1,2 @@ +name = Dungeon +common_mods = default, fire, bucket diff --git a/minetest.conf b/minetest.conf new file mode 100644 index 0000000..80002fe --- /dev/null +++ b/minetest.conf @@ -0,0 +1,2 @@ +# Default configuration for this game +mg_name = singlenode diff --git a/mods/animal_dm/License.txt b/mods/animal_dm/License.txt new file mode 100644 index 0000000..2a34efe --- /dev/null +++ b/mods/animal_dm/License.txt @@ -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 \ No newline at end of file diff --git a/mods/animal_dm/depends.txt b/mods/animal_dm/depends.txt new file mode 100644 index 0000000..04684db --- /dev/null +++ b/mods/animal_dm/depends.txt @@ -0,0 +1,3 @@ +default +mobf +fire \ No newline at end of file diff --git a/mods/animal_dm/init.lua b/mods/animal_dm/init.lua new file mode 100644 index 0000000..8fd346c --- /dev/null +++ b/mods/animal_dm/init.lua @@ -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") \ No newline at end of file diff --git a/mods/animal_dm/models/DM.blend b/mods/animal_dm/models/DM.blend new file mode 100644 index 0000000..d7a3e79 Binary files /dev/null and b/mods/animal_dm/models/DM.blend differ diff --git a/mods/animal_dm/models/animal_dm.b3d b/mods/animal_dm/models/animal_dm.b3d new file mode 100644 index 0000000..1f5d665 Binary files /dev/null and b/mods/animal_dm/models/animal_dm.b3d differ diff --git a/mods/animal_dm/models/animal_dm.blend b/mods/animal_dm/models/animal_dm.blend new file mode 100644 index 0000000..f922681 Binary files /dev/null and b/mods/animal_dm/models/animal_dm.blend differ diff --git a/mods/animal_dm/sounds/animal_dm_die.ogg b/mods/animal_dm/sounds/animal_dm_die.ogg new file mode 100644 index 0000000..99a8b9a Binary files /dev/null and b/mods/animal_dm/sounds/animal_dm_die.ogg differ diff --git a/mods/animal_dm/sounds/animal_dm_fireball.ogg b/mods/animal_dm/sounds/animal_dm_fireball.ogg new file mode 100644 index 0000000..c5f76d7 Binary files /dev/null and b/mods/animal_dm/sounds/animal_dm_fireball.ogg differ diff --git a/mods/animal_dm/sounds/animal_dm_hit.ogg b/mods/animal_dm/sounds/animal_dm_hit.ogg new file mode 100644 index 0000000..417b622 Binary files /dev/null and b/mods/animal_dm/sounds/animal_dm_hit.ogg differ diff --git a/mods/animal_dm/sounds/animal_dm_random_1.ogg b/mods/animal_dm/sounds/animal_dm_random_1.ogg new file mode 100644 index 0000000..a65c262 Binary files /dev/null and b/mods/animal_dm/sounds/animal_dm_random_1.ogg differ diff --git a/mods/animal_dm/sounds/soundlist.txt b/mods/animal_dm/sounds/soundlist.txt new file mode 100644 index 0000000..edf12df --- /dev/null +++ b/mods/animal_dm/sounds/soundlist.txt @@ -0,0 +1,16 @@ +--dm-- +random: + random_1.ogg +hit: + hit.ogg +die: + die.ogg +melee: + hit.ogg +distance: + fireball.ogg + + + + + diff --git a/mods/animal_dm/textures/animal_dm_dm.png b/mods/animal_dm/textures/animal_dm_dm.png new file mode 100644 index 0000000..a52d016 Binary files /dev/null and b/mods/animal_dm/textures/animal_dm_dm.png differ diff --git a/mods/animal_dm/textures/animal_dm_dm_item.png b/mods/animal_dm/textures/animal_dm_dm_item.png new file mode 100644 index 0000000..14b9aa0 Binary files /dev/null and b/mods/animal_dm/textures/animal_dm_dm_item.png differ diff --git a/mods/animal_dm/textures/animal_dm_mesh.png b/mods/animal_dm/textures/animal_dm_mesh.png new file mode 100644 index 0000000..386674c Binary files /dev/null and b/mods/animal_dm/textures/animal_dm_mesh.png differ diff --git a/mods/animal_dm/textures/animal_dm_mesh2.jpg b/mods/animal_dm/textures/animal_dm_mesh2.jpg new file mode 100644 index 0000000..220d410 Binary files /dev/null and b/mods/animal_dm/textures/animal_dm_mesh2.jpg differ diff --git a/mods/animal_dm/vault.lua b/mods/animal_dm/vault.lua new file mode 100644 index 0000000..09eb0f4 --- /dev/null +++ b/mods/animal_dm/vault.lua @@ -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) \ No newline at end of file diff --git a/mods/animal_rat/depends.txt b/mods/animal_rat/depends.txt new file mode 100644 index 0000000..8880e7f --- /dev/null +++ b/mods/animal_rat/depends.txt @@ -0,0 +1,3 @@ +default +animalmaterials +mobf \ No newline at end of file diff --git a/mods/animal_rat/init.lua b/mods/animal_rat/init.lua new file mode 100644 index 0000000..37bf312 --- /dev/null +++ b/mods/animal_rat/init.lua @@ -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") \ No newline at end of file diff --git a/mods/animal_rat/models/animal_rat.b3d b/mods/animal_rat/models/animal_rat.b3d new file mode 100644 index 0000000..887e318 Binary files /dev/null and b/mods/animal_rat/models/animal_rat.b3d differ diff --git a/mods/animal_rat/models/rat.blend b/mods/animal_rat/models/rat.blend new file mode 100644 index 0000000..180506b Binary files /dev/null and b/mods/animal_rat/models/rat.blend differ diff --git a/mods/animal_rat/models/rat.xcf b/mods/animal_rat/models/rat.xcf new file mode 100644 index 0000000..1b4a6ee Binary files /dev/null and b/mods/animal_rat/models/rat.xcf differ diff --git a/mods/animal_rat/textures/animal_rat_mesh.png b/mods/animal_rat/textures/animal_rat_mesh.png new file mode 100644 index 0000000..9b989c8 Binary files /dev/null and b/mods/animal_rat/textures/animal_rat_mesh.png differ diff --git a/mods/animal_rat/textures/animal_rat_rat.png b/mods/animal_rat/textures/animal_rat_rat.png new file mode 100644 index 0000000..db49eba Binary files /dev/null and b/mods/animal_rat/textures/animal_rat_rat.png differ diff --git a/mods/animal_rat/textures/animal_rat_rat_item.png b/mods/animal_rat/textures/animal_rat_rat_item.png new file mode 100644 index 0000000..472cdd8 Binary files /dev/null and b/mods/animal_rat/textures/animal_rat_rat_item.png differ diff --git a/mods/animal_vombie/License.txt b/mods/animal_vombie/License.txt new file mode 100644 index 0000000..b1ba1a1 --- /dev/null +++ b/mods/animal_vombie/License.txt @@ -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 \ No newline at end of file diff --git a/mods/animal_vombie/depends.txt b/mods/animal_vombie/depends.txt new file mode 100644 index 0000000..3662c78 --- /dev/null +++ b/mods/animal_vombie/depends.txt @@ -0,0 +1,2 @@ +default +mobf \ No newline at end of file diff --git a/mods/animal_vombie/flame.lua b/mods/animal_vombie/flame.lua new file mode 100644 index 0000000..02ceedd --- /dev/null +++ b/mods/animal_vombie/flame.lua @@ -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) \ No newline at end of file diff --git a/mods/animal_vombie/init.lua b/mods/animal_vombie/init.lua new file mode 100644 index 0000000..2822401 --- /dev/null +++ b/mods/animal_vombie/init.lua @@ -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") \ No newline at end of file diff --git a/mods/animal_vombie/models/animal_vombie_vombie.b3d b/mods/animal_vombie/models/animal_vombie_vombie.b3d new file mode 100644 index 0000000..964c9b1 Binary files /dev/null and b/mods/animal_vombie/models/animal_vombie_vombie.b3d differ diff --git a/mods/animal_vombie/models/animal_vombie_vombie.xcf b/mods/animal_vombie/models/animal_vombie_vombie.xcf new file mode 100644 index 0000000..e287da3 Binary files /dev/null and b/mods/animal_vombie/models/animal_vombie_vombie.xcf differ diff --git a/mods/animal_vombie/models/vombie.blend b/mods/animal_vombie/models/vombie.blend new file mode 100644 index 0000000..c66fc12 Binary files /dev/null and b/mods/animal_vombie/models/vombie.blend differ diff --git a/mods/animal_vombie/sounds/animal_vombie_random_1.ogg b/mods/animal_vombie/sounds/animal_vombie_random_1.ogg new file mode 100644 index 0000000..0806ca6 Binary files /dev/null and b/mods/animal_vombie/sounds/animal_vombie_random_1.ogg differ diff --git a/mods/animal_vombie/sounds/animal_vombie_sun_damage.ogg b/mods/animal_vombie/sounds/animal_vombie_sun_damage.ogg new file mode 100644 index 0000000..4b06f51 Binary files /dev/null and b/mods/animal_vombie/sounds/animal_vombie_sun_damage.ogg differ diff --git a/mods/animal_vombie/sounds/soundlist.txt b/mods/animal_vombie/sounds/soundlist.txt new file mode 100644 index 0000000..fd4da95 --- /dev/null +++ b/mods/animal_vombie/sounds/soundlist.txt @@ -0,0 +1,16 @@ +--vombie-- +random: + random_1.ogg +hit: + --missing-- +die: + --missing-- +melee: + hit.ogg +sun_damage: + sun_damage.ogg + + + + + diff --git a/mods/animal_vombie/textures/animal_vombie_flames.png b/mods/animal_vombie/textures/animal_vombie_flames.png new file mode 100644 index 0000000..5e388aa Binary files /dev/null and b/mods/animal_vombie/textures/animal_vombie_flames.png differ diff --git a/mods/animal_vombie/textures/animal_vombie_vombie.png b/mods/animal_vombie/textures/animal_vombie_vombie.png new file mode 100644 index 0000000..4e24733 Binary files /dev/null and b/mods/animal_vombie/textures/animal_vombie_vombie.png differ diff --git a/mods/animal_vombie/textures/animal_vombie_vombie_item.png b/mods/animal_vombie/textures/animal_vombie_vombie_item.png new file mode 100644 index 0000000..8f2f51f Binary files /dev/null and b/mods/animal_vombie/textures/animal_vombie_vombie_item.png differ diff --git a/mods/animal_vombie/textures/animal_vombie_vombie_mesh.png b/mods/animal_vombie/textures/animal_vombie_vombie_mesh.png new file mode 100644 index 0000000..cd97217 Binary files /dev/null and b/mods/animal_vombie/textures/animal_vombie_vombie_mesh.png differ diff --git a/mods/animalmaterials/depends.txt b/mods/animalmaterials/depends.txt new file mode 100644 index 0000000..331d858 --- /dev/null +++ b/mods/animalmaterials/depends.txt @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/mods/animalmaterials/init.lua b/mods/animalmaterials/init.lua new file mode 100644 index 0000000..34b982f --- /dev/null +++ b/mods/animalmaterials/init.lua @@ -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") diff --git a/mods/animalmaterials/models/animalmaterials_egg_ent.b3d b/mods/animalmaterials/models/animalmaterials_egg_ent.b3d new file mode 100644 index 0000000..fcbc920 Binary files /dev/null and b/mods/animalmaterials/models/animalmaterials_egg_ent.b3d differ diff --git a/mods/animalmaterials/models/animalmaterials_egg_ent.xcf b/mods/animalmaterials/models/animalmaterials_egg_ent.xcf new file mode 100644 index 0000000..9a61de6 Binary files /dev/null and b/mods/animalmaterials/models/animalmaterials_egg_ent.xcf differ diff --git a/mods/animalmaterials/models/egg.blend b/mods/animalmaterials/models/egg.blend new file mode 100644 index 0000000..4a24a94 Binary files /dev/null and b/mods/animalmaterials/models/egg.blend differ diff --git a/mods/animalmaterials/textures/animalmaterials_bone.png b/mods/animalmaterials/textures/animalmaterials_bone.png new file mode 100644 index 0000000..71c68b6 Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_bone.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_egg.png b/mods/animalmaterials/textures/animalmaterials_egg.png new file mode 100644 index 0000000..50010de Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_egg.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_egg_big.png b/mods/animalmaterials/textures/animalmaterials_egg_big.png new file mode 100644 index 0000000..610ea0e Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_egg_big.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_egg_ent_mesh.png b/mods/animalmaterials/textures/animalmaterials_egg_ent_mesh.png new file mode 100644 index 0000000..d276db1 Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_egg_ent_mesh.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_feather.png b/mods/animalmaterials/textures/animalmaterials_feather.png new file mode 100644 index 0000000..f579568 Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_feather.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_fur.png b/mods/animalmaterials/textures/animalmaterials_fur.png new file mode 100644 index 0000000..c8dd7e3 Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_fur.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_glass.png b/mods/animalmaterials/textures/animalmaterials_glass.png new file mode 100644 index 0000000..9fadc0d Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_glass.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_lasso.png b/mods/animalmaterials/textures/animalmaterials_lasso.png new file mode 100644 index 0000000..ef3797a Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_lasso.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_meat_raw.png b/mods/animalmaterials/textures/animalmaterials_meat_raw.png new file mode 100644 index 0000000..0dea4ec Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_meat_raw.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_milk.png b/mods/animalmaterials/textures/animalmaterials_milk.png new file mode 100644 index 0000000..e535264 Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_milk.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_net.png b/mods/animalmaterials/textures/animalmaterials_net.png new file mode 100644 index 0000000..5f92ddc Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_net.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_saddle.png b/mods/animalmaterials/textures/animalmaterials_saddle.png new file mode 100644 index 0000000..aba0f9c Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_saddle.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_scale_blue.png b/mods/animalmaterials/textures/animalmaterials_scale_blue.png new file mode 100644 index 0000000..b970c23 Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_scale_blue.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_scale_golden.png b/mods/animalmaterials/textures/animalmaterials_scale_golden.png new file mode 100644 index 0000000..6e5dabe Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_scale_golden.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_scale_grey.png b/mods/animalmaterials/textures/animalmaterials_scale_grey.png new file mode 100644 index 0000000..901bc8f Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_scale_grey.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_scale_white.png b/mods/animalmaterials/textures/animalmaterials_scale_white.png new file mode 100644 index 0000000..5d96ac7 Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_scale_white.png differ diff --git a/mods/animalmaterials/textures/animalmaterials_scissors.png b/mods/animalmaterials/textures/animalmaterials_scissors.png new file mode 100644 index 0000000..8d34596 Binary files /dev/null and b/mods/animalmaterials/textures/animalmaterials_scissors.png differ diff --git a/mods/dungeon/depends.txt b/mods/dungeon/depends.txt new file mode 100644 index 0000000..4ad96d5 --- /dev/null +++ b/mods/dungeon/depends.txt @@ -0,0 +1 @@ +default diff --git a/mods/dungeon/init.lua b/mods/dungeon/init.lua new file mode 100644 index 0000000..dd0fa32 --- /dev/null +++ b/mods/dungeon/init.lua @@ -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) + diff --git a/mods/mobf/README b/mods/mobf/README new file mode 100644 index 0000000..45ef3db --- /dev/null +++ b/mods/mobf/README @@ -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 = +-->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 + diff --git a/mods/mobf/api.lua b/mods/mobf/api.lua new file mode 100644 index 0000000..a87581f --- /dev/null +++ b/mods/mobf/api.lua @@ -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 diff --git a/mods/mobf/compatibility.lua b/mods/mobf/compatibility.lua new file mode 100644 index 0000000..6dadcbf --- /dev/null +++ b/mods/mobf/compatibility.lua @@ -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 \ No newline at end of file diff --git a/mods/mobf/debug.lua b/mods/mobf/debug.lua new file mode 100644 index 0000000..42fe20a --- /dev/null +++ b/mods/mobf/debug.lua @@ -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 " .. toadd) + minetest.chat_send_player(player, "Usage: /spawnmob " .. 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 = " ", + 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 + + +--!@} diff --git a/mods/mobf/debug_trace.lua b/mods/mobf/debug_trace.lua new file mode 100644 index 0000000..fb93ab4 --- /dev/null +++ b/mods/mobf/debug_trace.lua @@ -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, +} \ No newline at end of file diff --git a/mods/mobf/depends.txt b/mods/mobf/depends.txt new file mode 100644 index 0000000..2591fc3 --- /dev/null +++ b/mods/mobf/depends.txt @@ -0,0 +1,2 @@ +default +animalmaterials diff --git a/mods/mobf/doc/Doxyfile b/mods/mobf/doc/Doxyfile new file mode 100644 index 0000000..00b1455 --- /dev/null +++ b/mods/mobf/doc/Doxyfile @@ -0,0 +1,1774 @@ +# Doxyfile 1.7.6.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "Mob Framework Minetest Mod" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" "The $name widget" "The $name file" is provides specifies contains represents a an the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST = YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = NO + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +#FILE_PATTERNS = *.c \ + +FILE_PATTERNS = *.lua + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = *.lua=/usr/local/bin/lua2dox + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# style sheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 1 + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/mods/mobf/doc/Testcases.txt b/mods/mobf/doc/Testcases.txt new file mode 100644 index 0000000..37274ea --- /dev/null +++ b/mods/mobf/doc/Testcases.txt @@ -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? diff --git a/mods/mobf/doc/mainpage_description.lua b/mods/mobf/doc/mainpage_description.lua new file mode 100644 index 0000000..5b54245 --- /dev/null +++ b/mods/mobf/doc/mainpage_description.lua @@ -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 diff --git a/mods/mobf/doc/mob_template.lua b/mods/mobf/doc/mob_template.lua new file mode 100644 index 0000000..2d194e4 --- /dev/null +++ b/mods/mobf/doc/mob_template.lua @@ -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 = { "" }, + --! @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 = "", + --! @brief [OPTIONAL] a special sprite to be used for this state + graphics_2d = "", + --! @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"}, + } + } \ No newline at end of file diff --git a/mods/mobf/environment.lua b/mods/mobf/environment.lua new file mode 100644 index 0000000..448f685 --- /dev/null +++ b/mods/mobf/environment.lua @@ -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") \ No newline at end of file diff --git a/mods/mobf/environments/flight_1.lua b/mods/mobf/environments/flight_1.lua new file mode 100644 index 0000000..03e585a --- /dev/null +++ b/mods/mobf/environments/flight_1.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/environments/general_env_sets.lua b/mods/mobf/environments/general_env_sets.lua new file mode 100644 index 0000000..de35543 --- /dev/null +++ b/mods/mobf/environments/general_env_sets.lua @@ -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" + +} +--@} +--@} \ No newline at end of file diff --git a/mods/mobf/environments/meadow.lua b/mods/mobf/environments/meadow.lua new file mode 100644 index 0000000..721bdaa --- /dev/null +++ b/mods/mobf/environments/meadow.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/environments/on_ground_1.lua b/mods/mobf/environments/on_ground_1.lua new file mode 100644 index 0000000..04f9e22 --- /dev/null +++ b/mods/mobf/environments/on_ground_1.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/environments/on_ground_2.lua b/mods/mobf/environments/on_ground_2.lua new file mode 100644 index 0000000..631a404 --- /dev/null +++ b/mods/mobf/environments/on_ground_2.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/environments/open_waters.lua b/mods/mobf/environments/open_waters.lua new file mode 100644 index 0000000..30172c9 --- /dev/null +++ b/mods/mobf/environments/open_waters.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/environments/shallow_waters.lua b/mods/mobf/environments/shallow_waters.lua new file mode 100644 index 0000000..a10b7d4 --- /dev/null +++ b/mods/mobf/environments/shallow_waters.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/environments/simple_air.lua b/mods/mobf/environments/simple_air.lua new file mode 100644 index 0000000..6b3cf2e --- /dev/null +++ b/mods/mobf/environments/simple_air.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/fighting.lua b/mods/mobf/fighting.lua new file mode 100644 index 0000000..94d93f0 --- /dev/null +++ b/mods/mobf/fighting.lua @@ -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 \ No newline at end of file diff --git a/mods/mobf/generic_functions.lua b/mods/mobf/generic_functions.lua new file mode 100644 index 0000000..f6180df --- /dev/null +++ b/mods/mobf/generic_functions.lua @@ -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 +--!@} diff --git a/mods/mobf/graphics.lua b/mods/mobf/graphics.lua new file mode 100644 index 0000000..1d49b6d --- /dev/null +++ b/mods/mobf/graphics.lua @@ -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 \ No newline at end of file diff --git a/mods/mobf/harvesting.lua b/mods/mobf/harvesting.lua new file mode 100644 index 0000000..1143e7e --- /dev/null +++ b/mods/mobf/harvesting.lua @@ -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 diff --git a/mods/mobf/init.lua b/mods/mobf/init.lua new file mode 100644 index 0000000..b0726af --- /dev/null +++ b/mods/mobf/init.lua @@ -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") diff --git a/mods/mobf/inventory.lua b/mods/mobf/inventory.lua new file mode 100644 index 0000000..950b896 --- /dev/null +++ b/mods/mobf/inventory.lua @@ -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 + +--!@} \ No newline at end of file diff --git a/mods/mobf/mgen_follow/main_follow.lua b/mods/mobf/mgen_follow/main_follow.lua new file mode 100644 index 0000000..f02793d --- /dev/null +++ b/mods/mobf/mgen_follow/main_follow.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/mgen_jordan4ibanez/mgen_jordan4ibanez.lua b/mods/mobf/mgen_jordan4ibanez/mgen_jordan4ibanez.lua new file mode 100644 index 0000000..55b3132 --- /dev/null +++ b/mods/mobf/mgen_jordan4ibanez/mgen_jordan4ibanez.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/mgen_pathbased/path_based_movement_gen.lua b/mods/mobf/mgen_pathbased/path_based_movement_gen.lua new file mode 100644 index 0000000..6c6de94 --- /dev/null +++ b/mods/mobf/mgen_pathbased/path_based_movement_gen.lua @@ -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 \ No newline at end of file diff --git a/mods/mobf/mgen_probab/direction_control.lua b/mods/mobf/mgen_probab/direction_control.lua new file mode 100644 index 0000000..70587de --- /dev/null +++ b/mods/mobf/mgen_probab/direction_control.lua @@ -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 \ No newline at end of file diff --git a/mods/mobf/mgen_probab/height_level_control.lua b/mods/mobf/mgen_probab/height_level_control.lua new file mode 100644 index 0000000..7dad707 --- /dev/null +++ b/mods/mobf/mgen_probab/height_level_control.lua @@ -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 diff --git a/mods/mobf/mgen_probab/main_probab.lua b/mods/mobf/mgen_probab/main_probab.lua new file mode 100644 index 0000000..419cbeb --- /dev/null +++ b/mods/mobf/mgen_probab/main_probab.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/mgen_probab/movement_patterns/dont_move.lua b/mods/mobf/mgen_probab/movement_patterns/dont_move.lua new file mode 100644 index 0000000..8a0c2e5 --- /dev/null +++ b/mods/mobf/mgen_probab/movement_patterns/dont_move.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/mgen_probab/movement_patterns/flight_pattern1.lua b/mods/mobf/mgen_probab/movement_patterns/flight_pattern1.lua new file mode 100644 index 0000000..30ad0cd --- /dev/null +++ b/mods/mobf/mgen_probab/movement_patterns/flight_pattern1.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/mgen_probab/movement_patterns/run_and_jump_low.lua b/mods/mobf/mgen_probab/movement_patterns/run_and_jump_low.lua new file mode 100644 index 0000000..25aecd1 --- /dev/null +++ b/mods/mobf/mgen_probab/movement_patterns/run_and_jump_low.lua @@ -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) \ No newline at end of file diff --git a/mods/mobf/mgen_probab/movement_patterns/stop_and_go.lua b/mods/mobf/mgen_probab/movement_patterns/stop_and_go.lua new file mode 100644 index 0000000..aaca5ec --- /dev/null +++ b/mods/mobf/mgen_probab/movement_patterns/stop_and_go.lua @@ -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 stop_and_go.lua +--! @brief movementpattern creating a random stop and go movement e.g. sheep/cow +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-10 +-- +--! @addtogroup mpatterns +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +--! @struct stop_and_go_prototype +--! @brief movement pattern for mobs wandering around randomly +local stop_and_go_prototype = { + name ="stop_and_go", + jump_up =0.4, + + random_jump_chance =0, + random_jump_initial_speed =0, + random_jump_delay =0, + random_acceleration_change =0.01, + +-- +-- --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,stop_and_go_prototype) \ No newline at end of file diff --git a/mods/mobf/mgen_probab/movement_patterns/swim_pattern1.lua b/mods/mobf/mgen_probab/movement_patterns/swim_pattern1.lua new file mode 100644 index 0000000..9cfa87e --- /dev/null +++ b/mods/mobf/mgen_probab/movement_patterns/swim_pattern1.lua @@ -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 swim_pattern1.lua +--! @brief movementpattern for slow swimming mobs +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-10 +-- +--! @addtogroup mpatterns +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +--! @struct swim_pattern1_prototype +--! @brief movement pattern for mobs swimming slow +local swim_pattern1_prototype = { + name ="swim_pattern1", + jump_up =0, + + random_jump_chance =0.2, + random_jump_initial_speed =0, + random_jump_delay =10, + random_acceleration_change =0.5, + +-- +-- --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,swim_pattern1_prototype) \ No newline at end of file diff --git a/mods/mobf/mgen_probab/movement_patterns/swim_pattern2.lua b/mods/mobf/mgen_probab/movement_patterns/swim_pattern2.lua new file mode 100644 index 0000000..7dbc342 --- /dev/null +++ b/mods/mobf/mgen_probab/movement_patterns/swim_pattern2.lua @@ -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 swim_pattern2.lua +--! @brief movementpattern for medium swimming mobs +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-10 +-- +--! @addtogroup mpatterns +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +--! @struct swim_pattern1_prototype +--! @brief movement pattern for mobs swimming medium speeds +local swim_pattern2_prototype = { + name ="swim_pattern2", + jump_up =0, + + random_jump_chance =0.4, + random_jump_initial_speed =0, + random_jump_delay =15, + random_acceleration_change =0.7, + +-- +-- --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,swim_pattern2_prototype) \ No newline at end of file diff --git a/mods/mobf/mgen_probab/movement_patterns/template.lua b/mods/mobf/mgen_probab/movement_patterns/template.lua new file mode 100644 index 0000000..5141048 --- /dev/null +++ b/mods/mobf/mgen_probab/movement_patterns/template.lua @@ -0,0 +1,37 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file template.lua +--! @brief template file for movement patterns +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-11 +-- +--! @addtogroup mpatterns +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- +--movement pattern for movement generator +-- { +-- --patternname +-- name ="example" +-- +-- --chance to jump to higher level instead of turning +-- jump_up =0, +-- +-- --chance an mob does random jumps +-- random_jump_chance =0, +-- --mobs jump speed (controling height of jump) +-- random_jump_initial_speed =0, +-- --minimum time between random jumps +-- random_jump_delay =0, +-- +-- --chance an mob randomly changes its speed/direction +-- random_acceleration_change =0, +-- } + +--!@} \ No newline at end of file diff --git a/mods/mobf/mgen_rasterized/mgen_raster.lua b/mods/mobf/mgen_rasterized/mgen_raster.lua new file mode 100644 index 0000000..c678d2a --- /dev/null +++ b/mods/mobf/mgen_rasterized/mgen_raster.lua @@ -0,0 +1,196 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file mgen_raster.lua +--! @brief component containing a probabilistic movement generator (uses mgen follow) +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @defgroup mgen_raster MGEN: raster based movement generator +--! @brief A movement generator creating movement that creates a rasterized +--! random movement +--! @ingroup framework_int +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +--! @class mgen_raster +--! @brief a movement generator creating a rasterized probabilistic movement +mgen_raster = {} + +--!@} + +--! @brief movement generator identifier +--! @memberof mgen_follow +mgen_raster.name = "probab_raster_mov_gen" + + +------------------------------------------------------------------------------- +-- name: stop(entity) +-- +--! @brief stop this entity +--! @memberof mgen_raster +-- +--! @param entity mob to stop +------------------------------------------------------------------------------- +function mgen_raster.stop(entity) + local defgrav = environment.get_default_gravity(entity.getbasepos(entity), + entity.environment, + entity.data.movement.canfly) + + entity.dynamic_data.movement.target = nil + entity.object:setvelocity({x=0,y=0,z=0}) + entity.object:setacceleration({x=0,y=defgrav,z=0}) +end + +------------------------------------------------------------------------------- +-- name: callback(entity,now) +-- +--! @brief main callback to make a mob follow its target +--! @memberof mgen_raster +-- +--! @param entity mob to generate movement for +--! @param now current time +------------------------------------------------------------------------------- +function mgen_raster.callback(entity,now) + + local basepos = entity.getbasepos(entity) + local dtime = entity.current_dtime + + entity.dynamic_data.movement.time_travelled = entity.dynamic_data.movement.time_travelled + dtime + + --check environment + local pos_state = environment.pos_is_ok(basepos,entity) + if pos_state ~= "ok" and + pos_state ~= "possible_surface" then + mgen_raster.stop(entity) + + --try to find a good position around to move mob to + local new_pos = environment.get_suitable_pos_same_level(basepos,1,entity,false) + + if (new_pos == nil ) then + new_pos = environment.get_suitable_pos_same_level({x=basepos.x, + y=basepos.y+1, + z=basepos.z} + ,1,entity,false) + end + + if (new_pos == nil ) then + new_pos = environment.get_suitable_pos_same_level({x=basepos.x, + y=basepos.y+1, + z=basepos.z} + ,1,entity,false) + end + + if (new_pos ~= nil ) then + --TODO fix position according to model information! + enity.object:moveto(new_pos) + basepos = new_pos + else + --TODO error handling + end + end + + --check on ground + if not mgen_raster.onground(entity,now,basepos) then + mgen_raster.stop(entity) + end + + --check how long we've been traveling + if ( entity.dynamic_data.movement.time_travelled > entity.dynamic_data.movement.eta ) then + mgen_raster.stop(entity) + end + + --check distance to target (target reached or missed) + local distance_to_target = mobf_calc_distance(basepos,entity.dynamic_data.movement.target) + if distance_to_target > 2.0 or + distance_to_target < 0.1 then + mgen_raster.stop(entity) + end + + --find next point + if entity.dynamic_data.nextpoint == nil then + entity.dynamic_data.nextpoint = environment.get_suitable_pos_same_level(basepos,1,entity,false) + --TODO calc maximum eta + 10% + end + + --call move to target mgen + helper_mgen.callback(entity,now) +end + +------------------------------------------------------------------------------- +-- name: initialize() +-- +--! @brief initialize movement generator +--! @memberof mgen_raster +--! @public +------------------------------------------------------------------------------- +function mgen_raster.initialize(entity,now) +--intentionaly doing nothing this function is for symmetry reasons only +end + +------------------------------------------------------------------------------- +-- name: init_dynamic_data(entity,now) +-- +--! @brief initialize dynamic data required by movement generator +--! @memberof mgen_raster +--! @public +-- +--! @param entity mob to initialize dynamic data +--! @param now current time +------------------------------------------------------------------------------- +function mgen_raster.init_dynamic_data(entity,now) + + local pos = entity.object:getpos() + + local data = { + target = nil, + guardspawnpoint = true, + orientation_fix_needed = true, + time_travelled = 0, + helper_mgen = getMovementGen(follow_mov_gen), + max_distance = 0.1, + } + + entity.dynamic_data.movement = data + +end + + +------------------------------------------------------------------------------- +-- name: onground(entity,now) +-- +--! @brief check if mob is touching ground +--! @memberof mgen_raster +--! @public +-- +--! @param entity mob to initialize dynamic data +--! @param now current time +--! @param basepos position of feet +------------------------------------------------------------------------------- +function mgen_raster.onground(entity,now,basepos) + + local posbelow = { x=basepos.x, y=basepos.y-1,z=basepos.z} + + for dx=-1,1 do + for dz=-1,1 do + local fp0 = posbelow + local fp = { x= posbelow.x + (0.5*dx), + y= posbelow.y, + z= posbelow.z + (0.5*dz) } + local n = minetest.env:get_node(fp) + if mobf_is_walkable(n) then + return true + end + end + end + +end + +--register this movement generator +registerMovementGen(mgen_raster.name,mgen_raster) \ No newline at end of file diff --git a/mods/mobf/mob_state.lua b/mods/mobf/mob_state.lua new file mode 100644 index 0000000..0462349 --- /dev/null +++ b/mods/mobf/mob_state.lua @@ -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 mob_state.lua +--! @brief component mob state transition handling +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +-- +--! @defgroup mob_state State handling functions +--! @brief a component to do basic changes to mob on state change +--! @ingroup framework_int +--! @{ +-- +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +mob_state = {} +mob_state.default_state_time = 30 + +------------------------------------------------------------------------------- +-- name: initialize(entity,now) +-- +--! @brief initialize state dynamic data +--! @ingroup mob_state +-- +--! @param entity elemet to initialize state data +--! @param now current time +------------------------------------------------------------------------------- +function mob_state.initialize(entity,now) + + dbg_mobf.mob_state_lvl3("MOBF: " .. entity.data.name + .. " initializing state dynamic data") + + local state = { + current = "default", + time_to_next_change = 30, + locked = false, + enabled = false, + } + + local sum_chances = 0 + local state_count = 0 + + if entity.data.states ~= nil then + for s = 1, #entity.data.states , 1 do + sum_chances = sum_chances + entity.data.states[s].chance + + if entity.data.states[s].name ~= "combat" and + entity.data.states[s].name ~= "default" then + state_count = state_count +1 + end + end + end + + --sanity check for state chances + if sum_chances > 1 then + minetest.log(LOGLEVEL_WARNING,"MOBF: Warning sum of state chances for mob " + .. entity.data.name .. " > 1") + end + + --only enable state changeing if there is at least one state + if state_count > 0 then + state.enabled = true + end + + entity.dynamic_data.state = state +end + + +------------------------------------------------------------------------------- +-- name: get_entity_name(mob,state) +-- +--! @brief get entity name for a state +--! @ingroup mob_state +-- +--! @param mob generic data +--! @param state selected state data +--! +--! @return name to use for entity +------------------------------------------------------------------------------- +function mob_state.get_entity_name(mob,state) + return mob.modname .. ":"..mob.name .. "__" .. state.name +end + +------------------------------------------------------------------------------- +-- name: get_state_by_name(entity,name) +-- +--! @brief get a state by its name +--! @ingroup mob_state +-- +--! @param entity elemet to look for state data +--! @param name of state +--! +--! @return state data or nil +------------------------------------------------------------------------------- +function mob_state.get_state_by_name(entity,name) + mobf_assert_backtrace(entity ~= nil and entity.data ~= nil) + + for i=1, #entity.data.states, 1 do + if entity.data.states[i].name == name then + return entity.data.states[i] + end + end + + return nil +end + +------------------------------------------------------------------------------- +-- name: lock(entity,value) +-- +--! @brief disable random state changes for a mob +--! @ingroup mob_state +-- +--! @param entity elemet to lock +--! @param value to set +------------------------------------------------------------------------------- +function mob_state.lock(entity,value) + if value ~= false and value ~= true then + return + end + if entity.dynamic_data.state == nil then + dbg_mobf.mob_state_lvl1("MOBF: unable to lock state for: " + .. entity.data.name .. " no state dynamic data present") + return + end + + entity.dynamic_data.state.locked = value +end + + +------------------------------------------------------------------------------- +-- name: callback(entity,now,dstep) +-- +--! @brief callback handling state changes +--! @ingroup mob_state +-- +--! @param entity elemet to look for state data +--! @param now current time +--! @param dstep time passed since last call +------------------------------------------------------------------------------- +function mob_state.callback(entity,now,dstep) + + if entity.dynamic_data.state == nil then + minetest.log(LOGLEVEL_ERRROR,"MOBF BUG: " .. entity.data.name + .. " mob state callback without mob dynamic data!") + mob_state.initialize(entity,now) + local default_state = mob_state.get_state_by_name(self,"default") + entity.dynamic_data.current_movement_gen = getMovementGen(default_state.movgen) + entity.dynamic_data.current_movement_gen.init_dynamic_data(entity,mobf_get_current_time()) + entity = spawning.replace_entity(entity,entity.data.modname .. ":"..entity.data.name,true) + return true + end + --abort state change if current state is locked + if entity.dynamic_data.state.locked or + entity.dynamic_data.state.enabled == false then + dbg_mobf.mob_state_lvl3("MOBF: " .. entity.data.name + .. " state locked or no custom states definded ") + return true + end + + entity.dynamic_data.state.time_to_next_change = entity.dynamic_data.state.time_to_next_change -dstep + + --do only change if last state timed out + if entity.dynamic_data.state.time_to_next_change < 0 then + + dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name + .. " time to change state: " .. entity.dynamic_data.state.time_to_next_change + .. " , " .. dstep .. " entity=" .. tostring(entity)) + + local rand = math.random() + + local maxvalue = 0 + + local state_table = {} + + --fill table with available states + for i=1, #entity.data.states, 1 do + if entity.data.states[i].custom_preconhandler == nil or + entity.data.states[i].custom_preconhandler() then + table.insert(state_table,entity.data.states[i]) + end + end + + --try to get a random state to change to + for i=1, #state_table, 1 do + + local rand_state = math.random(#state_table) + local current_chance = 0 + + if type (state_table[rand_state].chance) == "function" then + current_chance = state_table[rand_state].chance(entity,now,dstep) + else + if state_table[rand_state].chance ~= nil then + current_chance = state_table[rand_state].chance + end + end + + if math.random() < current_chance then + if mob_state.change_state(entity,state_table[rand_state]) ~= nil then + return false + else + return true + end + end + end + + --switch to default state (only reached if no change has been done + if mob_state.change_state(entity,mob_state.get_state_by_name(entity,"default")) ~= nil then + return false + end + else + dbg_mobf.mob_state_lvl3("MOBF: " .. entity.data.name + .. " is not ready for state change ") + return true + end + + return true +end + +------------------------------------------------------------------------------- +-- name: switch_entity(entity,state) +-- +--! @brief helper function to swich an entity based on new state +--! @ingroup mob_state +-- +--! @param entity to replace +--! @param state to take new entity +--! +--! @return the new entity or nil +------------------------------------------------------------------------------- +function mob_state.switch_entity(entity,state) + --switch entity + local state_has_model = false + + if minetest.setting_getbool("mobf_disable_3d_mode") then + if state.graphics ~= nil then + state_has_model = true + end + else + if state.graphics_3d ~= nil then + state_has_model = true + end + end + + local newentity = nil + + if state_has_model then + dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name + .. " switching to state model ") + newentity = spawning.replace_entity(entity, + mob_state.get_entity_name(entity.data,state),true) + else + dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name + .. " switching to default model ") + newentity = spawning.replace_entity(entity,entity.data.modname + .. ":"..entity.data.name .. "__default",true) + end + + if newentity ~= nil then + dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name + .. " replaced entity=" .. tostring(entity) .. " by newentity=" + .. tostring(newentity)) + return newentity + else + return entity + end +end + +------------------------------------------------------------------------------- +-- name: switch_switch_movgenentity(entity,state) +-- +--! @brief helper function to swich a movement based on new state +--! @ingroup mob_state +-- +--! @param entity to change movement gen +--! @param state to take new entity +------------------------------------------------------------------------------- +function mob_state.switch_movgen(entity,state) + local mov_to_set = nil + + --determine new movement gen + if state.movgen ~= nil then + mov_to_set = getMovementGen(state.movgen) + else + local default_state = mob_state.get_state_by_name(entity,"default") + mov_to_set = getMovementGen(default_state.movgen) + end + + --check if new mov gen differs from old one + if mov_to_set ~= nil and + mov_to_set ~= entity.dynamic_data.current_movement_gen then + entity.dynamic_data.current_movement_gen = mov_to_set + + --TODO initialize new movement gen + entity.dynamic_data.current_movement_gen.init_dynamic_data(entity,mobf_get_current_time()) + end +end + + +------------------------------------------------------------------------------- +-- name: change_state(entity,state) +-- +--! @brief change state for an entity +--! @ingroup mob_state +-- +--! @param entity to change state +--! @param state to change to +--! +--! @return the new entity or nil +------------------------------------------------------------------------------- +function mob_state.change_state(entity,state) + + dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name + .. " state change called entity=" .. tostring(entity) .. " state:" + .. dump(state)) + --check if time precondition handler tells us to stop state change + --if not mob_state.precon_time(state) then + -- return + --end + + --check if custom precondition handler tells us to stop state change + if state ~= nil and + type(state.custom_preconhandler) == "function" then + if not state.custom_preconhandler(entity,state) then + dbg_mobf.mob_state_lvl1("MOBF: " .. entity.data.name + .. " custom precondition handler didn't meet ") + return nil + end + end + + --switch to default state if no state given + if state == nil then + dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name + .. " invalid state switch, switching to default instead of: " + .. dump(state)) + state = mob_state.get_state_by_name("default") + end + + local entityname = entity.data.name + local statename = state.name + + dbg_mobf.mob_state_lvl2("MOBF: " .. entityname .. " switching state to " + .. statename) + + if entity.dynamic_data.state == nil then + mobf_bug_warning(LOGLEVEL_WARNING,"MOBF BUG!!! mob_state no state dynamic data") + return nil + end + + if entity.dynamic_data.state.current ~= state.name then + dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name + .. " different states now really changeing to " .. state.name) + local switchedentity = mob_state.switch_entity(entity,state) + mob_state.switch_movgen(switchedentity,state) + + switchedentity.dynamic_data.state.time_to_next_change = mob_state.getTimeToNextState(state.typical_state_time) + switchedentity.dynamic_data.state.current = state.name + + graphics.set_animation(switchedentity,state.animation) + dbg_mobf.mob_state_lvl2("MOBF: time to next change = " + .. switchedentity.dynamic_data.state.time_to_next_change) + + if switchedentity ~= entity then + return switchedentity + end + else + dbg_mobf.mob_state_lvl2("MOBF: " .. entity.data.name + .. " switching to same state as before") + entity.dynamic_data.state.time_to_next_change = mob_state.getTimeToNextState(state.typical_state_time) + dbg_mobf.mob_state_lvl2("MOBF: time to next change = " + .. entity.dynamic_data.state.time_to_next_change) + end + + return nil +end + + +------------------------------------------------------------------------------- +-- name: getTimeToNextState(typical_state_time) +-- +--! @brief helper function to calculate a gauss distributed random value +--! @ingroup mob_state +-- +--! @param typical_state_time center of gauss +--! +--! @return a random value around typical_state_time +------------------------------------------------------------------------------- +function mob_state.getTimeToNextState(typical_state_time) + + if typical_state_time == nil then + mobf_bug_warning(LOGLEVEL_WARNING,"MOBF MOB BUG!!! missing typical state time!") + typical_state_time = mob_state.default_state_time + end + + local u1 = 2 * math.random() -1 + local u2 = 2 * math.random() -1 + + local q = u1*u1 + u2*u2 + + local maxtries = 0 + + while (q == 0 or q >= 1) and maxtries < 10 do + u1 = math.random() + u2 = math.random() * -1 + q = u1*u1 + u2*u2 + + maxtries = maxtries +1 + end + + --abort random generation + if maxtries >= 10 then + return typical_state_time + end + + local p = math.sqrt( (-2*math.log(q))/q ) + + local retval = 2 + --calculate normalized state time with maximum error or half typical time up and down + if math.random() < 0.5 then + retval = typical_state_time + ( u1*p * (typical_state_time/2)) + else + retval = typical_state_time + ( u2*p * (typical_state_time/2)) + end + + --! ensure minimum state time of 2 seconds + if retval > 2 then + return retval + else + return 2 + end +end + +------------------------------------------------------------------------------- +-- name: prepare_states(mob) +-- +--! @brief register a mob within mob framework +--! @ingroup mob_state +-- +--! @param mob a mob declaration +------------------------------------------------------------------------------- +function mob_state.prepare_states(mob) + local custom_combat_state_defined = false + local default_state_defined = false + + --add graphics for any mob state + if mob.states ~= nil then + for s = 1, #mob.states , 1 do + graphic_to_set = graphics.prepare_info(mob.states[s].graphics, + mob.states[s].graphics_3d, + mob.modname,"_"..mob.name, mob.states[s].name) + + if graphic_to_set ~= nil then + mobf.register_entity(":" .. mob_state.get_entity_name(mob,mob.states[s]), graphic_to_set, mob) + end + + if mob.states[s].name == "combat" then + custom_combat_state_defined = true + end + + if mob.states[s].name == "default" then + default_state_defined = true + end + + + end + else + mob.states = {} + end + + --add a default combat state if no custom state is defined + if mob.combat ~= nil then + if custom_combat_state_defined == false then + table.insert(mob.states, + { + name = "combat", + custom_preconhandler = nil, + movgen = "follow_mov_gen", + typical_state_time = -1, + chance = 0, + }) + end + end + + + --legacy code to run old mobs + if not default_state_defined then + minetest.log(LOGLEVEL_WARNING,"MOBF: -----------------------------------------------------------------------------------------") + minetest.log(LOGLEVEL_WARNING,"MOBF: Automatic default state generation is legacy code subject to be removed in later version.") + minetest.log(LOGLEVEL_WARNING,"MOBF: -----------------------------------------------------------------------------------------") + + local default_state = { + name = "default", + movgen = mob.movement.default_gen, + graphics = mob.graphics, + graphics_3d = mob.graphics_3d, + chance = 0, + typical_state_time = 30, + animation = "walk", + } + + graphic_to_set = graphics.prepare_info(default_state.graphics, + default_state.graphics_3d, + mob.modname,"_"..mob.name) + + if graphic_to_set ~= nil then + mobf.register_entity(":" .. mob_state.get_entity_name(mob,default_state), + graphic_to_set, mob) + end + + --replace old mobs by new default state mobs + minetest.register_entity(":".. mob.modname .. ":"..mob.name, + { + new_name = mob_state.get_entity_name(mob,default_state), + on_activate = function(self,staticdata) + minetest.log(LOGLEVEL_INFO, "MOBF replacing " .. self.name + .. " by " .. self.new_name) + local pos = self.object:getpos() + self.object:remove() + + minetest.env:add_entity(pos,self.new_name) + end + }) + + table.insert(mob.states,default_state) + end +end + +--!@} \ No newline at end of file diff --git a/mods/mobf/mobf.lua b/mods/mobf/mobf.lua new file mode 100644 index 0000000..dad7018 --- /dev/null +++ b/mods/mobf/mobf.lua @@ -0,0 +1,681 @@ +------------------------------------------------------------------------------- +-- 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 mobf.lua +--! @brief class containing mob initialization functions +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +-- +--! @defgroup mobf Basic mob entity functions +--! @brief a component containing basic functions for mob handling and initialization +--! @ingroup framework_int +--! @{ +-- +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +mobf = {} + +mobf.on_step_callbacks = {} +mobf.on_punch_callbacks = {} +mobf.on_rightclick_callbacks = {} + + +------------------------------------------------------------------------------ +-- name: register_on_step_callback(callback) +-- +--! @brief make a new on_step callback known to mobf +--! @ingroup mobf +-- +--! @param callback to make known to mobf +------------------------------------------------------------------------------- +function mobf.register_on_step_callback(callback) + + if callback.configcheck == nil or + type(callback.configcheck) ~= "function" then + return false + end + + if callback.handler == nil or + type(callback.configcheck) ~= "function" then + return false + end + + table.insert(mobf.on_step_callbacks,callback) +end + + +------------------------------------------------------------------------------ +-- name: init_on_step_callbacks(entity,now) +-- +--! @brief initalize callbacks to be used on step +--! @ingroup mobf +-- +--! @param entity entity to initialize on_step handler +--! @param now current time +------------------------------------------------------------------------------- +function mobf.init_on_step_callbacks(entity,now) + entity.on_step_hooks = {} + + if #mobf.on_step_callbacks > 32 then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!!: " .. #mobf.on_step_callbacks + .. " incredible high number of onstep callbacks registred!") + end + + dbg_mobf.mobf_core_lvl2("MOBF: initializing " .. #mobf.on_step_callbacks + .. " on_step callbacks for " .. entity.data.name + .. " entity=" .. tostring(entity)) + for i = 1, #mobf.on_step_callbacks , 1 do + if mobf.on_step_callbacks[i].configcheck(entity) then + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") enabling callback " + .. mobf.on_step_callbacks[i].name) + table.insert(entity.on_step_hooks,mobf.on_step_callbacks[i].handler) + if type(mobf.on_step_callbacks[i].init) == "function" then + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") executing init function for " + .. mobf.on_step_callbacks[i].name) + mobf.on_step_callbacks[i].init(entity,now) + else + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") no init function defined") + end + else + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") callback " + .. mobf.on_step_callbacks[i].name .. " disabled due to config check") + end + end + +end + +------------------------------------------------------------------------------ +-- name: register_on_rightclick_callback(callback) +-- +--! @brief make a new on_rightclick callback known to mobf +--! @ingroup mobf +-- +--! @param callback to make known to mobf +------------------------------------------------------------------------------- +function mobf.register_on_rightclick_callback(callback) + + if callback.configcheck == nil or + type(callback.configcheck) ~= "function" then + return false + end + + if callback.handler == nil or + type(callback.configcheck) ~= "function" then + return false + end + + table.insert(mobf.on_rightclick_callbacks,callback) +end + + +------------------------------------------------------------------------------ +-- name: register_on_punch_callback(callback) +-- +--! @brief make a new on_punch callback known to mobf +--! @ingroup mobf +-- +--! @param callback the callback to register in mobf +------------------------------------------------------------------------------- +function mobf.register_on_punch_callback(callback) + if callback.configcheck == nil or + type(callback.configcheck) ~= "function" then + return false + end + + if callback.handler == nil or + type(callback.configcheck) ~= "function" then + return false + end + + table.insert(mobf.on_punch_callbacks,callback) +end + + +------------------------------------------------------------------------------ +-- name: init_on_punch_callbacks(entity,now) +-- +--! @brief initalize callbacks to be used on punch +--! @ingroup mobf +-- +--! @param entity entity to initialize on_punch handler +--! @param now current time +------------------------------------------------------------------------------- +function mobf.init_on_punch_callbacks(entity,now) + entity.on_punch_hooks = {} + + dbg_mobf.mobf_core_lvl2("MOBF: initializing " .. #mobf.on_punch_callbacks + .. " on_punch callbacks for " .. entity.data.name + .. " entity=" .. tostring(entity)) + for i = 1, #mobf.on_punch_callbacks , 1 do + if mobf.on_punch_callbacks[i].configcheck(entity) and + type(mobf.on_punch_callbacks[i].handler) == "function" then + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") enabling callback " + .. mobf.on_punch_callbacks[i].name) + table.insert(entity.on_punch_hooks,mobf.on_punch_callbacks[i].handler) + + if type(mobf.on_punch_callbacks[i].init) == "function" then + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") executing init function for " + .. mobf.on_punch_callbacks[i].name) + mobf.on_punch_callbacks[i].init(entity,now) + else + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") no init function defined") + end + else + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") callback " + .. mobf.on_punch_callbacks[i].name .. " disabled due to config check") + end + end +end + +------------------------------------------------------------------------------ +-- name: init_on_rightclick_callbacks(entity,now) +-- +--! @brief initalize callbacks to be used on punch +--! @ingroup mobf +-- +--! @param entity entity to initialize on_punch handler +--! @param now current time +------------------------------------------------------------------------------- +function mobf.init_on_rightclick_callbacks(entity,now) + entity.on_rightclick_hooks = {} + + dbg_mobf.mobf_core_lvl2("MOBF: initializing " .. #mobf.on_rightclick_callbacks + .. " on_rightclick callbacks for " .. entity.data.name .. " entity=" .. tostring(entity)) + for i = 1, #mobf.on_rightclick_callbacks , 1 do + if mobf.on_rightclick_callbacks[i].configcheck(entity) and + type(mobf.on_rightclick_callbacks[i].handler) == "function" then + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") enabling callback " + .. mobf.on_rightclick_callbacks[i].name) + table.insert(entity.on_rightclick_hooks,mobf.on_rightclick_callbacks[i].handler) + + if type(mobf.on_rightclick_callbacks[i].init) == "function" then + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") executing init function for " + .. mobf.on_rightclick_callbacks[i].name) + mobf.on_rightclick_callbacks[i].init(entity,now) + else + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") no init function defined") + end + else + dbg_mobf.mobf_core_lvl2("MOBF: (" .. i .. ") callback " + .. mobf.on_rightclick_callbacks[i].name .. " disabled due to config check") + end + end +end + +------------------------------------------------------------------------------ +-- name: get_basepos(entity) +-- +--! @brief get basepos for an entity +--! @ingroup mobf +-- +--! @param entity entity to fetch basepos +--! @return basepos of mob +------------------------------------------------------------------------------- +function mobf.get_basepos(entity) + local pos = entity.object:getpos() + local nodeatpos = minetest.env:get_node(pos) + + dbg_mobf.mobf_core_helper_lvl3("MOBF: " .. entity.data.name + .. " Center Position: " .. printpos(pos) .. " is: " .. nodeatpos.name) + + -- if visual height is more than one block the center of base block is + -- below the entities center + if (entity.collisionbox[2] < -0.5) then + pos.y = pos.y + (entity.collisionbox[2] + 0.5) + dbg_mobf.mobf_core_helper_lvl3("MOBF: collision box lower end: " + .. entity.collisionbox[2]) + end + + nodeatpos = minetest.env:get_node(pos) + dbg_mobf.mobf_core_helper_lvl3("MOBF: Base Position: " .. printpos(pos) + .. " is: " .. nodeatpos.name) + + return pos +end + +------------------------------------------------------------------------------ +-- name: mobf_activate_handler(self,staticdata) +-- +--! @brief hanlder called for basic mob initialization +--! @ingroup mobf +-- +--! @param self entity to initialize onstep handler +--! @param staticdata data to use for initialization +------------------------------------------------------------------------------- +function mobf.activate_handler(self,staticdata) + + + --do some initial checks + local pos = self.object:getpos() + + if pos == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: mob at nil pos!") + end + local current_node = minetest.env:get_node(pos) + + + if current_node == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: trying to activate mob in nil node! removing") + + spawning.remove_uninitialized(self,staticdata) + return + end + + + if environment.is_media_element(current_node.name,self.environment.media) == false then + minetest.log(LOGLEVEL_WARNING,"MOBF: trying to activate mob " + .. self.data.name .. " at invalid position") + minetest.log(LOGLEVEL_WARNING," Activation at: " + .. current_node.name .. " --> removing") + spawning.remove_uninitialized(self,staticdata) + return + end + + + + --do initialization of dynamic modules + local now = mobf_get_current_time() + + spawning.init_dynamic_data(self,now) + + mobf.init_on_step_callbacks(self,now) + mobf.init_on_punch_callbacks(self,now) + mobf.init_on_rightclick_callbacks(self,now) + + --initialize ride support + mobf_ride.init(self) + + --check if this was a replacement mob + if self.dyndata_delayed ~= nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: delayed activation for replacement mob." + .."This shouldn't happen at all but if we can fix it") + self.dynamic_data = dyndata_delayed.data + self.object:set_hp(dyndata_delayed.health) + self.object:setyaw(dyndata_delayed.entity_orientation) + dyndata_delayed = nil + return + end + + --restore saved data + local retval = mobf_deserialize_permanent_entity_data(staticdata) + + if self.dynamic_data.spawning ~= nil then + if mobf_pos_is_zero(retval.spawnpoint) ~= true then + self.dynamic_data.spawning.spawnpoint = retval.spawnpoint + else + self.dynamic_data.spawning.spawnpoint = mobf_round_pos(pos) + end + self.dynamic_data.spawning.player_spawned = retval.playerspawned + + if retval.original_spawntime ~= -1 then + self.dynamic_data.spawning.original_spawntime = retval.original_spawntime + end + + if retval.spawner ~= nil then + minetest.log(LOGLEVEL_INFO,"MOBF: setting spawner to: " + .. retval.spawner) + self.dynamic_data.spawning.spawner = retval.spawner + end + + --only relevant if mob has different states + if retval.state ~= nil and + self.dynamic_data.state ~= nil then + minetest.log(LOGLEVEL_INFO,"MOBF: setting current state to: " + .. retval.state) + self.dynamic_data.state.current = retval.state + + + end + end + + + local current_state = mob_state.get_state_by_name(self,self.dynamic_data.state.current) + local default_state = mob_state.get_state_by_name(self,"default") + + if self.dynamic_data.state.current == nil then + current_state = default_state + end + + + --initialize move gen + if current_state.movgen ~= nil then + dbg_mobf.mobf_core_lvl1("MOBF: setting movegen to: " .. current_state.movgen) + self.dynamic_data.current_movement_gen = getMovementGen(current_state.movgen) + else + dbg_mobf.mobf_core_lvl1("MOBF: setting movegen to: " .. default_state.movgen) + self.dynamic_data.current_movement_gen = getMovementGen(default_state.movgen) + end + + if current_state.animation ~= nil then + dbg_mobf.mobf_core_lvl1("MOBF: setting animation to: " .. current_state.animation) + graphics.set_animation(self,current_state.animation) + else + dbg_mobf.mobf_core_lvl1("MOBF: setting animation to: " .. dump(default_state.animation)) + graphics.set_animation(self,default_state.animation) + end + + mobf_assert_backtrace(self.dynamic_data.current_movement_gen ~= nil) + + self.dynamic_data.current_movement_gen.init_dynamic_data(self,now) + + --initialize armor groups + if self.data.generic.armor_groups ~= nil then + self.object:set_armor_groups(self.data.generic.armor_groups) + end + + --initialize height level + pos = environment.fix_base_pos(self, self.collisionbox[2] * -1) + + --custom on activate handler + if (self.data.generic.custom_on_activate_handler ~= nil) then + self.data.generic.custom_on_activate_handler(self) + end + + self.dynamic_data.initialized = true +end + + + + + +------------------------------------------------------------------------------ +-- name: register_entity(entityname,graphics) +-- +--! @brief register an entity +--! @ingroup mobf +-- +--! @param name of entity to add +--! @param graphics graphics to use for entity +--! @param mob data to use +------------------------------------------------------------------------------- +function mobf.register_entity(name, graphics, mob) + dbg_mobf.mobf_core_lvl1("MOBF: registering new entity: " .. name) + minetest.register_entity(name, + { + physical = true, + collisionbox = graphics.collisionbox, + visual = graphics.visual, + textures = graphics.textures, + visual_size = graphics.visual_size, + spritediv = graphics.spritediv, + mesh = graphics.mesh, + mode = graphics.mode, + initial_sprite_basepos = {x=0, y=0}, + makes_footstep_sound = true, + automatic_rotate = true, + groups = mob.generic.groups, + hp_max = mob.generic.base_health, + + + + + -- actions to be done by mob on its own + on_step = function(self, dtime) + + local starttime = mobf_get_time_ms() + + if self.removed ~= false then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: on_step: " + .. self.data.name .. " on_step for removed entity????") + return + end + + if self.dynamic_data == nil then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: on_step: " + .. "no dynamic data available!") + return + end + + if (self.dynamic_data.initialized == false) then + if entity_at_loaded_pos(self.object:getpos()) then + mobf.activate_handler(self,self.dynamic_data.last_static_data) + self.dynamic_data.last_static_data = nil + else + return + end + end + + --do special ride callback + if mobf_ride.on_step_callback(self) then + return + end + + self.current_dtime = self.current_dtime + dtime + + local now = mobf_get_current_time() + + if self.current_dtime < 0.25 then + return + end + + --check lifetime + if spawning.lifecycle(self,now) == false then + return + end + + mobf_warn_long_fct(starttime,"on_step lifecycle","lifecycle") + + --movement generator + self.dynamic_data.current_movement_gen.callback(self,now) + + mobf_warn_long_fct(starttime,"on_step movement","movement") + + if #self.on_step_hooks > 32 then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!!: " + .. tostring(self) .. " incredible high number of onstep hooks! " + .. #self.on_step_hooks .. " ohlist: " .. tostring(self.on_step_hooks)) + end + + --dynamic modules + for i = 1, #self.on_step_hooks, 1 do + --check return value if on_step hook tells us to stop any other processing + if self.on_step_hooks[i](self,now,self.current_dtime) == false then + dbg_mobf.mobf_core_lvl1("MOBF: on_step: " .. self.data.name + .. " aborting callback processing entity=" .. tostring(self)) + break + end + mobf_warn_long_fct(starttime,"callback nr " .. i,"callback_os_" + .. self.data.name .. "_" .. i) + end + + mobf_warn_long_fct(starttime,"on_step_total","on_step_total") + self.current_dtime = 0 + end, + + --player <-> mob interaction + on_punch = function(self, hitter, time_from_last_punch, tool_capabilities, dir) + local starttime = mobf_get_time_ms() + local now = mobf_get_current_time() + + for i = 1, #self.on_punch_hooks, 1 do + if self.on_punch_hooks[i](self,hitter,now, + time_from_last_punch, tool_capabilities, dir) then + return + end + mobf_warn_long_fct(starttime,"callback nr " .. i,"callback_op_" + .. self.data.name .. "_" .. i) + end + end, + + --rightclick is only used for debug reasons by now + on_rightclick = function(self, clicker) + local starttime = mobf_get_time_ms() + for i = 1, #self.on_rightclick_hooks, 1 do + if self.on_rightclick_hooks[i](self,clicker) then + break + end + mobf_warn_long_fct(starttime,"callback nr " .. i,"callback_or_" + .. self.data.name .. "_" .. i) + end + + --tesrcode only + --mobf_ride.attache_player(self,clicker) + end, + + --do basic mob initialization on activation + on_activate = function(self,staticdata) + + self.dynamic_data = {} + self.dynamic_data.initialized = false + + --make sure entity is in loaded area at initialization + local pos = self.object:getpos() + + if pos ~= nil and + entity_at_loaded_pos(pos) then + mobf.activate_handler(self,staticdata) + else + minetest.log(LOGLEVEL_INFO,"MOBF: animal activated at invalid position .. delaying activation") + self.dynamic_data.last_static_data = staticdata + end + end, + + getbasepos = mobf.get_basepos, + is_on_ground = function(entity) + + local basepos = entity.getbasepos(entity) + local posbelow = { x=basepos.x, y=basepos.y-1,z=basepos.z} + + for dx=-1,1 do + for dz=-1,1 do + local fp0 = posbelow + local fp = { x= posbelow.x + (0.5*dx), + y= posbelow.y, + z= posbelow.z + (0.5*dz) } + local n = minetest.env:get_node(fp) + if not mobf_is_walkable(n) then + return true + end + end + end + + return false + end, + + --prepare permanent data + get_staticdata = function(self) + return mobf_serialize_permanent_entity_data(self) + end, + + --custom variables for each mob + data = mob, + environment = environment_list[mob.generic.envid], + current_dtime = 0, + } + ) +end + +------------------------------------------------------------------------------- +-- name: register_mob_item(mob) +-- +--! @brief add mob item for catchable mobs +--! @ingroup mobf +-- +--! @param name name of mob +--! @param modname name of mod mob is defined in +--! @param description description to use for mob +------------------------------------------------------------------------------- +function mobf.register_mob_item(name,modname,description) + minetest.register_craftitem(modname..":"..name, { + description = description, + image = modname.."_"..name.."_item.png", + on_place = function(item, placer, pointed_thing) + if pointed_thing.type == "node" then + local pos = pointed_thing.above + + local entity = spawning.spawn_and_check(modname..":"..name,"__default",pos,"item_spawner") + + if entity ~= nil then + entity.dynamic_data.spawning.player_spawned = true + + if placer:is_player(placer) then + minetest.log(LOGLEVEL_INFO,"MOBF: mob placed by " .. placer:get_player_name(placer)) + entity.dynamic_data.spawning.spawner = placer:get_player_name(placer) + end + + if entity.data.generic.custom_on_place_handler ~= nil then + entity.data.generic.custom_on_place_handler(entity, placer, pointed_thing) + end + + item:take_item() + end + return item + end + end + }) +end + +------------------------------------------------------------------------------- +-- name: blacklist_handling(mob) +-- +--! @brief add mob item for catchable mobs +--! @ingroup mobf +-- +--! @param mob +------------------------------------------------------------------------------- +function mobf.blacklisthandling(mob) + local blacklisted = minetest.registered_entities[mob.modname.. ":"..mob.name] + + + local on_activate_fct = nil + + --remove unknown animal objects + if minetest.setting_getbool("mobf_delete_disabled_mobs") then + on_activate_fct = function(self,staticdata) + self.object:remove() + end + + + + --cleanup spawners too + if minetest.registered_entities[mob.modname.. ":"..mob.name] == nil and + environment_list[mob.generic.envid] ~= nil and + mobf_spawn_algorithms[mob.spawning.algorithm] ~= nil and + type(mobf_spawn_algorithms[mob.spawning.algorithm].register_cleanup) == "function" then + + mobf_spawn_algorithms[mob.spawning.algorithm].register_cleanup(mob.modname.. ":" .. mob.name) + + if mob.spawning.algorithm_secondary ~= nil and + type(mobf_spawn_algorithms.register_spawn[mob.spawning.algorithm_secondary].register_cleanup) == "function" then + mobf_spawn_algorithms.register_spawn[mob.spawning.algorithm_secondary].register_cleanup(mob.modname.. ":" .. mob.name) + end + end + else + on_activate_fct = function(self,staticdata) + self.object:setacceleration({x=0,y=0,z=0}) + self.object:setvelocity({x=0,y=0,z=0}) + end + end + + if minetest.registered_entities[mob.modname.. ":"..mob.name] == nil then + + --cleanup mob entities + minetest.register_entity(mob.modname.. ":"..mob.name .. "__default", + { + on_activate = on_activate_fct + }) + + if mob.states ~= nil then + for s = 1, #mob.states , 1 do + minetest.register_entity(":".. mob_state.get_entity_name(mob,mob.states[s]), + { + on_activate = on_activate_fct + }) + end + end + end + + + if blacklisted == nil then + minetest.log(LOGLEVEL_INFO,"MOBF: " .. mob.modname.. ":"..mob.name .. " was blacklisted") + else + minetest.log(LOGLEVEL_ERROR,"MOBF: " .. mob.modname.. ":"..mob.name .. " already known not registering mob with same name!") + end +end \ No newline at end of file diff --git a/mods/mobf/mov_gen_none.lua b/mods/mobf/mov_gen_none.lua new file mode 100644 index 0000000..e644caa --- /dev/null +++ b/mods/mobf/mov_gen_none.lua @@ -0,0 +1,78 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file mov_gen_none.lua +--! @brief a dummy movement gen +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +--! @class mgen_none +--! @brief a movement generator doing nothing +mgen_none = {} + +--!@} + +--! @brief movement generator identifier +--! @memberof mgen_none +mgen_none.name = "none" + +------------------------------------------------------------------------------- +-- name: callback(entity,now) +-- +--! @brief main callback to do nothing +--! @memberof mgen_none +-- +--! @param entity mob to generate movement for +--! @param now current time +------------------------------------------------------------------------------- +function mgen_none.callback(entity,now) + local pos = entity.getbasepos(entity) + local speed = entity.object:getvelocity() + local default_y_acceleration = environment.get_default_gravity(pos, + entity.environment.media, + entity.data.movement.canfly) + + entity.object:setacceleration({x=0,y=default_y_acceleration,z=0}) + entity.object:setvelocity({x=0,y=speed.y,z=0}) + +end + +------------------------------------------------------------------------------- +-- name: initialize() +-- +--! @brief initialize movement generator +--! @memberof mgen_none +--! @public +------------------------------------------------------------------------------- +function mgen_none.initialize(entity,now) +end + +------------------------------------------------------------------------------- +-- name: init_dynamic_data(entity,now) +-- +--! @brief initialize dynamic data required by movement generator +--! @memberof mgen_none +--! @public +-- +--! @param entity mob to initialize dynamic data +--! @param now current time +------------------------------------------------------------------------------- +function mgen_none.init_dynamic_data(entity,now) + + local data = { + moving = false, + } + + entity.dynamic_data.movement = data +end + +--register this movement generator +registerMovementGen(mgen_none.name,mgen_none) \ No newline at end of file diff --git a/mods/mobf/movement_gen_registry.lua b/mods/mobf/movement_gen_registry.lua new file mode 100644 index 0000000..08cc804 --- /dev/null +++ b/mods/mobf/movement_gen_registry.lua @@ -0,0 +1,74 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file movement_gen_registry.lua +--! @brief registry for movement generators +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +movement_generators = {} + + +------------------------------------------------------------------------------- +-- name: getMovementGen(id) +-- +--! @brief get movement generator specified by id +--! @ingroup framework_int +-- +--! @param id id of movementgenerator +--! @return module pointer for movementgenerator +------------------------------------------------------------------------------- +function getMovementGen(id) + if movement_generators[id] == nil then + local name = "" + if id ~= nil then + name = id + end + minetest.log(LOGLEVEL_ERROR,"MOBF: movement generator " .. dump(id) .. " not found!") + return nil + end + return movement_generators[id] +end + +------------------------------------------------------------------------------- +-- name: registerMovementGen(name,generator) +-- +--! @brief register a movement generator to mob framework +--! @ingroup framework_mob +-- +--! @param name id to use for movement generator +--! @param generator pointer to movement generator +--! @return true/false successfully added generator +------------------------------------------------------------------------------- +function registerMovementGen(name,generator) + + --some movement gen checks + + if generator.init_dynamic_data == nil then + return false + end + + if generator.callback == nil then + return false + end + if movement_generators[name] == nil then + movement_generators[name] = generator + + minetest.log(LOGLEVEL_NOTICE,"\tRegistering movement generator ".. name) + if generator.initilize ~= nil then + generator.initialize() + end + return true + else + return false + end + +end \ No newline at end of file diff --git a/mods/mobf/movement_generic.lua b/mods/mobf/movement_generic.lua new file mode 100644 index 0000000..f8dcff0 --- /dev/null +++ b/mods/mobf/movement_generic.lua @@ -0,0 +1,133 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file movement_generic.lua +--! @brief generic movement related functions +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @defgroup generic_movement Generic movement functions +--! @brief Movement related functions used by different movement generators +--! @ingroup framework_int +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +movement_generic = {} + +--!@} + +------------------------------------------------------------------------------- +-- name: get_accel_to(new_pos,entity) +-- +--! @brief calculate a random speed directed to new_pos +-- +--! @param new_pos position to go to +--! @param entity mob to move +--! @return { x,y,z } random speed directed to new_pos +------------------------------------------------------------------------------- +-- +function movement_generic.get_accel_to(new_pos,entity) + + if new_pos == nil or entity == nil then + minetest.log(LOGLEVEL_CRITICAL,"MOBF: movement_generic.get_accel_to : Invalid parameters") + end + + local old_pos = entity.object:getpos() + local node = minetest.env:get_node(old_pos) + local maxaccel = entity.data.movement.max_accel + local minaccel = entity.data.movement.min_accel + + local yaccel = environment.get_default_gravity(old_pos, + entity.environment.media, + entity.data.movement.canfly) + mobf_assert_backtrace(yaccel ~= nil) + + -- calc y speed for flying mobs + local x_diff = new_pos.x - old_pos.x + local z_diff = new_pos.z - old_pos.z + + local rand_x = (math.random() * (maxaccel - minaccel)) + minaccel + local rand_z = (math.random() * (maxaccel - minaccel)) + minaccel + + if x_diff < 0 then + rand_x = rand_x * -1 + end + + if z_diff < 0 then + rand_z = rand_z * -1 + end + + return { x=rand_x,y=yaccel,z=rand_z } +end + + + +------------------------------------------------------------------------------- +-- name: calc_new_pos(pos,acceleration,prediction_time) +-- +--! @brief calc the position a mob would be after a specified time +-- this doesn't handle velocity changes due to colisions +-- +--! @param pos position +--! @param acceleration acceleration to predict pos +--! @param prediction_time time to predict pos +--! @param current_velocity current velocity of mob +--! @return { x,y,z } position after specified time +------------------------------------------------------------------------------- +function movement_generic.calc_new_pos(pos,acceleration,prediction_time,current_velocity) + + local predicted_pos = {x=pos.x,y=pos.y,z=pos.z} + + predicted_pos.x = predicted_pos.x + current_velocity.x * prediction_time + (acceleration.x/2)*math.pow(prediction_time,2) + predicted_pos.z = predicted_pos.z + current_velocity.z * prediction_time + (acceleration.z/2)*math.pow(prediction_time,2) + + + return predicted_pos +end + +------------------------------------------------------------------------------- +-- name: predict_next_block(pos,velocity,acceleration) +-- +--! @brief predict next block based on pos velocity and acceleration +-- +--! @param pos current position +--! @param velocity current velocity +--! @param acceleration current acceleration +--! @return { x,y,z } position of next block +------------------------------------------------------------------------------- +function movement_generic.predict_next_block(pos,velocity,acceleration) + + local prediction_time = 2 + + local pos_predicted = movement_generic.calc_new_pos(pos, + acceleration, + prediction_time, + velocity + ) + local count = 1 + + --check if after prediction time we would have traveled more than one block and adjust to not predict to far + while mobf_calc_distance(pos,pos_predicted) > 1 do + + pos_predicted = movement_generic.calc_new_pos(pos, + acceleration, + prediction_time - (count*0.1), + velocity + ) + + if (prediction_time - (count*0.1)) < 0 then + minetest.log(LOGLEVEL_WARNING,"MOBF: Bug!!!! didn't find a suitable prediction time. Mob will move more than one block within prediction period") + break + end + + count = count +1 + end + + return pos_predicted +end \ No newline at end of file diff --git a/mods/mobf/permanent_data.lua b/mods/mobf/permanent_data.lua new file mode 100644 index 0000000..19d4765 --- /dev/null +++ b/mods/mobf/permanent_data.lua @@ -0,0 +1,195 @@ +------------------------------------------------------------------------------- +-- 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 permanent_data.lua +--! @brief functions for storing required data permanently +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +--! +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +--! @defgroup perm_data Permanent data handling +--! @brief functions to deal with permanent data +--! @ingroup framework_int +--! @{ + +------------------------------------------------------------------------------- +-- name: mobf_deserialize_permanent_entity_data(datastring) +-- +--! @brief parse datastring and return table of data +-- +--! @param staticdata string to deserialize +--! @return table containing data +------------------------------------------------------------------------------- + +function mobf_deserialize_permanent_entity_data(staticdata) + +-- local deserialized = minetest.deserialize(staticdata) + +-- if deserialized ~= nil and +-- deserialized.version ~= nil then +-- --print("DEBUG: deserialized -> " ..dump(deserialized)) +-- return deserialized +-- end + + --old style serialized static data + local retval = {spawnpoint={x=0,y=0,z=0},playerspawned=false,original_spawntime=-1,state="default"} + + if staticdata ~= nil and + staticdata ~= "" then + local start_pos = 1 + local end_pos = string.find(staticdata,";") + + if end_pos ~= nil then + dbg_mobf.permanent_store_lvl1("MOBF: Found: ".. string.sub(staticdata,start_pos,end_pos-1).. " as first element") + if string.sub(staticdata,start_pos,end_pos-1) == "true" then + retval.playerspawned = true + end + else + return retval + end + + start_pos = end_pos +1 + end_pos = string.find(staticdata,";",start_pos) + + if end_pos ~= nil then + dbg_mobf.permanent_store_lvl1("MOBF: Found: ".. string.sub(staticdata,start_pos,end_pos-1).. " as second element") + retval.spawnpoint.x = tonumber(string.sub(staticdata,start_pos,end_pos-1)) + else + return retval + end + + start_pos = end_pos +1 + end_pos = string.find(staticdata,";",start_pos) + + if end_pos ~= nil then + dbg_mobf.permanent_store_lvl1("MOBF: Found: ".. string.sub(staticdata,start_pos,end_pos-1).. " as third element") + retval.spawnpoint.y = tonumber(string.sub(staticdata,start_pos,end_pos-1)) + else + return retval + end + + start_pos = end_pos +1 + end_pos = string.find(staticdata,";",start_pos) + + if end_pos ~= nil then + dbg_mobf.permanent_store_lvl1("MOBF: Found: ".. string.sub(staticdata,start_pos,end_pos-1).. " as fourth element") + retval.spawnpoint.z = tonumber(string.sub(staticdata,start_pos,end_pos-1)) + else + return retval + end + + start_pos = end_pos +1 + end_pos = string.find(staticdata,";",start_pos) + + if end_pos ~= nil then + dbg_mobf.permanent_store_lvl1("MOBF: Found: ".. string.sub(staticdata,start_pos,end_pos-1).. " as fivth element") + retval.original_spawntime = tonumber(string.sub(staticdata,start_pos,end_pos-1)) + else + return retval + end + + start_pos = end_pos +1 + end_pos = string.find(staticdata,";",start_pos) + + if end_pos ~= nil then + dbg_mobf.permanent_store_lvl1("MOBF: Found: ".. string.sub(staticdata,start_pos,end_pos-1).. " as sixth element") + retval.spawner = string.sub(staticdata,start_pos,end_pos-1) + if retval.spawner == "" then + retval.spawner = nil + end + else + return retval + end + + start_pos = end_pos +1 + end_pos = string.find(staticdata,";",start_pos) + + if end_pos ~= nil then + dbg_mobf.permanent_store_lvl1("MOBF: Found: ".. string.sub(staticdata,start_pos,end_pos-1).. " as sevemth element") + retval.state = string.sub(staticdata,start_pos,end_pos-1) + if retval.state == "" then + retval.state = nil + end + else + return retval + end + end + + return retval + +end + +------------------------------------------------------------------------------- +-- name: mobf_serialize_permanent_entity_data(entity) +-- +--! @brief return string containing all entity data to be preserved +-- +--! @param entity to get data from +--! @return string containing entitys permanent data +------------------------------------------------------------------------------- +function mobf_serialize_permanent_entity_data(entity) + if entity.removed == false and + entity.dynamic_data ~= nil and + entity.dynamic_data.spawning ~= nil then + + local playerspawned = "false" + + if entity.dynamic_data.spawning.player_spawned then + playerspawned = "true" + end + + local spawner = "" + + if entity.dynamic_data.spawning.spawner ~= nil then + spawner = entity.dynamic_data.spawning.spawner + end + + local state = "default" + if entity.dynamic_data.state ~= nil and + entity.dynamic_data.state.current ~= nil then + state = entity.dynamic_data.state.current + end + + if entity.dynamic_data.spawning.original_spawntime == nil then + entity.dynamic_data.spawning.original_spawntime = mobf_get_current_time() + minetest.log(LOGLEVEL_WARNING, + "MOBF: deactivating entity without spawntime setting current time") + end + + local serialized = playerspawned .. + ";" ..entity.dynamic_data.spawning.spawnpoint.x .. + ";" ..entity.dynamic_data.spawning.spawnpoint.y .. + ";" ..entity.dynamic_data.spawning.spawnpoint.z .. + ";" ..entity.dynamic_data.spawning.original_spawntime .. + ";" ..spawner .. + ";" ..state .. + ";" + + + --local toserialize = { + -- spawnpoint = entity.dynamic_data.spawning.spawnpoint, + -- playerspawned = entity.dynamic_data.spawning.player_spawned, + -- original_spawntime = entity.dynamic_data.spawning.original_spawntime, + -- spawner = entity.dynamic_data.spawning.spawner, + -- version = 2 + -- } + --local serialized = minetest.serialize(toserialize) + --print("DEBUG: serialized -> " .. serialized) + return serialized + else + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: >" .. dump(entity.data.name) .. + "< removed=" ..dump(entity.removed) .. " entity=" .. tostring(entity) .. + " No spawning information available on saving mob or mob already deleted") + end + + return "" +end + +--!@} diff --git a/mods/mobf/random_drop.lua b/mods/mobf/random_drop.lua new file mode 100644 index 0000000..268bbb4 --- /dev/null +++ b/mods/mobf/random_drop.lua @@ -0,0 +1,190 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file random_drop.lua +--! @brief component containing random drop features +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @defgroup randdrop Random Drop subcomponent +--! @brief Component handling all random drops +--! @ingroup framework_int +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +--! @class random_drop +--! @brief random drop features e.g lay eggs +random_drop = {} + +--!@} + +------------------------------------------------------------------------------- +-- name: callback(entity) +-- +--! @brief random drop periodic callback +--! @memberof random_drop +-- +--! @param entity mob calling it +--! @param now current time +------------------------------------------------------------------------------- +function random_drop.callback(entity,now) + if entity.data.random_drop ~= nil and + entity.dynamic_data.random_drop ~= nil and + entity.data.random_drop.result ~= "" then + + dbg_mobf.random_drop_lvl3("MOBF: random drop for ".. entity.data.name .." is set") + + if entity.dynamic_data.random_drop.ts_last_drop + entity.data.random_drop.min_delay < now then + + dbg_mobf.random_drop_lvl3("MOBF: enough time passed give drop a chance") + if math.random() < entity.data.random_drop.chance then + + entity.dynamic_data.random_drop.ts_last_drop = now + + local entitybasepos = entity.getbasepos(entity) + + --find pos around + local toput = environment.get_suitable_pos_same_level(entitybasepos,1,entity) + + if toput ~= nil then + minetest.env:add_entity(toput,entity.data.random_drop.result.."_ent") + dbg_mobf.random_drop_lvl3("MOBF: adding random drop for "..entity.data.name .. ": "..entity.data.random_drop.result.."_ent" .. " at " .. printpos(toput)) + if entity.data.sound ~= nil then + sound.play(entitybasepos,entity.data.sound.random_drop) + end + else + dbg_mobf.random_drop_lvl2("MOBF: didn't find a place to put random drop for ".. entity.data.name) + end + end + + end + end +end + +------------------------------------------------------------------------------- +-- name: register_random_drop(random_drop) +-- +--! @brief register random drop item and entity +--! @memberof random_drop +-- +--! @param random_drop configuration for random drop feature +------------------------------------------------------------------------------- +function random_drop.register(random_drop) + + --get basename from random drop item name + local start_pos = 1 + local end_pos = string.find(random_drop.result,":") + + if end_pos == nil then + return + end + + local drop_basename = string.sub(random_drop.result,start_pos,end_pos-1) + local drop_itemname = string.sub(random_drop.result,end_pos+1) + + + if drop_itemname == nil or + drop_basename == nil then + return + end + + minetest.log("LOGLEVEL_INFO","MOBF:\tregistering random drop entity: "..":"..random_drop.result.."_ent".. + " item="..drop_itemname .. " basename=" .. drop_basename) + + local ent_graphics = {} + local id = drop_basename .. "_" .. drop_itemname + + if minetest.setting_getbool("mobf_disable_3d_mode") or + animalmaterialsdata[id] == nil or + animalmaterialsdata[id].graphics_3d == nil then + ent_graphics.visual = "sprite" + ent_graphics.textures = {drop_basename .. "_"..drop_itemname..".png"} + ent_graphics.collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5} + else + ent_graphics.visual = animalmaterialsdata[id].graphics_3d.visual + ent_graphics.mesh = animalmaterialsdata[id].graphics_3d.mesh + ent_graphics.textures = animalmaterialsdata[id].graphics_3d.textures + ent_graphics.collisionbox = animalmaterialsdata[id].graphics_3d.collisionbox + ent_graphics.visual_size = animalmaterialsdata[id].graphics_3d.visual_size + end + + + --Entity + minetest.register_entity(":"..random_drop.result.."_ent", + { + physical = true, + collisionbox = ent_graphics.collisionbox, + visual = ent_graphics.visual, + textures = ent_graphics.textures, + mesh = ent_graphics.mesh, + visual_size = ent_graphics.visual_size, + + on_activate = function(self,staticdata) + + self.object:setacceleration({x=0,y=-9.81,z=0}) + local now = mobf_get_current_time() + + if staticdata == "" then + self.dropped = now + else + + self.dropped = tonumber(staticdata) + end + + if self.dropped + self.random_drop_max_life < now then + dbg_mobf.random_drop_lvl2("MOBF: random drop entity timed out") + self.object:remove() + end + end, + + on_punch = function(self, hitter) + hitter:get_inventory():add_item("main", random_drop.result.." 1") + self.object:remove() + end, + + on_step = function(self,dtime) + if self.dropped + self.random_drop_max_life < mobf_get_current_time() then + dbg_mobf.random_drop_lvl2("MOBF: random drop entity timed out") + self.object:remove() + end + + end, + + + get_staticdata = function(self) + return self.dropped + end, + + random_drop_max_life = random_drop.min_delay/4, + dropped = 0, + + }) +end + +------------------------------------------------------------------------------- +-- name: init_dynamic_data(entity,now) +-- +--! @brief initialize dynamic data required by random drop +--! @memberof random_drop +-- +--! @param entity mob to add data +--! @param now current time +------------------------------------------------------------------------------- +function random_drop.init_dynamic_data(entity,now) + if entity.data.random_drop ~= nil and + entity.data.random_drop.min_delay > 5 then + entity.dynamic_data.random_drop = { + ts_last_drop = now + math.random(5,entity.data.random_drop.min_delay) + } + else + entity.dynamic_data.random_drop = { + ts_last_drop = now + } + end +end diff --git a/mods/mobf/removesvnfolders.sh b/mods/mobf/removesvnfolders.sh new file mode 100755 index 0000000..35af2d6 --- /dev/null +++ b/mods/mobf/removesvnfolders.sh @@ -0,0 +1,2 @@ +#cleanup via find +# find . -name .svn -exec rm -r {} \; \ No newline at end of file diff --git a/mods/mobf/ride.lua b/mods/mobf/ride.lua new file mode 100644 index 0000000..08ad6e3 --- /dev/null +++ b/mods/mobf/ride.lua @@ -0,0 +1,297 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file ride.lua +--! @brief class containing mobf functions for riding +--! @copyright Sapier +--! @author Sapier +--! @date 2013-01-06 +-- +-- +--! @defgroup mobf_ride functions required for riding mobs +--! @brief a component containing all functions required to ride a mob +--! @ingroup framework_int +--! @{ +-- +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +mobf_ride = {} + +------------------------------------------------------------------------------ +-- name: attache_player(entity,player) +-- +--! @brief make a player ride this mob +--! @ingroup mobf +-- +--! @param entity entity to be ridden +--! @param player player riding +------------------------------------------------------------------------------- +function mobf_ride.attache_player(entity,player) + + entity.dynamic_data.ride.is_attached = true + entity.dynamic_data.ride.player = player + entity.object:setacceleration({x=0,y=-9.81,z=0}) + entity.object:setvelocity({x=0,y=-9.81,z=0}) + + local attacheoffset = {x=0,y=0.5,z=0} + + if entity.data.ride ~= nil and + entity.data.ride.attacheoffset ~= nil then + attacheoffset = entity.data.ride.attacheoffset + end + + player:set_attach(entity.object,"",attacheoffset, {x=0,y=0,z=0}) + if entity.data.ride.texturemod ~= nil then + entity.object:settexturemod(entity.data.ride.texturemod); + end +end + +------------------------------------------------------------------------------ +-- name: dettache_player(entity,player) +-- +--! @brief make a player ride this mob +--! @ingroup mobf +-- +--! @param entity entity to be ridden +------------------------------------------------------------------------------- +function mobf_ride.dettache_player(entity) + + entity.dynamic_data.ride.is_attached = false + entity.dynamic_data.ride.player:set_detach() + entity.dynamic_data.ride.player = nil + entity.object:settexturemod(""); +end + + +------------------------------------------------------------------------------ +-- name: on_step_callback(entity) +-- +--! @brief make a player ride this mob +--! @ingroup mobf +-- +--! @param entity entity to be ridden +------------------------------------------------------------------------------- +function mobf_ride.on_step_callback(entity) + + if entity.dynamic_data.ride.is_attached then + dbg_mobf.ride_lvl3("MOBF: have attached player") + local walkspeed = 3 + local sneakspeed = 0.5 + local jumpspeed = 30 + + if entity.data.ride ~= nil then + if entity.data.ride.walkspeed ~= nil then + walkspeed = entity.data.ride.walkspeed + end + + if entity.data.ride.runspeed ~= nil then + runspeed = entity.data.ride.runspeed + end + + if entity.data.ride.sneakspeed ~= nil then + sneakspeed = entity.data.ride.sneakspeed + end + + if entity.data.ride.jumpspeed ~= nil then + jumpspeed = entity.data.ride.jumpspeed + end + end + + local dir = entity.dynamic_data.ride.player:get_look_yaw() + local current_speed = entity.object:getacceleration() + + local speed_to_set = {x=0,y=current_speed.y,z=0} + if dir ~= nil then + local playerctrl = entity.dynamic_data.ride.player:get_player_control() + + if playerctrl ~= nil then + + local setspeed = false + + if playerctrl.jump and + entity.is_on_ground(entity) then + speed_to_set.y = jumpspeed + setspeed = true + end + + --just set speed to playerview direction + if playerctrl.up then + setspeed = true + end + + --invert playerview direction + if playerctrl.down then + dir = dir + math.pi + setspeed = true + end + + if playerctrl.left then + if playerctrl.up then + dir = dir + math.pi/4 + elseif playerctrl.down then + dir = dir - math.pi/4 + else + dir = dir + math.pi/2 + end + setspeed = true + end + + if playerctrl.right then + if playerctrl.up then + dir = dir - math.pi/4 + elseif playerctrl.down then + dir = dir + math.pi/4 + else + dir = dir - math.pi/2 + end + setspeed = true + end + + local selected_speed = walkspeed + + if playerctrl.sneak then + selected_speed = sneakspeed + end + + + + if setspeed then + speed_to_set_xz = mobf_calc_vector_components(dir,selected_speed) + + speed_to_set.x = speed_to_set_xz.x + speed_to_set.z = speed_to_set_xz.z + + if entity.data.ride.walk_anim ~= nil then + graphics.set_animation(entity,entity.data.ride.walk_anim) + end + else + if entity.data.ride.walk_anim ~= nil then + if entity.data.ride.stand_anim ~= nil then + graphics.set_animation(entity,entity.data.ride.stand_anim) + mob_state.change_state(entity,mob_state.get_state_by_name(entity,entity.data.ride.state_stand)) + else + graphics.set_animation(entity,"stand") + end + end + end + + entity.object:setvelocity(speed_to_set) + + --fix switched model orientation + entity.object:setyaw(dir) + end + + + end + return true + else + return false + end +end + +------------------------------------------------------------------------------ +-- name: on_punch_callback(entity,player) +-- +--! @brief make a player ride this mob +--! @ingroup mobf +-- +--! @param entity entity to be ridden +--! @param player player riding +------------------------------------------------------------------------------- +function mobf_ride.on_punch_callback(entity,player) + dbg_mobf.ride_lvl2("MOBF: ride on punch callback") + print("MOBF: ride on punch callback") + --detache + if entity.dynamic_data.ride.is_attached ~= false then + dbg_mobf.ride_lvl2("MOBF: punched ridden mob") + if entity.dynamic_data.ride.player == player then + dbg_mobf.ride_lvl2("MOBF: detaching player") + mobf_ride.dettache_player(entity) + player:get_inventory():add_item("main","animalmaterials:saddle 1") + return true + end + else + --check if player has saddle + dbg_mobf.ride_lvl2("MOBF: punched free mob") + if player:get_wielded_item():get_name() == "animalmaterials:saddle" then + dbg_mobf.ride_lvl2("MOBF: punching with saddle") + if player:get_inventory():contains_item("main","animalmaterials:saddle 1") then + dbg_mobf.ride_lvl2("MOBF: have saddle") + mobf_ride.attache_player(entity,player) + player:get_inventory():remove_item("main","animalmaterials:saddle 1") + return true + end + else + dbg_mobf.ride_lvl2("MOBF: not punching with saddle but: " .. player:get_wielded_item():get_name()) + end + end + + return false +end + +------------------------------------------------------------------------------ +-- name: is_enabled(entity) +-- +--! @brief check if riding is enabled for a mob +--! @ingroup mobf +-- +--! @param entity entity to be ridden +------------------------------------------------------------------------------- +function mobf_ride.is_enabled(entity) + if entity.data.ride ~= nil then + return true + end + dbg_mobf.ride_lvl2("riding of " .. entity.data.name .. " is disabled") + return false +end + +------------------------------------------------------------------------------ +-- name: attache_player(entity,player) +-- +--! @brief make a player ride this mob +--! @ingroup mobf +-- +--! @param entity entity to be ridden +------------------------------------------------------------------------------- +function mobf_ride.init(entity) + local data = { + is_attached = false, + player = nil, + } + + entity.dynamic_data.ride = data +end + +minetest.register_on_leaveplayer( function(player) + if player ~= nil and + player.object ~= nil then + local pos = player.object:getpos() + + --print("MOBF: got player position: " ..printpos(pos) ) + + if pos ~= nil then + local objects = get_objects_inside_radius(pos, 5) + + print("MOBF: found " .. dump(#objects) .. " objects around player") + + if objects ~= nil then + for i=1,#objects,1 do + local entity = objects[i]:get_luaentity() + if entity ~= nil and + entity.dynamic_data ~= nil and + entity.dynamic_data.ride ~= nil and + entity.dynamic_data.ride.player == player then + print("MOBF: found player to be attached") + ride.dettache_player(entity) + break + end + end + end + end + end +end) \ No newline at end of file diff --git a/mods/mobf/sound.lua b/mods/mobf/sound.lua new file mode 100644 index 0000000..4010775 --- /dev/null +++ b/mods/mobf/sound.lua @@ -0,0 +1,103 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file sound.lua +--! @brief component containing sound related functions +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @defgroup grp_sound Sound subcomponent +--! @brief Component handling all sound related actions +--! @ingroup framework_int +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +--! @class sound +sound = {} + +--!@} + +------------------------------------------------------------------------------- +-- name: play(entity) +-- +--! @brief play a sound at a specified position +--! @memberof sound +-- +--! @param pos position to play sound at +--! @param soundspec sound to play +------------------------------------------------------------------------------- +function sound.play(pos,soundspec) + + if (soundspec ~= nil) then + + local toplay = { + gain = soundspec.gain, + pos = pos, + max_hear_distance = soundspec.max_hear_distance, + loop = false, + } + + minetest.sound_play(soundspec.name,toplay) + else + dbg_mobf.sound_lvl2("MOBF: no soundspec") + --todo add log entry + end +end + + +------------------------------------------------------------------------------- +-- name: play_random(entity,now) +-- +--! @brief play a random sound for mob +--! @memberof sound +-- +--! @param entity mob to do action +--! @param now current time +------------------------------------------------------------------------------- +function sound.play_random(entity,now) + + if entity.dynamic_data == nil or + entity.dynamic_data.sound == nil then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!!: >" ..entity.data.name .. "< removed=" .. dump(entity.removed) .. " entity=" .. tostring(entity) .. " sound callback without dynamic data") + return + end + + if entity.data.sound ~= nil and + entity.data.sound.random ~= nil then + + if (entity.dynamic_data.sound.random_last + entity.data.sound.random.min_delta < now) then + + if math.random() < entity.data.sound.random.chance then + sound.play(entity.object:getpos(),entity.data.sound.random) + entity.dynamic_data.sound.random_last = now + dbg_mobf.sound_lvl1("MOBF: playing sound") + else + dbg_mobf.sound_lvl1("MOBF: not playing sound") + end + end + end + +end + +------------------------------------------------------------------------------- +-- name: sound.init_dynamic_data(entity) +-- +--! @brief initialize all dynamic data for sound on activate +--! @memberof sound +-- +--! @param entity mob to do action +--! @param now current time +------------------------------------------------------------------------------- +function sound.init_dynamic_data(entity,now) + local data = { + random_last = now, + } + + entity.dynamic_data.sound = data +end \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/at_night.lua b/mods/mobf/spawn_algorithms/at_night.lua new file mode 100644 index 0000000..ee117c2 --- /dev/null +++ b/mods/mobf/spawn_algorithms/at_night.lua @@ -0,0 +1,227 @@ +------------------------------------------------------------------------------- +-- 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 at_night.lua +--! @brief component containing spawning features +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +at_night_surfaces = { "default:stone","default:dirt_with_grass","default:dirt","default:desert_stone","default:desert_sand" } + +------------------------------------------------------------------------------- +-- name: mobf_spawn_at_night(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief spawn only at night +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_at_night(mob_name,mob_transform,spawning_data,environment) + + print("\tregistering night spawn abm callback for mob "..mob_name) + + local media = nil + + if environment ~= nil and + environment.media ~= nil then + media = environment.media + end + + minetest.register_abm({ + nodenames = at_night_surfaces, + neighbors = media, + interval = 20, + chance = math.floor(1/spawning_data.rate), + action = function(pos, node, active_object_count, active_object_count_wider) + + local gametime = minetest.env:get_timeofday() + + if gametime > 0.25 and + gametime < 0.75 then + return + end + + local starttime = mobf_get_time_ms() + local pos_above = { + x = pos.x, + y = pos.y + 1, + z = pos.z + } + + --never try to spawn an mob at pos (0,0,0) it's initial entity spawnpos and + --used to find bugs in initial spawnpoint setting code + if mobf_pos_is_zero(pos) then + mobf_warn_long_fct(starttime,"mobf_spawn_at_night") + return + end + + --check if there s enough space above to place mob + if mobf_air_above(pos,spawning_data.height) ~= true then + mobf_warn_long_fct(starttime,"mobf_spawn_at_night") + return + end + + local gametime = minetest.env:get_timeofday() + + if gametime > 0.25 and + gametime < 0.75 then + return + end + + + local node_above = minetest.env:get_node(pos_above) + + + if mob_name == nil then + minetest.log(LOGLEVEL_ERROR, "MOBF: Bug!!! mob name not available") + else + --print("Find mobs of same type around:"..mob_name.. " pop dens: ".. population_density) + if mobf_mob_around(mob_name,mob_transform,pos,spawning_data.density,true) == 0 then + if minetest.env:get_node_light(pos_above,0.5) == LIGHT_MAX +1 and + minetest.env:get_node_light(pos_above,0.0) < 7 and + minetest.env:get_node_light(pos_above) < 6 then + + local newobject = minetest.env:add_entity(pos_above,mob_name .. "__default") + + local newentity = mobf_find_entity(newobject) + + if newentity == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: Bug!!! no "..mob_name.." has been created!") + end + + minetest.log(LOGLEVEL_INFO,"MOBF Spawning "..mob_name.." at night at position "..printpos(pos)) + end + end + end + mobf_warn_long_fct(starttime,"mobf_spawn_at_night") + end, + }) +end + +------------------------------------------------------------------------------- +-- name: mobf_spawn_at_night_entity(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place on surface to spawn at night +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_at_night_entity(mob_name,mob_transform,spawning_data,environment) + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering at night mapgen spawn mapgen callback for mob "..mob_name) + + spawning.register_spawner_entity(mob_name,mob_transform,spawning_data,environment, + function(self) + local gametime = minetest.env:get_timeofday() + + if gametime > 0.25 and + gametime < 0.75 then + return + end + + local pos = self.object:getpos() + local good = true + + dbg_mobf.spawning_lvl3("MOBF: " .. dump(self.spawner_mob_env)) + + --check if own position is good + local pos_below = {x=pos.x,y=pos.y-1,z=pos.z} + local node_below = minetest.env:get_node(pos_below) + + + if not mobf_contains(at_night_surfaces,node_below.name) then + good = false + end + + --check if there s enough space above to place mob + if mobf_air_above(pos_below,self.spawner_mob_spawndata.height) ~= true then + good = false + end + + --check if area is in day/night cycle + if minetest.env:get_node_light(pos,0.5) ~= LIGHT_MAX +1 or + minetest.env:get_node_light(pos,0.0) > 7 then + good = false + end + + if not good then + dbg_mobf.spawning_lvl2("MOBF: not spawning spawner for " .. self.spawner_mob_name .. " somehow got to bad place") + --TODO try to move spawner to better place + + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + return + end + + + --check if current light is dark enough + + if minetest.env:get_node_light(pos) > 6 then + return + end + + dbg_mobf.spawning_lvl3("MOBF: at_night checking how many mobs around: " .. dump(self.spawner_mob_name)) + if mobf_mob_around(self.spawner_mob_name, + self.spawner_mob_transform, + pos, + self.spawner_mob_spawndata.density,true) < 2 then + + spawning.spawn_and_check(self.spawner_mob_name,"__default",pos,"at_night_spawner_ent") + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + else + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + dbg_mobf.spawning_lvl2("MOBF: not spawning " .. self.spawner_mob_name .. " there's a mob around") + end + end) + + --add mob spawner on map generation + minetest.register_on_generated(function(minp, maxp, seed) + + spawning.divide_mapgen_entity(minp,maxp,spawning_data,mob_name, + function(name,pos,min_y,max_y) + dbg_mobf.spawning_lvl3("MOBF: trying to create a spawner for " .. name .. " at " ..printpos(pos)) + local surface = mobf_get_surface(pos.x,pos.z,min_y,max_y) + + if surface then + pos.y= surface -1 + + local node = minetest.env:get_node(pos) + + if not mobf_contains(at_night_surfaces,node.name) then + dbg_mobf.spawning_lvl3("MOBF: node ain't of correct type: " .. node.name) + return false + end + + local pos_above = {x=pos.x,y=pos.y+1,z=pos.z} + local node_above = minetest.env:get_node(pos_above) + if not mobf_contains({"air"},node_above.name) then + dbg_mobf.spawning_lvl3("MOBF: node above ain't air but: " .. node_above.name) + return + end + + spawning.spawn_and_check(name,"_spawner",pos_above,"at_night_spawner") + return true + else + dbg_mobf.spawning_lvl3("MOBF: didn't find surface for " .. name .. " spawner at " ..printpos(pos)) + end + return false + end) + end) --register mapgen + end --end spawn algo + +--!@} + +spawning.register_spawn_algorithm("at_night", mobf_spawn_at_night) +spawning.register_spawn_algorithm("at_night_spawner", mobf_spawn_at_night_entity,spawning.register_cleanup_spawner) \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/big_willow.lua b/mods/mobf/spawn_algorithms/big_willow.lua new file mode 100644 index 0000000..bd5c07c --- /dev/null +++ b/mods/mobf/spawn_algorithms/big_willow.lua @@ -0,0 +1,155 @@ +------------------------------------------------------------------------------- +-- 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 willow.lua +--! @brief spawn algorithm willow +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- name: mobf_spawn_on_big_willow(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place on big willow to spawn a mob +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_on_big_willow(mob_name,mob_transform,spawning_data,environment) + minetest.log(LOGLEVEL_WARNING,"MOBF: using deprecated abm based spawn algorithm \"spawn_on_willow\" most likely causing lag in server!\t Use spawn_on_willow_mapgen instead!") + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering willow spawn abm callback for mob "..mob_name) + + local media = nil + + if environment ~= nil and + environment.media ~= nil then + media = environment.media + end + + minetest.register_abm({ + nodenames = { "default:dirt_with_grass" }, + neighbors = media, + interval = 7200, + chance = math.floor(1/spawning_data.rate), + action = function(pos, node, active_object_count, active_object_count_wider) + local starttime = mobf_get_time_ms() + local pos_above = { + x = pos.x, + y = pos.y + 1, + z = pos.z + } + + --never try to spawn an mob at pos (0,0,0) it's initial entity spawnpos and + --used to find bugs in initial spawnpoint setting code + if mobf_pos_is_zero(pos) then + mobf_warn_long_fct(starttime,"mobf_spawn_on_willow") + return + end + + --check if there s enough space above to place mob + if mobf_air_above(pos,spawning_data.height) ~= true then + mobf_warn_long_fct(starttime,"mobf_spawn_on_willow") + return + end + + if mob_name == nil then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!! mob name not available") + else + if mobf_mob_around(mob_name,mob_transform,pos,spawning_data.density,true) == 0 then + local pos_is_big_willow = true + + for x=pos.x-2,pos.x+2,1 do + for z=pos.z-2,pos.z+2,1 do + local node_to_check = minetest.env:getnode({x=x,y=pos.y,z=z}) + + if node_to_check == nil or + node_to_check.name ~= "default:dirt_with_grass" then + break + end + + --check if there s enough space above to place mob + if not mobf_air_above({x=x,y=pos.y,z=z},spawning_data.height) then + pos_is_big_willow = false + break + end + end + end + + if pos_is_big_willow then + dbg_mobf.spawning_lvl3("willow is big enough " ..printpos(centerpos)) + local spawnpos = {x=pos.x,y=pos.y+1,z=pos.z} + spawning.spawn_and_check(name,"__default",spawnpos,"on_big_willow_mapgen") + return true + end + end + end + mobf_warn_long_fct(starttime,"mobf_spawn_on_willow") + end, + }) +end + +------------------------------------------------------------------------------- +-- name: mobf_spawn_on_big_willow_mapgen(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place on big willow to spawn a mob on map generation +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_on_big_willow_mapgen(mob_name,mob_transform,spawning_data,environment) + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering willow mapgen spawn mapgen callback for mob "..mob_name) + + --add mob on map generation + minetest.register_on_generated(function(minp, maxp, seed) + spawning.divide_mapgen(minp,maxp,spawning_data.density,mob_name,mob_transform, + + function(name,pos,min_y,max_y) + local pos_is_big_willow = true + + for x=pos.x-2,pos.x+2,1 do + for z=pos.z-2,pos.z+2,1 do + local node_to_check = minetest.env:get_node({x=x,y=pos.y,z=z}) + if node_to_check == nil or + node_to_check.name ~= "default:dirt_with_grass" then + pos_is_big_willow = false + break + end + + --check if there s enough space above to place mob + if not mobf_air_above({x=x,y=pos.y,z=z},spawning_data.height) then + pos_is_big_willow = false + break + end + end + end + + if pos_is_big_willow then + dbg_mobf.spawning_lvl3("willow is big enough " ..printpos(centerpos)) + local spawnpos = {x=pos.x,y=pos.y+1,z=pos.z} + spawning.spawn_and_check(name,"__default",spawnpos,"on_big_willow_mapgen") + return true + end + + return false + end, + mobf_get_sunlight_surface, + 20) + end) + end --end spawn algo +--!@} + +spawning.register_spawn_algorithm("big_willow", mobf_spawn_on_big_willow) +spawning.register_spawn_algorithm("big_willow_mapgen", mobf_spawn_on_big_willow_mapgen) \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/deep_large_caves.lua b/mods/mobf/spawn_algorithms/deep_large_caves.lua new file mode 100644 index 0000000..94e8daa --- /dev/null +++ b/mods/mobf/spawn_algorithms/deep_large_caves.lua @@ -0,0 +1,196 @@ +------------------------------------------------------------------------------- +-- 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 deep_large_caves.lua +--! @brief spawn algorithm to spawn in deep large caves +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_deep_large_caves(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place with low light to spawn an mob +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- + +function mobf_spawn_in_deep_large_caves(mob_name,mob_transform,spawning_data,environment) + + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering shadow spawn abm callback for mob "..mob_name) + + local media = nil + + if environment ~= nil and + environment.media ~= nil then + media = environment.media + end + + minetest.register_abm({ + nodenames = { "default:stone" }, + neighbors = media, + interval = 60, + chance = math.floor(1/spawning_data.rate), + action = function(pos, node, active_object_count, active_object_count_wider) + local starttime = mobf_get_time_ms() + local pos_above = { + x = pos.x, + y = pos.y + 1, + z = pos.z + } + + if pos.y > spawning_data.min_depth then + dbg_mobf.spawning_lvl3("MOBF: min depth requirement not met") + mobf_warn_long_fct(starttime,"mobf_spawn_in_deep_large_caves") + return + end + + --never try to spawn an mob at pos (0,0,0) it's initial entity spawnpos and + --used to find bugs in initial spawnpoint setting code + if mobf_pos_is_zero(pos) then + dbg_mobf.spawning_lvl1("MOBF: not spawning due to 0 pos") + mobf_warn_long_fct(starttime,"mobf_spawn_in_deep_large_caves") + return + end + + --check if there s enough space above to place mob + if mobf_air_above(pos,spawning_data.height) ~= true then + dbg_mobf.spawning_lvl3("MOBF: height requirement not met") + mobf_warn_long_fct(starttime,"mobf_spawn_in_deep_large_caves") + return + end + + local node_above = minetest.env:get_node(pos_above) + + if mob_name == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: Bug!!! mob name not available") + else + dbg_mobf.spawning_lvl3("MOBF: trying to spawn " .. mob_name) + if mobf_mob_around(mob_name,mob_transform,pos,spawning_data.density,true) == 0 then + local newobject = minetest.env:add_entity(pos_above,mob_name .. "__default") + + local newentity = mobf_find_entity(newobject) + + if newentity == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: unable to create mob " .. mob_name .. " at pos " .. printpos(pos)) + else + minetest.log(LOGLEVEL_INFO,"MOBF: Spawning "..mob_name.." in shadows at position "..printpos(pos)) + end + else + dbg_mobf.spawning_lvl3("MOBF: too many mobs next to " ..printpos(pos)) + end + end + mobf_warn_long_fct(starttime,"mobf_spawn_in_deep_large_caves") + end, + }) +end + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_deep_large_caves(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place in deep large caces to spawn +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_in_deep_large_caves_entity(mob_name,mob_transform,spawning_data,environment) + + spawning.register_spawner_entity(mob_name,mob_transform,spawning_data,environment, + function(self) + local pos = self.object:getpos() + local good = true + + dbg_mobf.spawning_lvl3("MOBF: " .. dump(self.spawner_mob_env)) + + --check if own position is good + local pos_below = {x=pos.x,y=pos.y-1,z=pos.z} + local node_below = minetest.env:get_node(pos_below) + + + if not mobf_contains({ "default:stone","default:gravel","default:dirt" },node_below.name) then + good = false + end + + --check if there s enough space above to place mob + if mobf_air_above(pos_below,self.spawner_mob_spawndata.height) ~= true then + good = false + end + + if not good then + dbg_mobf.spawning_lvl2("MOBF: not spawning spawner for " .. self.spawner_mob_name .. " somehow got to bad place") + --TODO try to move spawner to better place + + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + return + end + + if mobf_mob_around(self.spawner_mob_name, + self.spawner_mob_transform, + pos, + self.spawner_mob_spawndata.density,true) < 2 then + + spawning.spawn_and_check(self.spawner_mob_name,"__default",pos,"at_night_spawner_ent") + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + else + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + dbg_mobf.spawning_lvl2("MOBF: not spawning " .. self.spawner_mob_name .. " there's a mob around") + end + end) + + --add mob spawner on map generation + minetest.register_on_generated(function(minp, maxp, seed) + + spawning.divide_mapgen_entity(minp,maxp,spawning_data,mob_name, + function(name,pos,min_y,max_y,spawning_data) + + if max_y > spawning_data.min_depth then + return + end + + dbg_mobf.spawning_lvl3("MOBF: trying to create a spawner for " .. name .. " at " ..printpos(pos)) + local surface = mobf_get_surface(pos.x,pos.z,min_y,max_y) + + if surface then + pos.y= surface -1 + + local node = minetest.env:get_node(pos) + + if not mobf_contains({ "default:stone","default:gravel","default:dirt","default:sand" },node.name) then + dbg_mobf.spawning_lvl3("MOBF: node ain't of correct type: " .. node.name) + return false + end + + local pos_above = {x=pos.x,y=pos.y+1,z=pos.z} + local node_above = minetest.env:get_node(pos_above) + if not mobf_contains({"air"},node_above.name) then + dbg_mobf.spawning_lvl3("MOBF: node above ain't air but: " .. node_above.name) + return + end + + spawning.spawn_and_check(name,"_spawner",pos_above,"deep_large_caves_spawner") + return true + else + dbg_mobf.spawning_lvl3("MOBF: didn't find surface for " .. name .. " spawner at " ..printpos(pos)) + end + return false + end) + end) --register mapgen +end +--!@} + +spawning.register_spawn_algorithm("deep large caves", mobf_spawn_in_deep_large_caves) +spawning.register_spawn_algorithm("deep_large_caves_spawner", mobf_spawn_in_deep_large_caves_entity,spawning.register_cleanup_spawner) \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/forrest.lua b/mods/mobf/spawn_algorithms/forrest.lua new file mode 100644 index 0000000..e2fe4c4 --- /dev/null +++ b/mods/mobf/spawn_algorithms/forrest.lua @@ -0,0 +1,135 @@ +------------------------------------------------------------------------------- +-- 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 forrest.lua +--! @brief spawn algorithm for forrest spawning +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_forrest(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place in the forrest to spawn an mob +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_in_forrest(mob_name,mob_transform,spawning_data,environment) + minetest.log(LOGLEVEL_WARNING,"MOBF: using deprecated abm based spawn algorithm \"spawn_in_forrest\" most likely causing lag in server!\t Use spawn_in_forrest_mapgen instead!") + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering forrest spawn abm callback for mob "..mob_name) + + local media = nil + + if environment ~= nil and + environment.media ~= nil then + media = environment.media + end + + minetest.register_abm({ + nodenames = { "default:dirt_with_grass" }, + neighbors = media, + interval = 60, + chance = math.floor(1/spawning_data.rate), + action = function(pos, node, active_object_count, active_object_count_wider) + local starttime = mobf_get_time_ms() + local pos_above = { + x = pos.x, + y = pos.y + 1, + z = pos.z + } + + --never try to spawn an mob at pos (0,0,0) it's initial entity spawnpos and + --used to find bugs in initial spawnpoint setting code + if mobf_pos_is_zero(pos) then + mobf_warn_long_fct(starttime,"mobf_spawn_in_forrest") + return + end + + --check if there s enough space above to place mob + if mobf_air_above(pos,spawning_data.height) ~= true then + mobf_warn_long_fct(starttime,"mobf_spawn_in_forrest") + return + end + + + local node_above = minetest.env:get_node(pos_above) + + if mob_name == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF Bug!!!: mob name not available") + else + --print("Try to spawn mob: "..mob_name) + + if node_above.name == "air" then + --print("Find mobs of same type around:"..mob_name.. " pop dens: ".. population_density) + if mobf_mob_around(mob_name,mob_transform,pos,spawning_data.density,true) == 0 then + + if minetest.env:find_node_near(pos, 3, { "default:leaves", + "default:tree"} ) ~= nil then + + local newobject = minetest.env:add_entity(pos_above,mob_name .. "__default") + + local newentity = mobf_find_entity(newobject) + + if newentity == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF Bug!!!: no "..mob_name.." has been created!") + end + + minetest.log(LOGLEVEL_INFO,"MOBF: Spawning "..mob_name.." in forrest at position "..printpos(pos)) + end + end + end + end + mobf_warn_long_fct(starttime,"mobf_spawn_in_forrest") + end, + }) +end + +function mobf_spawn_in_forrest_mapgen(mob_name,mob_transform,spawning_data,environment) + + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering forrest spawn mapgen callback for mob "..mob_name) + + --add mob on map generation + minetest.register_on_generated(function(minp, maxp, seed) + spawning.divide_mapgen(minp,maxp,spawning_data.density,mob_name,mob_transform, + + function(name,pos,min_y,max_y) + + --check if there s enough space above to place mob + if mobf_air_above(pos,spawning_data.height) then + dbg_mobf.spawning_lvl3("enough air above " ..printpos(centerpos) + .. " minimum is: " .. spawning_data.height ) + + if minetest.env:find_node_near(pos, 3, { "default:leaves", + "default:tree"} ) ~= nil or + minetest.env:find_node_near(pos, 3, growing_trees_nodes ) ~= nil then + local spawnpos = {x=pos.x,y=pos.y+1,z=pos.z} + spawning.spawn_and_check(name,"__default",spawnpos,"in_forrest_mapgen") + return true + end -- forrest nodes + else + dbg_mobf.spawning_lvl3("not enough air above " ..printpos(centerpos) + .. " for mob ".. name .. " minimum is: " .. spawning_data.height ) + end -- air_above + return false + end, + mobf_get_sunlight_surface, + 5) + end) --register mapgen +end --end spawn algo + +--!@} + +spawning.register_spawn_algorithm("forrest_mapgen", mobf_spawn_in_forrest_mapgen) +spawning.register_spawn_algorithm("forrest", mobf_spawn_in_forrest) \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/in_air1.lua b/mods/mobf/spawn_algorithms/in_air1.lua new file mode 100644 index 0000000..9865794 --- /dev/null +++ b/mods/mobf/spawn_algorithms/in_air1.lua @@ -0,0 +1,177 @@ +------------------------------------------------------------------------------- +-- 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 in_air1.lua +--! @brief spawn algorithm for birds +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_air1(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place in sky to spawn mob +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- + +function mobf_spawn_in_air1(mob_name,mob_transform,spawning_data,environment) + + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering in air 1 spawn abm callback for mob "..mob_name) + + local media = nil + + if environment ~= nil and + environment.media ~= nil then + media = environment.media + end + + minetest.register_abm({ + nodenames = { "default:dirt", "default:dirt_with_grass" }, + neighbors = media, + interval = 60, + chance = math.floor(1/spawning_data.rate), + action = function(pos, node, active_object_count, active_object_count_wider) + local starttime = mobf_get_time_ms() + 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 + mobf_warn_long_fct(starttime,"mobf_spawn_in_air1") + return + end + + + local pos_spawn = { + x = pos.x, + y = pos.y + 10 + math.floor(math.random(0,10)), + z = pos.z + } + + local node_spawn = minetest.env:get_node(pos_spawn) + + + + if node_spawn.name ~= "air" then + mobf_warn_long_fct(starttime,"mobf_spawn_in_air1") + return + end + + if mob_name == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: Bug!!! mob name not available") + else + --print("Try to spawn mob: "..mob_name) + + if mobf_mob_around(mob_name,mob_transform,pos,spawning_data.density,true) == 0 then + + spawning.spawn_and_check(mob_name,"__default",pos_spawn,"in_air1") + end + end + mobf_warn_long_fct(starttime,"mobf_spawn_in_air1") + end, + }) +end + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_air1_spawner(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief a spawner based spawn spawn algorithm +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_in_air1_spawner(mob_name,mob_transform,spawning_data,environment) + + spawning.register_spawner_entity(mob_name,mob_transform,spawning_data,environment, + function(self) + local pos = self.object:getpos() + local good = true + + dbg_mobf.spawning_lvl3("MOBF: " .. dump(self.spawner_mob_env)) + + --check if own position is good + for x=pos.x-1,pos.x+1,1 do + for y=pos.y-1,pos.y+1,1 do + for z=pos.z-1,pos.z+1,1 do + + local node_to_check = minetest.env:get_node({x=x,y=y,z=z}) + + if node_to_check == nil then + good = false + else + dbg_mobf.spawning_lvl3("MOBF: checking " .. node_to_check.name) + if not mobf_contains(self.spawner_mob_env.media,node_to_check.name) then + good = false + end + end + end + end + end + + if not good then + dbg_mobf.spawning_lvl2("MOBF: not spawning, spawner for " .. self.spawner_mob_name .. " somehow got to bad place") + --TODO try to move spawner to better place + + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + return + end + + + if mobf_mob_around(self.spawner_mob_name, + self.spawner_mob_transform, + pos, + self.spawner_mob_spawndata.density,true) == 0 then + + spawning.spawn_and_check(self.spawner_mob_name,"__default",pos,"in_air1_spawner_ent") + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + else + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + dbg_mobf.spawning_lvl2("MOBF: not spawning " .. self.spawner_mob_name .. " there's a mob around") + end + end) + + --add mob spawner on map generation + minetest.register_on_generated(function(minp, maxp, seed) + + spawning.divide_mapgen_entity(minp,maxp,spawning_data,mob_name, + function(name,pos,min_y,max_y) + dbg_mobf.spawning_lvl3("MOBF: trying to create a spawner for " .. name .. " at " ..printpos(pos)) + local surface = mobf_get_surface(pos.x,pos.z,min_y,max_y) + + if surface then + pos.y=surface + 8 + math.random(0,5) + + if mobf_air_above(pos,10) then + spawning.spawn_and_check(name,"_spawner",pos,"in_air1_spawner") + return true + end + end + return false + end) + end) --register mapgen + +end + +--!@} + +spawning.register_spawn_algorithm("in_air1", mobf_spawn_in_air1) +spawning.register_spawn_algorithm("in_air1_spawner", mobf_spawn_in_air1_spawner,spawning.register_cleanup_spawner) \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/in_shallow_water.lua b/mods/mobf/spawn_algorithms/in_shallow_water.lua new file mode 100644 index 0000000..caa6f83 --- /dev/null +++ b/mods/mobf/spawn_algorithms/in_shallow_water.lua @@ -0,0 +1,203 @@ +------------------------------------------------------------------------------- +-- 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 in_shallow_water.lua +--! @brief spawn algorithm for shallow water spawning +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_shallow_water(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place in water to spawn mob +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- + +function mobf_spawn_in_shallow_water(mob_name,mob_transform,spawning_data,environment) + + minetest.log(LOGLEVEL_INFO,"MOBF: \tregistering shallow water spawn abm callback for mob "..mob_name) + + local media = nil + + if environment ~= nil and + environment.media ~= nil then + media = environment.media + end + + minetest.register_abm({ + nodenames = { "default:water_source" }, + neighbors = media, + interval = 60, + chance = math.floor(1/spawning_data.rate), + action = function(pos, node, active_object_count, active_object_count_wider) + local starttime = mobf_get_time_ms() + local pos_above = { + x = pos.x, + y = pos.y + 1, + z = pos.z + } + + --never try to spawn an mob at pos (0,0,0) it's initial entity spawnpos and + --used to find bugs in initial spawnpoint setting code + if mobf_pos_is_zero(pos) then + mobf_warn_long_fct(starttime,"mobf_spawn_in_shallow_water") + return + end + + --check if water is to deep + if mobf_air_distance(pos) < 10 then + mobf_warn_long_fct(starttime,"mobf_spawn_in_shallow_water") + return + end + + if mob_name == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: Bug!!! mob name not available") + else + --print("Try to spawn mob: "..mob_name) + + if mobf_mob_around(mob_name,mob_transform,pos,spawning_data.density,true) == 0 then + + if minetest.env:find_node_near(pos, 10, {"default:dirt", + "default:dirt_with_grass"}) ~= nil then + + local newobject = minetest.env:add_entity(pos,mob_name .. "__default") + + local newentity = mobf_find_entity(newobject) + + if newentity == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: Bug!!! no "..mob_name.." has been created!") + end + + minetest.log(LOGLEVEL_INFO,"MOBF: Spawning "..mob_name.." in shallow water "..printpos(pos)) + --else + --print(printpos(pos).." not next to ground") + end + end + end + mobf_warn_long_fct(starttime,"mobf_spawn_in_shallow_water") + end, + }) +end + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_shallow_water_entity(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place in shallow water +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_in_shallow_water_entity(mob_name,mob_transform,spawning_data,environment) + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering in shallow water mapgen spawn mapgen callback for mob "..mob_name) + + spawning.register_spawner_entity(mob_name,mob_transform,spawning_data,environment, + function(self) + + local pos = self.object:getpos() + local good = true + + dbg_mobf.spawning_lvl3("MOBF: " .. dump(self.spawner_mob_env)) + + --check if own position is good + local node_to_check = minetest.env:get_node(pos) + + if node_to_check ~= nil and + node_to_check.name ~= "default:water_flowing" and + node_to_check.name ~= "default:water_source" then + dbg_mobf.spawning_lvl2("MOBF: spawner " .. printpos(pos) .. " not in water but:" .. dump(node_to_check)) + good = false + end + + local found_nodes = minetest.env:find_nodes_in_area({x=pos.x-1,y=pos.y-1,z=pos.z-1}, + {x=pos.x+1,y=pos.y+1,z=pos.z+1}, + { "default:water_flowing","default:water_source"} ) + if #found_nodes < 4 then + dbg_mobf.spawning_lvl2("MOBF: spawner " .. printpos(pos) .. " not enough water around: " .. #found_nodes) + good = false + end + + --check if water is to deep + if mobf_air_distance(pos) > 10 then + dbg_mobf.spawning_lvl2("MOBF: spawner " .. printpos(pos) .. " air distance to far no dirt around") + good = false + end + + --make sure we're near green coast + if minetest.env:find_node_near(pos, 10, + {"default:dirt","default:dirt_with_grass"}) == nil then + dbg_mobf.spawning_lvl2("MOBF: spawner " .. printpos(pos) .. " no dirt around") + good = false + end + + if not good then + dbg_mobf.spawning_lvl2("MOBF: not spawning, spawner for " .. self.spawner_mob_name .. " somehow got to bad place " .. printpos(pos)) + --TODO try to move spawner to better place + + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + return + end + + + if mobf_mob_around(self.spawner_mob_name, + self.spawner_mob_transform, + pos, + self.spawner_mob_spawndata.density,true) == 0 then + + spawning.spawn_and_check(self.spawner_mob_name,"__default",pos,"in_shallow_water_spawner_ent") + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + else + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + dbg_mobf.spawning_lvl2("MOBF: not spawning " .. self.spawner_mob_name .. " there's a mob around") + end + end) + + --add mob spawner on map generation + minetest.register_on_generated(function(minp, maxp, seed) + + spawning.divide_mapgen_entity(minp,maxp,spawning_data,mob_name, + function(name,pos,min_y,max_y) + dbg_mobf.spawning_lvl3("MOBF: trying to create a spawner for " .. name .. " at " ..printpos(pos)) + local surface = mobf_get_surface(pos.x,pos.z,min_y,max_y) + + if surface then + pos.y=surface - math.random(2,10) + + local node_to_check = minetest.env:get_node(pos) + + if node_to_check ~= nil and + node_to_check.name == "default:water_source" or + node_to_check.name == "default:water_flowing" then + spawning.spawn_and_check(name,"_spawner",pos,"in_shallow_water_spawner") + return true + else + dbg_mobf.spawning_lvl3("MOBF: pos to add spawner is not water but: " .. dump(node_to_check)) + end + else + dbg_mobf.spawning_lvl3("MOBF: unable to find surface") + end + return false + end, + 15 + ) + end) --register mapgen + end --end spawn algo +--!@} + +spawning.register_spawn_algorithm("in_shallow_water", mobf_spawn_in_shallow_water) +spawning.register_spawn_algorithm("in_shallow_water_spawner", mobf_spawn_in_shallow_water_entity,spawning.register_cleanup_spawner) \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/none.lua b/mods/mobf/spawn_algorithms/none.lua new file mode 100644 index 0000000..c564f4a --- /dev/null +++ b/mods/mobf/spawn_algorithms/none.lua @@ -0,0 +1,34 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file none.lua +--! @brief dummy for mobs that don't spawn by themselfs at all +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- name: mobf_spawn_none(mob_name,mob_transform,spawning_data) +-- +--! @brief don't spawn at all +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +------------------------------------------------------------------------------- +function mobf_spawn_none(mob_name,mob_transform,spawning_data) + --intended to do nothing! +end + +--!@} + +spawning.register_spawn_algorithm("none", mobf_spawn_none) \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/shadows.lua b/mods/mobf/spawn_algorithms/shadows.lua new file mode 100644 index 0000000..dfa80cf --- /dev/null +++ b/mods/mobf/spawn_algorithms/shadows.lua @@ -0,0 +1,207 @@ +------------------------------------------------------------------------------- +-- 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 shadows.lua +--! @brief spawn algorithm for in shadow spawning +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_shadows(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place with low light to spawn an mob +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- + +function mobf_spawn_in_shadows(mob_name,mob_transform,spawning_data,environment) + + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering shadow spawn abm callback for mob "..mob_name) + + local media = nil + + if environment ~= nil and + environment.media ~= nil then + media = environment.media + end + + minetest.register_abm({ + nodenames = { "default:stone" }, + neighbors = media, + interval = 60, + chance = math.floor(1/spawning_data.rate), + action = function(pos, node, active_object_count, active_object_count_wider) + local starttime = mobf_get_time_ms() + local pos_above = { + x = pos.x, + y = pos.y + spawning_data.height/2, + z = pos.z + } + + --never try to spawn an mob at pos (0,0,0) it's initial entity spawnpos and + --used to find bugs in initial spawnpoint setting code + if mobf_pos_is_zero(pos) then + dbg_mobf.spawning_lvl1("MOBF: not spawning due to 0 pos") + mobf_warn_long_fct(starttime,"mobf_spawn_in_shadows") + return + end + + --check if there s enough space above to place mob + if mobf_air_above(pos,spawning_data.height) ~= true then + dbg_mobf.spawning_lvl3("MOBF: height requirement not met") + mobf_warn_long_fct(starttime,"mobf_spawn_in_shadows") + return + end + + local node_above = minetest.env:get_node(pos_above) + + if mob_name == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: Bug!!! mob name not available") + else + dbg_mobf.spawning_lvl3("MOBF: trying to spawn " .. mob_name) + if mobf_mob_around(mob_name,mob_transform,pos,spawning_data.density,true) == 0 then + + local maxlight = mobf_max_light_around(pos,3,0.5) + if maxlight < 3 then + + local newobject = minetest.env:add_entity(pos_above,mob_name .. "__default") + + local newentity = mobf_find_entity(newobject) + + if newentity == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: unable to create mob " .. mob_name .. " at pos " .. printpos(pos)) + else + minetest.log(LOGLEVEL_INFO,"MOBF: Spawning "..mob_name.." in shadows at position "..printpos(pos) .. " maxlight was: " .. maxlight) + end + else + dbg_mobf.spawning_lvl3("MOBF: too much light next to " ..printpos(pos) .. " light: " .. maxlight ) + end + else + dbg_mobf.spawning_lvl3("MOBF: too many mobs next to " ..printpos(pos)) + end + end + mobf_warn_long_fct(starttime,"mobf_spawn_in_shadows") + end, + }) +end + +------------------------------------------------------------------------------- +-- name: mobf_spawn_in_deep_large_caves(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place in deep large caces to spawn +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_in_shadows_entity(mob_name,mob_transform,spawning_data,environment) + + spawning.register_spawner_entity(mob_name,mob_transform,spawning_data,environment, + function(self) + local pos = self.object:getpos() + local good = true + + dbg_mobf.spawning_lvl3("MOBF: " .. dump(self.spawner_mob_env)) + + --check if own position is good + local pos_below = {x=pos.x,y=pos.y-1,z=pos.z} + local node_below = minetest.env:get_node(pos_below) + + + if not mobf_contains({ "default:stone","default:gravel","default:dirt" },node_below.name) then + good = false + end + + --check if there s enough space above to place mob + if mobf_air_above(pos_below,self.spawner_mob_spawndata.height) ~= true then + good = false + end + + for i=0.0,1,0.1 do + if minetest.env:get_node_light(pos,i) > 6 then + good = false + end + end + + if not good then + dbg_mobf.spawning_lvl2("MOBF: not spawning spawner for " .. self.spawner_mob_name .. " somehow got to bad place") + --TODO try to move spawner to better place + + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + return + end + + if mobf_mob_around(self.spawner_mob_name, + self.spawner_mob_transform, + pos, + self.spawner_mob_spawndata.density,true) == 0 then + + spawning.spawn_and_check(self.spawner_mob_name,"__default",pos,"shadows_spawner_ent") + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + else + self.spawner_time_passed = self.spawner_mob_spawndata.respawndelay + dbg_mobf.spawning_lvl2("MOBF: not spawning " .. self.spawner_mob_name .. " there's a mob around") + end + end) + + --add mob spawner on map generation + minetest.register_on_generated(function(minp, maxp, seed) + + spawning.divide_mapgen_entity(minp,maxp,spawning_data,mob_name, + function(name,pos,min_y,max_y,spawning_data) + + dbg_mobf.spawning_lvl3("MOBF: trying to create a spawner for " .. name .. " at " ..printpos(pos)) + local surface = mobf_get_surface(pos.x,pos.z,min_y,max_y) + + if surface then + pos.y= surface -1 + + local node = minetest.env:get_node(pos) + + if not mobf_contains({ "default:stone","default:gravel","default:dirt","default:sand" },node.name) then + dbg_mobf.spawning_lvl3("MOBF: node ain't of correct type: " .. node.name) + return false + end + + local pos_above = {x=pos.x,y=pos.y+1,z=pos.z} + local node_above = minetest.env:get_node(pos_above) + if not mobf_contains({"air"},node_above.name) then + dbg_mobf.spawning_lvl3("MOBF: node above ain't air but: " .. node_above.name) + return false + end + + --check if its always in shadows + for i=0.0,1,0.1 do + if minetest.env:get_node_light(pos_above,i) > 6 then + return false + end + end + + spawning.spawn_and_check(name,"_spawner",pos_above,"shadows_spawner") + return true + else + dbg_mobf.spawning_lvl3("MOBF: didn't find surface for " .. name .. " spawner at " ..printpos(pos)) + end + return false + end) + end) --register mapgen +end + +--!@} + +spawning.register_spawn_algorithm("shadows", mobf_spawn_in_shadows) +spawning.register_spawn_algorithm("shadows_spawner", mobf_spawn_in_shadows_entity,spawning.register_cleanup_spawner) \ No newline at end of file diff --git a/mods/mobf/spawn_algorithms/willow.lua b/mods/mobf/spawn_algorithms/willow.lua new file mode 100644 index 0000000..26483ff --- /dev/null +++ b/mods/mobf/spawn_algorithms/willow.lua @@ -0,0 +1,126 @@ +------------------------------------------------------------------------------- +-- 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 willow.lua +--! @brief spawn algorithm willow +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @addtogroup spawn_algorithms +--! @{ +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- name: mobf_spawn_on_willow(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place on willow to spawn a mob +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_on_willow(mob_name,mob_transform,spawning_data,environment) + minetest.log(LOGLEVEL_WARNING,"MOBF: using deprecated abm based spawn algorithm \"spawn_on_willow\" most likely causing lag in server!\t Use spawn_on_willow_mapgen instead!") + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering willow spawn abm callback for mob "..mob_name) + + local media = nil + + if environment ~= nil and + environment.media ~= nil then + media = environment.media + end + + minetest.register_abm({ + nodenames = { "default:dirt_with_grass" }, + neighbors = media, + interval = 7200, + chance = math.floor(1/spawning_data.rate), + action = function(pos, node, active_object_count, active_object_count_wider) + local starttime = mobf_get_time_ms() + local pos_above = { + x = pos.x, + y = pos.y + 1, + z = pos.z + } + + --never try to spawn an mob at pos (0,0,0) it's initial entity spawnpos and + --used to find bugs in initial spawnpoint setting code + if mobf_pos_is_zero(pos) then + mobf_warn_long_fct(starttime,"mobf_spawn_on_willow") + return + end + + --check if there s enough space above to place mob + if mobf_air_above(pos,spawning_data.height) ~= true then + mobf_warn_long_fct(starttime,"mobf_spawn_on_willow") + return + end + + local node_above = minetest.env:get_node(pos_above) + + if mob_name == nil then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!! mob name not available") + else + --print("Try to spawn mob: "..mob_name) + if node_above.name == "air" then + --print("Find mobs of same type around:"..mob_name.. " pop dens: ".. population_density) + if mobf_mob_around(mob_name,mob_transform,pos,spawning_data.density,true) == 0 then + local newobject = minetest.env:add_entity(pos_above,mob_name .. "__default") + + local newentity = mobf_find_entity(newobject) + + if newentity == nil then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!! no "..mob_name.." has been created!") + end + minetest.log(LOGLEVEL_INFO,"MOBF: Spawning "..mob_name.." on willow at position "..printpos(pos)) + end + end + end + mobf_warn_long_fct(starttime,"mobf_spawn_on_willow") + end, + }) +end + +------------------------------------------------------------------------------- +-- name: mobf_spawn_on_willow_mapgen(mob_name,mob_transform,spawning_data,environment) +-- +--! @brief find a place on willow to spawn a mob on map generation +-- +--! @param mob_name name of mob +--! @param mob_transform secondary name of mob +--! @param spawning_data spawning configuration +--! @param environment environment of mob +------------------------------------------------------------------------------- +function mobf_spawn_on_willow_mapgen(mob_name,mob_transform,spawning_data,environment) + minetest.log(LOGLEVEL_INFO,"MOBF:\tregistering willow mapgen spawn mapgen callback for mob "..mob_name) + + --add mob on map generation + minetest.register_on_generated(function(minp, maxp, seed) + spawning.divide_mapgen(minp,maxp,spawning_data.density,mob_name,mob_transform, + + function(name,pos,min_y,max_y) + + --check if there s enough space above to place mob + if mobf_air_above(pos,spawning_data.height) then + dbg_mobf.spawning_lvl3("enough air above " ..printpos(centerpos) .. " minimum is: " .. spawning_data.height ) + local spawnpos = {x=pos.x,y=pos.y+1,z=pos.z} + spawning.spawn_and_check(name,"__default",spawnpos,"on_willow_mapgen") + return true + end -- air_above + return false + end, + mobf_get_sunlight_surface, + 5) + end) + end --end spawn algo +--!@} + +spawning.register_spawn_algorithm("willow", mobf_spawn_on_willow) +spawning.register_spawn_algorithm("willow_mapgen", mobf_spawn_on_willow_mapgen) \ No newline at end of file diff --git a/mods/mobf/spawning.lua b/mods/mobf/spawning.lua new file mode 100644 index 0000000..17246b7 --- /dev/null +++ b/mods/mobf/spawning.lua @@ -0,0 +1,648 @@ +------------------------------------------------------------------------------- +-- Mob Framework Mod by Sapier +-- +-- You may copy, use, modify or do nearly anything except removing this +-- copyright notice. +-- And of course you are NOT allow to pretend you have written it. +-- +--! @file spawning.lua +--! @brief component containing spawning features +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- + +--! @defgroup spawning Spawn mechanisms +--! @brief all functions and variables required for automatic mob spawning +--! @ingroup framework_int +--! @{ +-- +--! @defgroup spawn_algorithms Spawn algorithms +--! @brief spawn algorithms provided by mob framework (can be extended by mods) +-- +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- +--! @class spawning +--! @brief spawning features +spawning = {} + +--!@} + +--! @brief registry for spawn algorithms +--! @memberof spawning +--! @private +mobf_spawn_algorithms = {} + + +------------------------------------------------------------------------------- +-- name: remove_uninitialized(entity,staticdata) +-- @function [parent=#spawning] remove_uninitialized +-- +--! @brief remove a spawn point based uppon staticdata supplied +--! @memberof spawning +-- +--! @param entity to remove +--! @param staticdata of mob +------------------------------------------------------------------------------- +function spawning.remove_uninitialized(entity, staticdata) + --entity may be known in spawnlist + if staticdata ~= nil then + local permanent_data = mobf_deserialize_permanent_entity_data(staticdata) + if (permanent_data.spawnpoint ~= nil) then + + --prepare information required to remove entity + entity.dynamic_data = {} + entity.dynamic_data.spawning = {} + entity.dynamic_data.spawning.spawnpoint = permanent_data.spawnpoint + + spawning.remove(entity,"remove uninitialized") + end + else + dbg_mobf.spawning_lvl1("MOBF: remove uninitialized entity=" .. tostring(entity)) + --directly remove it can't be known to spawnlist + entity.object:remove() + end +end + +------------------------------------------------------------------------------- +-- name: remove(entity) +-- @function [parent=#spawning] remove +-- +--! @brief remove a mob +--! @memberof spawning +-- +--! @param entity mob to remove +--! @param reason text to log as reason for removal +------------------------------------------------------------------------------- +function spawning.remove(entity,reason) + local pos = entity.object:getpos() + dbg_mobf.spawning_lvl3("MOBF: --> remove " .. printpos(pos)) + if entity ~= nil then + entity.removed = true + dbg_mobf.spawning_lvl1("MOBF: remove entity=" .. tostring(entity)) + if minetest.setting_getbool("mobf_log_removed_entities") then + if reason == nil then + reason = "unknown" + end + minetest.log(LOGLEVEL_NOTICE,"MOBF: removing " .. entity.data.name .. + " at " .. printpos(pos) .. " due to: " .. reason) + end + entity.object:remove() + else + minetest.log(LOGLEVEL_ERROR,"Trying to delete an an non existant mob") + end + + dbg_mobf.spawning_lvl3("MOBF: <-- remove") +end + +------------------------------------------------------------------------------- +-- name: init_dynamic_data(entity) +-- @function [parent=#spawning] init_dynamic_data +-- +--! @brief initialize dynamic data required for spawning +--! @memberof spawning +-- +--! @param entity mob to initialize dynamic data +--! @param now current time +------------------------------------------------------------------------------- +function spawning.init_dynamic_data(entity,now) + + local data = { + player_spawned = false, + ts_dense_check = now, + spawnpoint = entity.object:getpos(), + original_spawntime = now, + } + + entity.removed = false + entity.dynamic_data.spawning = data +end + + + +------------------------------------------------------------------------------- +-- name: check_population_density(mob) +-- @function [parent=#spawning] check_population_density +-- +--! @brief check and fix if there are too many mobs within a specific range +--! @memberof spawning +-- +--! @param entity mob to check +--! @param now current time +------------------------------------------------------------------------------- +function spawning.check_population_density(entity,now) + + if entity == nil or + entity.dynamic_data == nil or + entity.dynamic_data.spawning == nil then + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!! " .. entity.data.name .. + " pop dense check called for entity with missing spawn data entity=" .. + tostring(entity)) + return + end + + + -- don't check if mob is player spawned + if entity.dynamic_data.spawning.player_spawned == true then + dbg_mobf.spawning_lvl1("MOBF: mob is player spawned skipping pop dense check") + return + end + + + --don't do population check while fighting + if entity.dynamic_data.combat ~= nil and + entity.dynamic_data.combat.target ~= "" then + return + end + + + --only check every 15 seconds + if entity.dynamic_data.spawning.ts_dense_check + 15 > now then + return + end + + entity.dynamic_data.spawning.ts_dense_check = now + + local entitypos = mobf_round_pos(entity.object:getpos()) + + --mob either not initialized completely or a bug + if mobf_pos_is_zero(entitypos) then + return + end + + local secondary_name = "" + if entity.data.harvest ~= nil then + secondary_name = entity.data.harvest.transform_to + end + + local mob_count = mobf_mob_around(entity.data.modname..":"..entity.data.name, + secondary_name, + entitypos, + entity.data.spawning.density, + true) + if mob_count > 5 then + entity.removed = true + minetest.log(LOGLEVEL_WARNING,"MOBF: Too many ".. mob_count .. " ".. + entity.data.name.." at one place dying: " .. + tostring(entity.dynamic_data.spawning.player_spawned)) + spawning.remove(entity, "population density check") + else + dbg_mobf.spawning_lvl3("Density ok only "..mob_count.." mobs around") + end +end + + +------------------------------------------------------------------------------- +-- name: replace_entity(pos,name,spawnpos,health) +-- @function [parent=#spawning] replace_entity +-- +--! @brief replace mob at a specific position by a new one +--! @memberof spawning +-- +--! @param entity mob to replace +--! @param name of the mob to add +--! @param preserve preserve original spawntime +--! @return entity added or nil on error +------------------------------------------------------------------------------- +function spawning.replace_entity(entity,name,preserve) + dbg_mobf.spawning_lvl3("MOBF: --> replace_entity(" + .. entity.data.name .. "|" .. name .. ")") + + if minetest.registered_entities[name] == nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: replace_entity: Bug no " + ..name.." is registred") + return nil + end + + -- avoid switching to same entity + if entity.name == name then + minetest.log(LOGLEVEL_INFO,"MOBF: not replacing " .. name .. + " by entity of same type!") + return nil + end + + + -- get data to be transfered to new entity + local pos = mobf.get_basepos(entity) + local health = entity.object:get_hp() + local temporary_dynamic_data = entity.dynamic_data + local entity_orientation = entity.object:getyaw() + + if preserve == nil or preserve == false then + temporary_dynamic_data.spawning.original_spawntime = mobf_get_current_time() + end + + --calculate new y pos + if minetest.registered_entities[name].collisionbox ~= nil then + pos.y = pos.y - minetest.registered_entities[name].collisionbox[2] + end + + + --delete current mob + dbg_mobf.spawning_lvl2("MOBF: replace_entity: removing " .. entity.data.name) + + --unlink dynamic data (this should work but doesn't due to other bugs) + entity.dynamic_data = nil + + --removing is done after exiting lua! + spawning.remove(entity,"replaced") + + local newobject = minetest.env:add_entity(pos,name) + local newentity = mobf_find_entity(newobject) + + if newentity ~= nil then + if newentity.dynamic_data ~= nil then + dbg_mobf.spawning_lvl2("MOBF: replace_entity: " .. name .. + " added at " .. + printpos(newentity.dynamic_data.spawning.spawnpoint)) + newentity.dynamic_data = temporary_dynamic_data + newentity.object:set_hp(health) + newentity.object:setyaw(entity_orientation) + else + minetest.log(LOGLEVEL_ERROR, + "MOBF: replace_entity: dynamic data not set for "..name.. + " maybe delayed activation?") + newentity.dyndata_delayed = { + data = temporary_dynamic_data, + health = health, + orientation = entity_orientation + } + end + else + minetest.log(LOGLEVEL_ERROR, + "MOBF: replace_entity 4 : Bug no "..name.." has been created") + end + dbg_mobf.spawning_lvl3("MOBF: <-- replace_entity") + return newentity +end + +------------------------------------------------------------------------------ +-- name: lifecycle() +-- @function [parent=#spawning] lifecycle +-- +--! @brief check mob lifecycle +--! @memberof spawning +-- +--! @return true/false still alive dead +------------------------------------------------------------------------------- +function spawning.lifecycle(entity,now) + + if entity.dynamic_data.spawning.original_spawntime ~= nil and + entity.data.spawning.lifetime ~= nil then + + local lifetime = entity.data.spawning.lifetime + + local current_age = now - entity.dynamic_data.spawning.original_spawntime + + if current_age > 0 and + current_age > lifetime then + dbg_mobf.spawning_lvl1("MOBF: removing animal due to limited lifetime") + spawning.remove(entity," limited mob lifetime") + return false + end + else + entity.dynamic_data.spawning.original_spawntime = now + end + + return true +end + +------------------------------------------------------------------------------ +-- name: register_spawn_algorithm() +-- @function [parent=#spawning] register_spawn_algorithm +-- +--! @brief print current spawn statistics +--! @memberof spawning +-- +--! @return true/false successfully added spawn algorithm +------------------------------------------------------------------------------- +function spawning.register_spawn_algorithm(name, spawnfunc, cleanupfunc) + + if (mobf_spawn_algorithms[name] ~= nil) then + return false + end + + local new_algorithm = {} + + new_algorithm.register_spawn = spawnfunc + new_algorithm.register_cleanup = cleanupfunc + + mobf_spawn_algorithms[name] = new_algorithm + + return true +end + +------------------------------------------------------------------------------ +-- name: spawn_and_check(name,suffix,pos) +-- @function [parent=#spawning] spawn_and_check +-- +--! @brief spawn an entity and check for presence +--! @memberof spawning +-- +--! @return spawned mob entity +------------------------------------------------------------------------------- +function spawning.spawn_and_check(name,suffix,pos,text) + local newobject = minetest.env:add_entity(pos,name .. suffix) + + if newobject then + local newentity = mobf_find_entity(newobject) + + if newentity == nil then + dbg_mobf.spawning_lvl3("MOBF BUG!!! no " .. name.. + " entity has been created by " .. text .. "!") + mobf_bug_warning(LOGLEVEL_ERROR,"BUG!!! no " .. name.. + " entity has been created by " .. text .. "!") + else + dbg_mobf.spawning_lvl2("MOBF: spawning "..name.." entity by " .. + text .. " at position ".. printpos(pos)) + minetest.log(LOGLEVEL_INFO,"MOBF: spawning "..name.." entity by " .. + text .. " at position ".. printpos(pos)) + return newentity + end + else + dbg_mobf.spawning_lvl3("MOBF BUG!!! no "..name.. + " object has been created by " .. text .. "!") + mobf_bug_warning(LOGLEVEL_ERROR,"MOBF BUG!!! no "..name.. + " object has been created by " .. text .. "!") + end + + return nil +end + + +------------------------------------------------------------------------------ +-- name: get_center(min,max,current_step,interval) +-- @function [parent=#spawning] get_center +-- +--! @brief calculate center and deltas +--! @memberof spawning +-- +--! @return center,delta +------------------------------------------------------------------------------- +function spawning.get_center(min,max,current_step,interval) + + dbg_mobf.spawning_lvl3("MOBF: get_center params: " .. min .. " " .. max .. + " " .. current_step .. " " .. interval ) + local abs_min = min + interval * (current_step-1) + local abs_max = abs_min + interval + + if abs_max > max then + abs_max = max + end + + local delta = (abs_max - abs_min) / 2 + + return (abs_min + delta),delta +end + +------------------------------------------------------------------------------ +-- name: divide_mapgen_entity(minp,maxp,density,name,spawnfunc) +-- @function [parent=#spawning] divide_mapgen_entity +-- +--! @brief divide mapblock into 2d chunks and call spawnfunc with randomized parameters for each +--! @memberof spawning +--! @param minp minimum 3d point of map block +--! @param maxp maximum 3d point of map block +--! @param spawndata spawndata +--! @param name name of entity to spawn +--! @param spawnfunc function to use for spawning +--! @param maxtries maximum number of tries to place a spawner +-- +------------------------------------------------------------------------------- +function spawning.divide_mapgen_entity(minp,maxp,spawndata,name,spawnfunc,maxtries) + + local density = spawndata.density + + dbg_mobf.spawning_lvl3("MOBF: divide_mapgen params: ") + dbg_mobf.spawning_lvl3("MOBF: " .. dump(spawndata.density)) + dbg_mobf.spawning_lvl3("MOBF: " .. dump(name)) + dbg_mobf.spawning_lvl3("MOBF: " .. dump(spawnfunc)) + + if maxtries == nil then + maxtries = 5 + end + + local starttime = mobf_get_time_ms() + + local min_x = MIN(minp.x,maxp.x) + local min_y = MIN(minp.y,maxp.x) + local min_z = MIN(minp.z,maxp.z) + + local max_x = MAX(minp.x,maxp.x) + local max_y = MAX(minp.y,maxp.y) + local max_z = MAX(minp.z,maxp.z) + + + local xdivs = math.floor(((max_x - min_x) / spawndata.density) +1) + local zdivs = math.floor(((max_z - min_z) / spawndata.density) +1) + + dbg_mobf.spawning_lvl3("MOBF: X: " .. min_x .. "-->" .. max_x) + dbg_mobf.spawning_lvl3("MOBF: Z: " .. min_z .. "-->" .. max_z) + dbg_mobf.spawning_lvl3("MOBF: Y: " .. min_y .. "-->" .. max_y) + dbg_mobf.spawning_lvl3("MOBF: generating in " .. xdivs .. " | " .. zdivs .. " chunks") + + for i = 1, xdivs,1 do + for j = 1, zdivs,1 do + + local x_center,x_delta = spawning.get_center(min_x,max_x,i,spawndata.density) + local z_center,z_delta = spawning.get_center(min_z,max_z,j,spawndata.density) + + local surface_center = mobf_get_surface(x_center,z_center,min_y,max_y) + + local centerpos = {x=x_center,y=surface_center,z=z_center} + + dbg_mobf.spawning_lvl3("MOBF: center is (" .. x_center .. "," .. z_center .. ")" + .." --> (".. x_delta .."," .. z_delta .. ")") + + --check if there is already a mob of same type within area + if surface_center then + local mobs_around = mobf_spawner_around(name,centerpos,spawndata.density) + if mobs_around == 0 then + dbg_mobf.spawning_lvl3("no " .. name .. " within range of " .. + spawndata.density .. " around " ..printpos(centerpos)) + for i= 0, maxtries do + local x_try = math.random(-x_delta,x_delta) + local z_try = math.random(-z_delta,z_delta) + + local pos = { x= x_center + x_try, + z= z_center + z_try } + + --do place spawners in center of block + pos.x = math.floor(pos.x + 0.5) + pos.z = math.floor(pos.z + 0.5) + + if spawnfunc(name,pos,min_y,max_y,spawndata) then + break + end + end --for -> 5 + end --mob around + else + dbg_mobf.spawning_lvl3("MOBF: didn't find surface for " ..printpos(centerpos)) + end --surface_center + end -- for z divs + end -- for x divs + dbg_mobf.spawning_lvl3("magen ended") +end + +------------------------------------------------------------------------------ +-- name: divide_mapgen(minp,maxp,density,name,spawnfunc) +-- @function [parent=#spawning] divide_mapgen +-- +--! @brief divide mapblock into 2d chunks and call spawnfunc with randomized parameters for each +--! @memberof spawning +--! @param minp minimum 3d point of map block +--! @param maxp maximum 3d point of map block +--! @param density chunk size +--! @param name name of entity to spawn +--! @param secondary_name secondary name of entity +--! @param spawnfunc function to use for spawning +--! @param surfacefunc use this function to detect surface +--! @param maxtries maximum number of tries to place a entity +-- +------------------------------------------------------------------------------- +function spawning.divide_mapgen(minp,maxp,density,name,secondary_name,spawnfunc,surfacefunc,maxtries) + local starttime = mobf_get_time_ms() + dbg_mobf.spawning_lvl3("MOBF: divide_mapgen params: ") + dbg_mobf.spawning_lvl3("MOBF: " .. dump(density)) + dbg_mobf.spawning_lvl3("MOBF: " .. dump(name)) + dbg_mobf.spawning_lvl3("MOBF: " .. dump(spawnfunc)) + + if maxtries == nil then + maxtries = 5 + end + + local starttime = mobf_get_time_ms() + + local min_x = MIN(minp.x,maxp.x) + local min_y = MIN(minp.y,maxp.x) + local min_z = MIN(minp.z,maxp.z) + + local max_x = MAX(minp.x,maxp.x) + local max_y = MAX(minp.y,maxp.y) + local max_z = MAX(minp.z,maxp.z) + + + local xdivs = math.floor(((max_x - min_x) / density) +1) + local zdivs = math.floor(((max_z - min_z) / density) +1) + + dbg_mobf.spawning_lvl3("MOBF: X: " .. min_x .. "-->" .. max_x) + dbg_mobf.spawning_lvl3("MOBF: Z: " .. min_z .. "-->" .. max_z) + dbg_mobf.spawning_lvl3("MOBF: Y: " .. min_y .. "-->" .. max_y) + dbg_mobf.spawning_lvl3("MOBF: generating in " .. xdivs .. " | " .. zdivs .. " chunks") + + for i = 1, xdivs,1 do + for j = 1, zdivs,1 do + + local x_center,x_delta = spawning.get_center(min_x,max_x,i,density) + local z_center,z_delta = spawning.get_center(min_z,max_z,j,density) + + local surface_center = surfacefunc(x_center,z_center,min_y,max_y) + + local centerpos = {x=x_center,y=surface_center,z=z_center} + + dbg_mobf.spawning_lvl3("MOBF: center is (" .. x_center .. "," .. z_center .. ") --> (".. x_delta .."," .. z_delta .. ")") + + --check if there is already a mob of same type within area + if surface_center then + local mobs_around = mobf_mob_around(name,secondary_name,centerpos,density,true) + if mobs_around == 0 then + dbg_mobf.spawning_lvl3("no " .. name .. " within range of " .. density .. " around " ..printpos(centerpos)) + i= 0, maxtries, 1 do + local x_try = math.random(-x_delta,x_delta) + local z_try = math.random(-z_delta,z_delta) + + local pos = { x= x_center + x_try, + z= z_center + z_try } + + pos.y = surfacefunc(pos.x,pos.z,min_y,max_y) + + if pos.y and spawnfunc(name,pos,min_y,max_y) then + break + end + end --for -> 5 + end --mob around + else + dbg_mobf.spawning_lvl3("MOBF: didn't find surface for " ..printpos(centerpos)) + end --surface_center + end -- for z divs + end -- for x divs + mobf_warn_long_fct(starttime,"on_mapgen" .. name,"mapgen") + dbg_mobf.spawning_lvl3("magen ended") +end + +------------------------------------------------------------------------------ +-- name: register_spawner_entity(mobname,secondary_mobname,spawndata,environment,spawnfunc) +-- @function [parent=#spawning] register_spawner_entity +-- +--! @brief register a spawner entity +--! @memberof spawning +-- +--! @param mobname name of mob +--! @param secondary_mobname secondary name of mob +--! @param spawndata spawning information to use +--! @param environment what environment is good for mob +--! @param spawnfunc function to call for spawning +-- +--! @return +------------------------------------------------------------------------------- +function spawning.register_spawner_entity(mobname,secondary_mobname,spawndata,environment,spawnfunc) + minetest.register_entity(mobname .. "_spawner", + { + physical = false, + collisionbox = { 0.0,0.0,0.0,0.0,0.0,0.0}, + visual = "sprite", + textures = { "invisible.png^[makealpha:128,0,0^[makealpha:128,128,0" }, + + + on_step = function(self,dtime) + self.spawner_time_passed = self.spawner_time_passed -dtime + + + if self.spawner_time_passed < 0 then + local starttime = mobf_get_time_ms() + spawnfunc(self) + mobf_warn_long_fct(starttime,"spawnfunc " .. self.spawner_mob_name,"spawnfunc") + end + end, + + on_activate = function(self,staticdata) + if self.spawner_mob_transform == nil then + self.spawner_mob_transform = "" + end + end, + + spawner_mob_name = mobname, + spawner_mob_transform = secondary_mobname, + spawner_time_passed = 1, + spawner_mob_env = environment, + spawner_mob_spawndata = spawndata, + }) + +end + +------------------------------------------------------------------------------ +-- name: register_cleanup_spawner(mobname) +-- @function [parent=#spawning] register_cleanup_spawner +-- +--! @brief register an entity to cleanup spawners +--! @memberof spawning +------------------------------------------------------------------------------- +function spawning.register_cleanup_spawner(mobname) + minetest.register_entity(mobname .. "_spawner", + { + on_activate = function(self,staticdata) + self.object:remove() + end + }) +end + +--include spawn algorithms +dofile (mobf_modpath .. "/spawn_algorithms/at_night.lua") +dofile (mobf_modpath .. "/spawn_algorithms/forrest.lua") +dofile (mobf_modpath .. "/spawn_algorithms/in_shallow_water.lua") +dofile (mobf_modpath .. "/spawn_algorithms/shadows.lua") +dofile (mobf_modpath .. "/spawn_algorithms/willow.lua") +dofile (mobf_modpath .. "/spawn_algorithms/big_willow.lua") +dofile (mobf_modpath .. "/spawn_algorithms/in_air1.lua") +dofile (mobf_modpath .. "/spawn_algorithms/none.lua") +dofile (mobf_modpath .. "/spawn_algorithms/deep_large_caves.lua") \ No newline at end of file diff --git a/mods/mobf/textures/animals_egg.png b/mods/mobf/textures/animals_egg.png new file mode 100644 index 0000000..610ea0e Binary files /dev/null and b/mods/mobf/textures/animals_egg.png differ diff --git a/mods/mobf/textures/animals_fireball.png b/mods/mobf/textures/animals_fireball.png new file mode 100644 index 0000000..ad4130f Binary files /dev/null and b/mods/mobf/textures/animals_fireball.png differ diff --git a/mods/mobf/textures/animals_plasmaball.png b/mods/mobf/textures/animals_plasmaball.png new file mode 100644 index 0000000..a768bf2 Binary files /dev/null and b/mods/mobf/textures/animals_plasmaball.png differ diff --git a/mods/mobf/textures/animals_spawn_marker.png b/mods/mobf/textures/animals_spawn_marker.png new file mode 100644 index 0000000..d1614b6 Binary files /dev/null and b/mods/mobf/textures/animals_spawn_marker.png differ diff --git a/mods/mobf/textures/invisible.png b/mods/mobf/textures/invisible.png new file mode 100644 index 0000000..8cb82be Binary files /dev/null and b/mods/mobf/textures/invisible.png differ diff --git a/mods/mobf/textures/old/templates/animals_template_128_4.png b/mods/mobf/textures/old/templates/animals_template_128_4.png new file mode 100644 index 0000000..8b399c3 Binary files /dev/null and b/mods/mobf/textures/old/templates/animals_template_128_4.png differ diff --git a/mods/mobf/textures/old/templates/animals_template_128_4_2levels.png b/mods/mobf/textures/old/templates/animals_template_128_4_2levels.png new file mode 100644 index 0000000..48b00c4 Binary files /dev/null and b/mods/mobf/textures/old/templates/animals_template_128_4_2levels.png differ diff --git a/mods/mobf/textures/old/templates/animals_template_128_6.png b/mods/mobf/textures/old/templates/animals_template_128_6.png new file mode 100644 index 0000000..ac7e420 Binary files /dev/null and b/mods/mobf/textures/old/templates/animals_template_128_6.png differ diff --git a/mods/mobf/textures/old/templates/animals_template_32.png b/mods/mobf/textures/old/templates/animals_template_32.png new file mode 100644 index 0000000..05ec25a Binary files /dev/null and b/mods/mobf/textures/old/templates/animals_template_32.png differ diff --git a/mods/mobf/weapons.lua b/mods/mobf/weapons.lua new file mode 100644 index 0000000..cbd8eed --- /dev/null +++ b/mods/mobf/weapons.lua @@ -0,0 +1,327 @@ +------------------------------------------------------------------------------- +-- 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 weapons.lua +--! @brief weapon related functions +--! @copyright Sapier +--! @author Sapier +--! @date 2012-08-09 +-- +--! @defgroup weapons Weapons +--! @brief weapon entitys predefined by mob framework (can be extended by mod) +-- +-- Contact sapier a t gmx net +------------------------------------------------------------------------------- + +local weapons_spacer = {} --unused to fix lua doxygen bug only + +------------------------------------------------------------------------------- +-- name: mobf_init_weapons = function(self, dtime) +-- +--! @brief initialize weapons handled by mobf mod +-- +------------------------------------------------------------------------------- +function mobf_init_weapons() + minetest.register_entity(":mobf:fireball_entity", MOBF_FIREBALL_ENTITY) + minetest.register_entity(":mobf:plasmaball_entity", MOBF_PLASMABALL_ENTITY) +end + +------------------------------------------------------------------------------- +-- name: mobf_do_area_damage(pos,immune,damage,range) +-- +--! @brief damage all objects within a certain range +-- +--! @param pos cennter of damage area +--! @param immune object immune to damage +--! @param damage damage to be done +--! @param range range around pos +------------------------------------------------------------------------------- +function mobf_do_area_damage(pos,immune,damage,range) + --damage objects within inner blast radius + objs = minetest.env:get_objects_inside_radius(pos, range) + for k, obj in pairs(objs) do + + --don't do damage to issuer + if obj ~= immune then + obj:set_hp(obj:get_hp()-damage) + end + end +end + + +------------------------------------------------------------------------------- +-- name: mobf_do_node_damage(pos,immune_list,range,chance) +-- +--! @brief damage all nodes within a certain range +-- +--! @param pos center of area +--! @param immune_list list of nodes immune to damage +--! @param range range to do damage +--! @param chance chance damage is done to a node +------------------------------------------------------------------------------- +function mobf_do_node_damage(pos,immune_list,range,chance) + --do node damage + for i=pos.x-range, pos.x+range, 1 do + for j=pos.y-range, pos.y+range, 1 do + for k=pos.z-range,pos.z+range,1 do + --TODO create a little bit more sophisticated blast resistance + if math.random() < chance then + local toremove = minetest.env:get_node({x=i,y=j,z=k}) + + if toremove ~= nil then + local immune = false + + if immune_list ~= nil then + for i,v in ipairs(immune_list) do + if (torremove.name == v) then + immune = true + end + end + end + + + if immune ~= true then + minetest.env:remove_node({x=i,y=j,z=k}) + end + end + end + end + end + end +end + +--! @class MOBF_FIREBALL_ENTITY +--! @ingroup weapons +--! @brief a fireball weapon entity +MOBF_FIREBALL_ENTITY = { + physical = false, + textures = {"animals_fireball.png"}, + collisionbox = {0,0,0,0,0,0}, + + damage_range = 4, + velocity = 3, + gravity = -0.01, + + damage = 15, + + owner = 0, + lifetime = 30, + created = -1, +} + + +------------------------------------------------------------------------------- +-- name: MOBF_FIREBALL_ENTITY.on_activate = function(self, staticdata) +-- +--! @brief onactivate callback for fireball +--! @memberof MOBF_FIREBALL_ENTITY +--! @private +-- +--! @param self fireball itself +--! @param staticdata +------------------------------------------------------------------------------- +function MOBF_FIREBALL_ENTITY.on_activate(self,staticdata) + self.created = mobf_get_current_time() +end + +------------------------------------------------------------------------------- +-- name: MOBF_FIREBALL_ENTITY.surfacefire = function(self, staticdata) +-- +--! @brief place fire on surfaces around pos +--! @memberof MOBF_FIREBALL_ENTITY +--! @private +-- +--! @param pos position to place fire around +--! @param range square around pos to set on fire +------------------------------------------------------------------------------- +function MOBF_FIREBALL_ENTITY.surfacefire(pos,range) + + if mobf_rtd.fire_enabled then + --start fire on any surface within inner damage range + for i=pos.x-range/2, pos.x+range/2, 1 do + for j=pos.y-range/2, pos.y+range/2, 1 do + for k=pos.z-range/2, pos.z+range/2, 1 do + + local current = minetest.env:get_node({x=i,y=j,z=k}) + local ontop = minetest.env:get_node({x=i,y=j+1,z=k}) + + --print("put fire? " .. printpos({x=i,y=j,z=k}) .. " " .. current.name .. " " ..ontop.name) + + if (current.name ~= "air") and + (current.name ~= "fire:basic_flame") and + (ontop.name == "air") then + minetest.env:set_node({x=i,y=j+1,z=k}, {name="fire:basic_flame"}) + end + + end + end + end + else + minetest.log(LOGLEVEL_ERROR,"MOBF: A fireball without fire mod??!? You're kidding!!") + end +end + +------------------------------------------------------------------------------- +-- name: MOBF_FIREBALL_ENTITY.on_step = function(self, dtime) +-- +--! @brief onstep callback for fireball +--! @memberof MOBF_FIREBALL_ENTITY +--! @private +-- +--! @param self fireball itself +--! @param dtime time since last callback +------------------------------------------------------------------------------- +function MOBF_FIREBALL_ENTITY.on_step(self, dtime) + local pos = self.object:getpos() + local node = minetest.env:get_node(pos) + + + --detect hit + local objs=minetest.env:get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 1) + + local hit = false + + for k, obj in pairs(objs) do + if obj:get_entity_name() ~= "mobf:fireball_entity" and + obj ~= self.owner then + hit=true + end + end + + + if hit then + --damage objects within inner blast radius + mobf_do_area_damage(pos,self.owner,self.damage_range/4,self.damage/4) + + --damage all objects within blast radius + mobf_do_area_damage(pos,self.owner,self.damage_range/2,self.damage/2) + + MOBF_FIREBALL_ENTITY.surfacefire(pos,self.damage_range) + + self.object:remove() + end + + -- vanish when hitting a node + if node.name ~= "air" then + MOBF_FIREBALL_ENTITY.surfacefire(pos,self.damage_range) + self.object:remove() + end + + --remove after lifetime has passed + if self.created > 0 and + self.created + self.lifetime < mobf_get_current_time() then + self.object:remove() + end +end + + +--! @class MOBF_PLASMABALL_ENTITY +--! @ingroup weapons +--! @brief a plasmaball weapon entity +MOBF_PLASMABALL_ENTITY = { + physical = false, + textures = {"animals_plasmaball.png"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, + + damage_range = 2, + velocity = 4, + gravity = -0.001, + + damage = 8, + + owner = 0, + lifetime = 30, + created = -1, +} + +------------------------------------------------------------------------------- +-- name: MOBF_PLASMABALL_ENTITY.on_activate = function(self, staticdata) +-- +--! @brief onactivate callback for plasmaball +--! @memberof MOBF_PLASMABALL_ENTITY +--! @private +-- +--! @param self fireball itself +--! @param staticdata +------------------------------------------------------------------------------- +function MOBF_PLASMABALL_ENTITY.on_activate(self,staticdata) + self.created = mobf_get_current_time() +end + + +------------------------------------------------------------------------------- +-- name: MOBF_PLASMABALL_ENTITY.on_step = function(self, dtime) +-- +--! @brief onstep callback for plasmaball +--! @memberof MOBF_PLASMABALL_ENTITY +--! @private +-- +--! @param self plasmaball itself +--! @param dtime time since last callback +------------------------------------------------------------------------------- +function MOBF_PLASMABALL_ENTITY.on_step(self, dtime) + local pos = self.object:getpos() + local node = minetest.env:get_node(pos) + + + --detect hit + local objs=minetest.env:get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 1) + + local hit = false + + for k, obj in pairs(objs) do + if obj:get_entity_name() ~= "mobf:plasmaball_entity" and + obj ~= self.owner then + hit=true + end + end + + --damage all objects not hit but at least passed + mobf_do_area_damage(pos,self.owner,2,1) + + if hit then + --damage objects within inner blast radius + mobf_do_area_damage(pos,self.owner,self.damage_range/4,self.damage/2) + + --damage all objects within blast radius + mobf_do_area_damage(pos,self.owner,self.damage_range/2,self.damage/2) + end + + -- vanish when hitting a node + if node.name ~= "air" or + hit then + + --replace this loop by minetest.env:find_node_near? + --do node damage + for i=pos.x-1, pos.x+1, 1 do + for j=pos.y-1, pos.y+1, 1 do + for k=pos.z-1,pos.z+1,1 do + --TODO create a little bit more sophisticated blast resistance + if math.random() < 0.5 then + local toremove = minetest.env:get_node({x=i,y=j,z=k}) + + if toremove ~= nil and + toremove.name ~= "default:stone" and + toremove.name ~= "default:cobble" then + + minetest.env:remove_node({x=i,y=j,z=k}) + end + end + end + end + end + + self.object:remove() + end + + --remove after lifetime has passed + if self.created > 0 and + self.created + self.lifetime < mobf_get_current_time() then + self.object:remove() + end +end diff --git a/mods/walking_light/init.lua b/mods/walking_light/init.lua new file mode 100644 index 0000000..e51805a --- /dev/null +++ b/mods/walking_light/init.lua @@ -0,0 +1,144 @@ +local players = {} +local player_positions = {} +local last_wielded = {} + +function round(num) + return math.floor(num + 0.5) +end + +minetest.register_on_joinplayer(function(player) + local player_name = player:get_player_name() + table.insert(players, player_name) + last_wielded[player_name] = player:get_wielded_item():get_name() + local pos = player:getpos() + local rounded_pos = {x=round(pos.x),y=round(pos.y)+1,z=round(pos.z)} + local wielded_item = player:get_wielded_item():get_name() + if wielded_item ~= "default:torch" and wielded_item ~= "walking_light:pick_mese" then + -- Neuberechnung des Lichts erzwingen + minetest.env:add_node(rounded_pos,{type="node",name="default:cobble"}) + minetest.env:add_node(rounded_pos,{type="node",name="air"}) + end + player_positions[player_name] = {} + player_positions[player_name]["x"] = rounded_pos.x; + player_positions[player_name]["y"] = rounded_pos.y; + player_positions[player_name]["z"] = rounded_pos.z; +end) + +minetest.register_on_leaveplayer(function(player) + local player_name = player:get_player_name() + for i,v in ipairs(players) do + if v == player_name then + table.remove(players, i) + last_wielded[player_name] = nil + -- Neuberechnung des Lichts erzwingen + local pos = player:getpos() + local rounded_pos = {x=round(pos.x),y=round(pos.y)+1,z=round(pos.z)} + minetest.env:add_node(rounded_pos,{type="node",name="default:cobble"}) + minetest.env:add_node(rounded_pos,{type="node",name="air"}) + player_positions[player_name]["x"] = nil + player_positions[player_name]["y"] = nil + player_positions[player_name]["z"] = nil + player_positions[player_name]["m"] = nil + player_positions[player_name] = nil + end + end +end) + +minetest.register_globalstep(function(dtime) + for i,player_name in ipairs(players) do + local player = minetest.env:get_player_by_name(player_name) + local wielded_item = player:get_wielded_item():get_name() + if wielded_item == "default:torch" or wielded_item == "walking_light:pick_mese" then + -- Fackel ist in der Hand + local pos = player:getpos() + local rounded_pos = {x=round(pos.x),y=round(pos.y)+1,z=round(pos.z)} + if (last_wielded[player_name] ~= "default:torch" and last_wielded[player_name] ~= "walking_light:pick_mese") or (player_positions[player_name]["x"] ~= rounded_pos.x or player_positions[player_name]["y"] ~= rounded_pos.y or player_positions[player_name]["z"] ~= rounded_pos.z) then + -- Fackel gerade in die Hand genommen oder zu neuem Node bewegt + local is_air = minetest.env:get_node_or_nil(rounded_pos) + if is_air == nil or (is_air ~= nil and (is_air.name == "air" or is_air.name == "walking_light:light")) then + -- wenn an aktueller Position "air" ist, Fackellicht setzen + minetest.env:add_node(rounded_pos,{type="node",name="walking_light:light"}) + end + if (player_positions[player_name]["x"] ~= rounded_pos.x or player_positions[player_name]["y"] ~= rounded_pos.y or player_positions[player_name]["z"] ~= rounded_pos.z) then + -- wenn Position geänder, dann altes Licht löschen + local old_pos = {x=player_positions[player_name]["x"], y=player_positions[player_name]["y"], z=player_positions[player_name]["z"]} + -- Neuberechnung des Lichts erzwingen + local is_light = minetest.env:get_node_or_nil(old_pos) + if is_light ~= nil and is_light.name == "walking_light:light" then + minetest.env:add_node(old_pos,{type="node",name="default:cobble"}) + minetest.env:add_node(old_pos,{type="node",name="air"}) + end + end + -- gemerkte Position ist nun die gerundete neue Position + player_positions[player_name]["x"] = rounded_pos.x + player_positions[player_name]["y"] = rounded_pos.y + player_positions[player_name]["z"] = rounded_pos.z + end + + last_wielded[player_name] = wielded_item; + elseif last_wielded[player_name] == "default:torch" or last_wielded[player_name] == "walking_light:pick_mese" then + -- Fackel nicht in der Hand, aber beim letzten Durchgang war die Fackel noch in der Hand + local pos = player:getpos() + local rounded_pos = {x=round(pos.x),y=round(pos.y)+1,z=round(pos.z)} + repeat + local is_light = minetest.env:get_node_or_nil(rounded_pos) + if is_light ~= nil and is_light.name == "walking_light:light" then + -- minetest.env:remove_node(rounded_pos) + -- Erzwinge Neuberechnung des Lichts + minetest.env:add_node(rounded_pos,{type="node",name="default:cobble"}) + minetest.env:add_node(rounded_pos,{type="node",name="air"}) + end + until minetest.env:get_node_or_nil(rounded_pos) ~= "walking_light:light" + local old_pos = {x=player_positions[player_name]["x"], y=player_positions[player_name]["y"], z=player_positions[player_name]["z"]} + repeat + is_light = minetest.env:get_node_or_nil(old_pos) + if is_light ~= nil and is_light.name == "walking_light:light" then + -- minetest.env:remove_node(old_pos) + -- Erzwinge Neuberechnung des Lichts + minetest.env:add_node(old_pos,{type="node",name="default:cobble"}) + minetest.env:add_node(old_pos,{type="node",name="air"}) + end + until minetest.env:get_node_or_nil(old_pos) ~= "walking_light:light" + last_wielded[player_name] = wielded_item + end + end +end) + +minetest.register_node("walking_light:light", { + drawtype = "glasslike", + tile_images = {"walking_light.png"}, + -- tile_images = {"walking_light_debug.png"}, + inventory_image = minetest.inventorycube("walking_light.png"), + paramtype = "light", + walkable = false, + is_ground_content = true, + light_propagates = true, + sunlight_propagates = true, + light_source = 13, + selection_box = { + type = "fixed", + fixed = {0, 0, 0, 0, 0, 0}, + }, +}) +minetest.register_tool("walking_light:pick_mese", { + description = "Mese Pickaxe with light", + inventory_image = "walking_light_mesepick.png", + wield_image = "default_tool_mesepick.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=3, + groupcaps={ + cracky={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}, + crumbly={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}, + snappy={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3} + } + }, +}) + +minetest.register_craft({ + output = 'walking_light:pick_mese', + recipe = { + {'default:torch'}, + {'default:pick_mese'}, + } +}) \ No newline at end of file diff --git a/mods/walking_light/textures/walking_light.png b/mods/walking_light/textures/walking_light.png new file mode 100644 index 0000000..334cb07 Binary files /dev/null and b/mods/walking_light/textures/walking_light.png differ diff --git a/mods/walking_light/textures/walking_light_debug.png b/mods/walking_light/textures/walking_light_debug.png new file mode 100644 index 0000000..43e41bd Binary files /dev/null and b/mods/walking_light/textures/walking_light_debug.png differ diff --git a/mods/walking_light/textures/walking_light_mesepick.png b/mods/walking_light/textures/walking_light_mesepick.png new file mode 100644 index 0000000..9f98229 Binary files /dev/null and b/mods/walking_light/textures/walking_light_mesepick.png differ