Mods update
|
@ -112,11 +112,11 @@ origin https://github.com/TheTermos/mobkit (fetch)
|
|||
Mod: lib_api/mobkit
|
||||
|
||||
origin https://notabug.org/tenplus1/mobs_redo (fetch)
|
||||
* master c158e84 [origin/master] stop grown child mobs sinking into blocks below
|
||||
* master 11e1d52 [origin/master] replace minetest 5.0 check
|
||||
Mod: lib_api/mobs_redo
|
||||
|
||||
origin https://github.com/appgurueu/modlib (fetch)
|
||||
* master b9483fb [origin/master] Add objects_inside_- radius and area iterators
|
||||
* master ba4a6ab [origin/master] b3d test default false
|
||||
Mod: lib_api/modlib
|
||||
|
||||
origin git@github.com:runsy/rcbows.git (fetch)
|
||||
|
@ -128,7 +128,7 @@ origin git@github.com:minetest-mods/ts_workshop.git (fetch)
|
|||
Mod: lib_api/ts_workshop
|
||||
|
||||
origin https://github.com/Skandarella/Animal-World.git (fetch)
|
||||
* main 75dfe53 [origin/main] Delete test.txt
|
||||
* main f45ee6f [origin/main] Add files via upload
|
||||
Mod: mobs/mobs_mobs/Animal-World
|
||||
|
||||
origin https://github.com/FreeLikeGNU/goblins.git (fetch)
|
||||
|
@ -152,15 +152,15 @@ origin https://github.com/berengma/aerotest (fetch)
|
|||
Mod: mobs/mobs_mobkit/aerotest
|
||||
|
||||
origin https://github.com/runsy/petz (fetch)
|
||||
* master 2080c8f [origin/master] pony cimarron
|
||||
* master 84e8b8f [origin/master] frogs improved
|
||||
Mod: mobs/mobs_mobkit/petz
|
||||
|
||||
origin https://github.com/berengma/water_life (fetch)
|
||||
* master 17c4289 [origin/master] Merge pull request #69 from berengma/staticIsTrue
|
||||
* master 6b4ad15 [origin/master] Merge pull request #72 from berengma/dev
|
||||
Mod: mobs/mobs_mobkit/water_life
|
||||
|
||||
origin https://github.com/minetest-mods/3d_armor (fetch)
|
||||
* master e7abacc [origin/master] Add armor set and armor set bonus setting (#41)
|
||||
* master 42f7dac [origin/master] Update fire and water protection (#42)
|
||||
Mod: player/3d_armor
|
||||
|
||||
origin https://github.com/appgurueu/character_anim (fetch)
|
||||
|
@ -191,8 +191,12 @@ origin https://github.com/minetest-mods/wielded_light.git (fetch)
|
|||
* master 60b31d3 [origin/master] fix mt-5.4 compatibility Closes #7
|
||||
Mod: player/wielded_light
|
||||
|
||||
origin https://notabug.org/TenPlus1/bonemeal.git (fetch)
|
||||
* master 308baf3 [origin/master] update mod.conf info
|
||||
Mod: tools/bonemeal
|
||||
|
||||
origin git@gitlab.com:daretmavi/bucket-lite.git (fetch)
|
||||
* master 03a1ebf [origin/master] Merge branch 'devel' into 'master'
|
||||
* master 5075acb [origin/master] Merge branch 'devel' into 'master'
|
||||
Mod: tools/bucket-lite
|
||||
|
||||
origin git@github.com:runsy/farbows.git (fetch)
|
||||
|
|
|
@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi")
|
|||
|
||||
mobs = {
|
||||
mod = "redo",
|
||||
version = "20210203",
|
||||
version = "20210206",
|
||||
intllib = S,
|
||||
invis = minetest.global_exists("invisibility") and invisibility or {}
|
||||
}
|
||||
|
@ -539,12 +539,10 @@ local ray_line_of_sight = function(self, pos1, pos2)
|
|||
return true
|
||||
end
|
||||
|
||||
-- detect if using minetest 5.0 by searching for permafrost node
|
||||
local is_50 = minetest.registered_nodes["default:permafrost"]
|
||||
|
||||
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
||||
|
||||
if is_50 then -- only use if minetest 5.0 is detected
|
||||
if minetest.raycast then -- only use if minetest 5.0 is detected
|
||||
return ray_line_of_sight(self, pos1, pos2)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,378 @@
|
|||
local metatable = {__index = getfenv(1)}
|
||||
|
||||
--! experimental
|
||||
--+ Reads a single BB3D chunk from a stream
|
||||
--+ Doing `assert(stream:read(1) == nil)` afterwards is recommended
|
||||
--+ See `b3d_specification.txt` as well as https://github.com/blitz-research/blitz3d/blob/master/blitz3d/loader_b3d.cpp
|
||||
--> B3D model
|
||||
function read(stream)
|
||||
local left = 8
|
||||
|
||||
local function byte()
|
||||
left = left - 1
|
||||
return assert(stream:read(1):byte())
|
||||
end
|
||||
|
||||
local function int()
|
||||
local value = byte() + byte() * 0x100 + byte() * 0x10000 + byte() * 0x1000000
|
||||
if value >= 2^31 then
|
||||
return value - 2^32
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function id()
|
||||
return int() + 1
|
||||
end
|
||||
|
||||
local function optional_id()
|
||||
local id = int()
|
||||
if id == -1 then
|
||||
return
|
||||
end
|
||||
return id + 1
|
||||
end
|
||||
|
||||
local function string()
|
||||
local rope = {}
|
||||
while true do
|
||||
left = left - 1
|
||||
local char = assert(stream:read(1))
|
||||
if char == "\0" then
|
||||
return table.concat(rope)
|
||||
end
|
||||
table.insert(rope, char)
|
||||
end
|
||||
end
|
||||
|
||||
local function float()
|
||||
-- TODO properly truncate to single floating point
|
||||
local byte_4, byte_3, byte_2, byte_1 = byte(), byte(), byte(), byte()
|
||||
local sign = 1
|
||||
if byte_1 >= 0x80 then
|
||||
sign = -1
|
||||
byte_1 = byte_1 - 0x80
|
||||
end
|
||||
local exponent = byte_1 * 2
|
||||
if byte_2 >= 0x80 then
|
||||
byte_2 = byte_2 - 0x80
|
||||
exponent = exponent + 1
|
||||
end
|
||||
local mantissa = ((((byte_4 / 0x100) + byte_3) / 0x100) + byte_2) / 0x80
|
||||
if exponent == 0xFF then
|
||||
if mantissa == 0 then
|
||||
return sign * math.huge
|
||||
end
|
||||
-- TODO differentiate quiet and signalling NaN as well as positive and negative
|
||||
return 0/0
|
||||
end
|
||||
if exponent == 0 then
|
||||
-- subnormal value
|
||||
return sign * 2^-126 * mantissa
|
||||
end
|
||||
return sign * 2 ^ (exponent - 127) * (1 + mantissa)
|
||||
end
|
||||
|
||||
local function float_array(length)
|
||||
local list = {}
|
||||
for index = 1, length do
|
||||
list[index] = float()
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local function color()
|
||||
return {
|
||||
r = float(),
|
||||
g = float(),
|
||||
b = float(),
|
||||
a = float()
|
||||
}
|
||||
end
|
||||
|
||||
local function vector3()
|
||||
return float_array(3)
|
||||
end
|
||||
|
||||
local function quaternion()
|
||||
return {[4] = float(), [1] = float(), [2] = float(), [3] = float()}
|
||||
end
|
||||
|
||||
local function content()
|
||||
assert(left >= 0, stream:seek())
|
||||
return left ~= 0
|
||||
end
|
||||
|
||||
local chunk
|
||||
local chunks = {
|
||||
TEXS = function()
|
||||
local textures = {}
|
||||
while content() do
|
||||
table.insert(textures, {
|
||||
file = string(),
|
||||
flags = int(),
|
||||
blend = int(),
|
||||
pos = float_array(2),
|
||||
scale = float_array(2),
|
||||
rotation = float()
|
||||
})
|
||||
end
|
||||
return textures
|
||||
end,
|
||||
BRUS = function()
|
||||
local brushes = {}
|
||||
brushes.n_texs = int()
|
||||
assert(brushes.n_texs <= 8)
|
||||
while content() do
|
||||
local brush = {
|
||||
name = string(),
|
||||
color = color(),
|
||||
shininess = float(),
|
||||
blend = float(),
|
||||
fx = float(),
|
||||
texture_id = {}
|
||||
}
|
||||
for index = 1, brushes.n_texs do
|
||||
brush.texture_id[index] = optional_id()
|
||||
end
|
||||
table.insert(brushes, brush)
|
||||
end
|
||||
return brushes
|
||||
end,
|
||||
VRTS = function()
|
||||
local vertices = {
|
||||
flags = int(),
|
||||
tex_coord_sets = int(),
|
||||
tex_coord_set_size = int()
|
||||
}
|
||||
assert(vertices.tex_coord_sets <= 8 and vertices.tex_coord_set_size <= 4)
|
||||
local has_normal = (vertices.flags % 2 == 1) or nil
|
||||
local has_color = (math.floor(vertices.flags / 2) % 2 == 1) or nil
|
||||
while content() do
|
||||
local vertex = {
|
||||
pos = vector3(),
|
||||
normal = has_normal and vector3(),
|
||||
color = has_color and color(),
|
||||
tex_coords = {}
|
||||
}
|
||||
for tex_coord_set = 1, vertices.tex_coord_sets do
|
||||
local tex_coords = {}
|
||||
for tex_coord = 1, vertices.tex_coord_set_size do
|
||||
tex_coords[tex_coord] = float()
|
||||
end
|
||||
vertex.tex_coords[tex_coord_set] = tex_coords
|
||||
end
|
||||
table.insert(vertices, vertex)
|
||||
end
|
||||
return vertices
|
||||
end,
|
||||
TRIS = function()
|
||||
local tris = {
|
||||
brush_id = id(),
|
||||
vertex_ids = {}
|
||||
}
|
||||
while content() do
|
||||
table.insert(tris.vertex_ids, {id(), id(), id()})
|
||||
end
|
||||
return tris
|
||||
end,
|
||||
MESH = function()
|
||||
local mesh = {
|
||||
brush_id = optional_id(),
|
||||
vertices = chunk{VRTS = true}
|
||||
}
|
||||
mesh.triangle_sets = {}
|
||||
repeat
|
||||
local tris = chunk{TRIS = true}
|
||||
table.insert(mesh.triangle_sets, tris)
|
||||
until not content()
|
||||
return mesh
|
||||
end,
|
||||
BONE = function()
|
||||
local bone = {}
|
||||
while content() do
|
||||
local vertex_id = id()
|
||||
assert(not bone[vertex_id], "duplicate vertex weight")
|
||||
local weight = float()
|
||||
if weight > 0 then
|
||||
-- Many exporters include unneeded zero weights
|
||||
bone[vertex_id] = weight
|
||||
end
|
||||
end
|
||||
return bone
|
||||
end,
|
||||
KEYS = function()
|
||||
local flags = int()
|
||||
local _flags = flags % 8
|
||||
local rotation, scale, position
|
||||
if _flags >= 4 then
|
||||
rotation = true
|
||||
_flags = _flags - 4
|
||||
end
|
||||
if _flags >= 2 then
|
||||
scale = true
|
||||
_flags = _flags - 2
|
||||
end
|
||||
position = _flags >= 1
|
||||
local bone = {
|
||||
flags = flags
|
||||
}
|
||||
while content() do
|
||||
table.insert(bone, {
|
||||
frame = int(),
|
||||
position = position and vector3() or nil,
|
||||
scale = scale and vector3() or nil,
|
||||
rotation = rotation and quaternion() or nil
|
||||
})
|
||||
end
|
||||
-- Ensure frames are sorted ascending
|
||||
table.sort(bone, function(a, b) return a.frame < b.frame end)
|
||||
return bone
|
||||
end,
|
||||
ANIM = function()
|
||||
return {
|
||||
-- flags are unused
|
||||
flags = int(),
|
||||
frames = int(),
|
||||
fps = float()
|
||||
}
|
||||
end,
|
||||
NODE = function()
|
||||
local node = {
|
||||
name = string(),
|
||||
position = vector3(),
|
||||
scale = vector3(),
|
||||
keys = {},
|
||||
rotation = quaternion(),
|
||||
children = {}
|
||||
}
|
||||
local node_type
|
||||
-- See https://github.com/blitz-research/blitz3d/blob/master/blitz3d/loader_b3d.cpp#L263
|
||||
-- Order is not validated; double occurences of mutually exclusive node def are
|
||||
while content() do
|
||||
local elem, type = chunk()
|
||||
if type == "MESH" then
|
||||
assert(not node_type)
|
||||
node_type = "mesh"
|
||||
node.mesh = elem
|
||||
elseif type == "BONE" then
|
||||
assert(not node_type)
|
||||
node_type = "bone"
|
||||
node.bone = elem
|
||||
elseif type == "KEYS" then
|
||||
assert((node.keys[#node.keys] or {}).frame ~= (elem[1] or {}).frame, "duplicate frame")
|
||||
modlib.table.append(node.keys, elem)
|
||||
elseif type == "NODE" then
|
||||
table.insert(node.children, elem)
|
||||
elseif type == "ANIM" then
|
||||
node.animation = elem
|
||||
else
|
||||
assert(not node_type)
|
||||
node_type = "pivot"
|
||||
end
|
||||
end
|
||||
-- TODO somehow merge keys
|
||||
return node
|
||||
end,
|
||||
BB3D = function()
|
||||
local version = int()
|
||||
local self = {
|
||||
version = {
|
||||
major = math.floor(version / 100),
|
||||
minor = version % 100,
|
||||
raw = version
|
||||
},
|
||||
textures = {},
|
||||
brushes = {}
|
||||
}
|
||||
assert(self.version.major <= 2, "unsupported version: " .. self.version.major)
|
||||
while content() do
|
||||
local field, type = chunk{TEXS = true, BRUS = true, NODE = true}
|
||||
if type == "TEXS" then
|
||||
modlib.table.append(self.textures, field)
|
||||
elseif type == "BRUS" then
|
||||
modlib.table.append(self.brushes, field)
|
||||
else
|
||||
self.node = field
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
}
|
||||
|
||||
local function chunk_header()
|
||||
left = left - 4
|
||||
return stream:read(4), int()
|
||||
end
|
||||
|
||||
function chunk(possible_chunks)
|
||||
local type, new_left = chunk_header()
|
||||
local parent_left
|
||||
left, parent_left = new_left, left
|
||||
if possible_chunks and not possible_chunks[type] then
|
||||
error("expected one of " .. table.concat(modlib.table.keys(possible_chunks), ", ") .. ", found " .. type)
|
||||
end
|
||||
local res = assert(chunks[type])()
|
||||
assert(left == 0)
|
||||
left = parent_left - new_left
|
||||
return res, type
|
||||
end
|
||||
|
||||
local self = chunk{BB3D = true}
|
||||
return setmetatable(self, metatable)
|
||||
end
|
||||
|
||||
-- TODO function write(self, stream)
|
||||
|
||||
local binary_search_frame = modlib.table.binary_search_comparator(function(a, b)
|
||||
return modlib.table.default_comparator(a, b.frame)
|
||||
end)
|
||||
|
||||
--> [bonename] = { position = vector, rotation = quaternion, scale = vector }
|
||||
function get_animated_bone_properties(self, keyframe, interpolate)
|
||||
local function get_frame_values(keys)
|
||||
local values = keys[keyframe]
|
||||
if values and values.frame == keyframe then
|
||||
return {
|
||||
position = values.position,
|
||||
rotation = values.rotation,
|
||||
scale = values.scale
|
||||
}
|
||||
end
|
||||
local index = binary_search_frame(keys, keyframe)
|
||||
if index > 0 then
|
||||
return keys[index]
|
||||
end
|
||||
index = -index
|
||||
assert(index > 1 and index <= #keys)
|
||||
local a, b = keys[index - 1], keys[index]
|
||||
if not interpolate then
|
||||
return a
|
||||
end
|
||||
local ratio = (keyframe - a.frame) / (b.frame - a.frame)
|
||||
return {
|
||||
position = (a.position and b.position and modlib.vector.interpolate(a.position, b.position, ratio)) or a.position or b.position,
|
||||
rotation = (a.rotation and b.rotation and modlib.quaternion.interpolate(a.rotation, b.rotation, ratio)) or a.rotation or b.rotation,
|
||||
scale = (a.scale and b.scale and modlib.vector.interpolate(a.scale, b.scale, ratio)) or a.scale or b.scale,
|
||||
}
|
||||
end
|
||||
local bone_properties = {}
|
||||
local function get_props(node)
|
||||
local properties = {}
|
||||
if node.keys and next(node.keys) ~= nil then
|
||||
properties = modlib.table.add_all(properties, get_frame_values(node.keys))
|
||||
end
|
||||
for _, property in pairs{"position", "rotation", "scale"} do
|
||||
properties[property] = properties[property] or modlib.table.copy(node[property])
|
||||
end
|
||||
if node.bone then
|
||||
assert(not bone_properties[node.name])
|
||||
bone_properties[node.name] = properties
|
||||
end
|
||||
for _, child in pairs(node.children or {}) do
|
||||
get_props(child)
|
||||
end
|
||||
end
|
||||
get_props(self.node)
|
||||
return bone_properties
|
||||
end
|
|
@ -0,0 +1,260 @@
|
|||
************************************************************************************
|
||||
* Blitz3d file format V0.01 *
|
||||
************************************************************************************
|
||||
|
||||
This document and the information contained within is placed in the Public Domain.
|
||||
|
||||
Please visit http://www.blitzbasic.co.nz for the latest version of this document.
|
||||
|
||||
Please contact marksibly@blitzbasic.co.nz for more information and general inquiries.
|
||||
|
||||
|
||||
|
||||
************************************************************************************
|
||||
* Introduction *
|
||||
************************************************************************************
|
||||
|
||||
The Blitz3D file format specifies a format for storing texture, brush and entity descriptions for
|
||||
use with the Blitz3D programming language.
|
||||
|
||||
The rationale behind the creation of this format is to allow for the generation of much richer and
|
||||
more complex Blitz3D scenes than is possible using established file formats - many of which do not
|
||||
support key features of Blitz3D, and all of which miss out on at least some features!
|
||||
|
||||
A Blitz3D (.b3d) file is split up into a sequence of 'chunks', each of which can contain data
|
||||
and/or other chunks.
|
||||
|
||||
Each chunk is preceded by an eight byte header:
|
||||
|
||||
char tag[4] ;4 byte chunk 'tag'
|
||||
int length ;4 byte chunk length (not including *this* header!)
|
||||
|
||||
If a chunk contains both data and other chunks, the data always appears first and is of a fixed
|
||||
length.
|
||||
|
||||
A file parser should ignore unrecognized chunks.
|
||||
|
||||
Blitz3D files are stored little endian (intel) style.
|
||||
|
||||
Many aspects of the file format are not quite a 'perfect fit' for the way Blitz3D works. This has
|
||||
been done mainly to keep the file format simple, and to make life easier for the authors of third
|
||||
party importers/exporters.
|
||||
|
||||
|
||||
|
||||
************************************************************************************
|
||||
* Chunk Types *
|
||||
************************************************************************************
|
||||
|
||||
This lists the types of chunks that can appear in a b3d file, and the data they contain.
|
||||
|
||||
Color values are always in the range 0 to 1.
|
||||
|
||||
string (char[]) values are 'C' style null terminated strings.
|
||||
|
||||
Quaternions are used to specify general orientations. The first value is the quaternion 'w' value,
|
||||
the next 3 are the quaternion 'vector'. A 'null' rotation should be specified as 1,0,0,0.
|
||||
|
||||
Anything that is referenced 'by index' always appears EARLIER in the file than anything that
|
||||
references it.
|
||||
|
||||
brush_id references can be -1: no brush.
|
||||
|
||||
In the following descriptions, {} is used to signify 'repeating until end of chunk'. Also, a chunk
|
||||
name enclosed in '[]' signifies the chunk is optional.
|
||||
|
||||
Here we go!
|
||||
|
||||
|
||||
BB3D
|
||||
int version ;file format version: default=1
|
||||
[TEXS] ;optional textures chunk
|
||||
[BRUS] ;optional brushes chunk
|
||||
[NODE] ;optional node chunk
|
||||
|
||||
The BB3D chunk appears first in a b3d file, and its length contains the rest of the file.
|
||||
|
||||
Version is in major*100+minor format. To check the version, just divide by 100 and compare it with
|
||||
the major version your software supports, eg:
|
||||
|
||||
if file_version/100>my_version/100
|
||||
RuntimeError "Can't handle this file version!"
|
||||
EndIf
|
||||
|
||||
if file_version Mod 100>my_version Mod 100
|
||||
;file is a more recent version, but should still be backwardly compatbile with what we can
|
||||
handle!
|
||||
EndIf
|
||||
|
||||
|
||||
TEXS
|
||||
{
|
||||
char file[] ;texture file name
|
||||
int flags,blend ;blitz3D TextureFLags and TextureBlend: default=1,2
|
||||
float x_pos,y_pos ;x and y position of texture: default=0,0
|
||||
float x_scale,y_scale ;x and y scale of texture: default=1,1
|
||||
float rotation ;rotation of texture (in radians): default=0
|
||||
}
|
||||
|
||||
The TEXS chunk contains a list of all textures used in the file.
|
||||
|
||||
The flags field value can conditional an additional flag value of '65536'. This is used to indicate that the texture uses secondary UV values, ala the TextureCoords command. Yes, I forgot about this one.
|
||||
|
||||
|
||||
BRUS
|
||||
int n_texs
|
||||
{
|
||||
char name[] ;eg "WATER" - just use texture name by default
|
||||
float red,green,blue,alpha ;Blitz3D Brushcolor and Brushalpha: default=1,1,1,1
|
||||
float shininess ;Blitz3D BrushShininess: default=0
|
||||
int blend,fx ;Blitz3D Brushblend and BrushFX: default=1,0
|
||||
int texture_id[n_texs] ;textures used in brush
|
||||
}
|
||||
|
||||
The BRUS chunk contains a list of all brushes used in the file.
|
||||
|
||||
|
||||
VRTS:
|
||||
int flags ;1=normal values present, 2=rgba values present
|
||||
int tex_coord_sets ;texture coords per vertex (eg: 1 for simple U/V) max=8
|
||||
int tex_coord_set_size ;components per set (eg: 2 for simple U/V) max=4
|
||||
{
|
||||
float x,y,z ;always present
|
||||
float nx,ny,nz ;vertex normal: present if (flags&1)
|
||||
float red,green,blue,alpha ;vertex color: present if (flags&2)
|
||||
float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords
|
||||
}
|
||||
|
||||
The VRTS chunk contains a list of vertices. The 'flags' value is used to indicate how much extra
|
||||
data (normal/color) is stored with each vertex, and the tex_coord_sets and tex_coord_set_size
|
||||
values describe texture coordinate information stored with each vertex.
|
||||
|
||||
|
||||
TRIS:
|
||||
int brush_id ;brush applied to these TRIs: default=-1
|
||||
{
|
||||
int vertex_id[3] ;vertex indices
|
||||
}
|
||||
|
||||
The TRIS chunk contains a list of triangles that all share a common brush.
|
||||
|
||||
|
||||
MESH:
|
||||
int brush_id ;'master' brush: default=-1
|
||||
VRTS ;vertices
|
||||
TRIS[,TRIS...] ;1 or more sets of triangles
|
||||
|
||||
The MESH chunk describes a mesh. A mesh only has one VRTS chunk, but potentially many TRIS chunks.
|
||||
|
||||
|
||||
BONE:
|
||||
{
|
||||
int vertex_id ;vertex affected by this bone
|
||||
float weight ;how much the vertex is affected
|
||||
}
|
||||
|
||||
The BONE chunk describes a bone. Weights are applied to the mesh described in the enclosing ANIM -
|
||||
in 99% of cases, this will simply be the MESH contained in the root NODE chunk.
|
||||
|
||||
|
||||
KEYS:
|
||||
int flags ;1=position, 2=scale, 4=rotation
|
||||
{
|
||||
int frame ;where key occurs
|
||||
float position[3] ;present if (flags&1)
|
||||
float scale[3] ;present if (flags&2)
|
||||
float rotation[4] ;present if (flags&4)
|
||||
}
|
||||
|
||||
The KEYS chunk is a list of animation keys. The 'flags' value describes what kind of animation
|
||||
info is stored in the chunk - position, scale, rotation, or any combination of.
|
||||
|
||||
|
||||
ANIM:
|
||||
int flags ;unused: default=0
|
||||
int frames ;how many frames in anim
|
||||
float fps ;default=60
|
||||
|
||||
The ANIM chunk describes an animation.
|
||||
|
||||
|
||||
NODE:
|
||||
char name[] ;name of node
|
||||
float position[3] ;local...
|
||||
float scale[3] ;coord...
|
||||
float rotation[4] ;system...
|
||||
[MESH|BONE] ;what 'kind' of node this is - if unrecognized, just use a Blitz3D
|
||||
pivot.
|
||||
[KEYS[,KEYS...]] ;optional animation keys
|
||||
[NODE[,NODE...]] ;optional child nodes
|
||||
[ANIM] ;optional animation
|
||||
|
||||
The NODE chunk describes a Blitz3D Entity. The scene hierarchy is expressed by the nesting of NODE
|
||||
chunks.
|
||||
|
||||
NODE kinds are currently mutually exclusive - ie: a node can be a MESH, or a BONE, but not both!
|
||||
However, it can be neither...if no kind is specified, the node is just a 'null' node - in Blitz3D
|
||||
speak, a pivot.
|
||||
|
||||
The presence of an ANIM chunk in a NODE indicates that an animation starts here in the hierarchy.
|
||||
This allows animations of differing speeds/lengths to be potentially nested.
|
||||
|
||||
There are many more 'kind' chunks coming, including camera, light, sprite, plane etc. For now, the
|
||||
use of a Pivot in cases where the node kind is unknown will allow for backward compatibility.
|
||||
|
||||
|
||||
|
||||
************************************************************************************
|
||||
* Examples *
|
||||
************************************************************************************
|
||||
|
||||
A typical b3d file will contain 1 TEXS chunk, 1 BRUS chunk and 1 NODE chunk, like this:
|
||||
|
||||
BB3D
|
||||
1
|
||||
TEXS
|
||||
...list of textures...
|
||||
BRUS
|
||||
...list of brushes...
|
||||
NODE
|
||||
...stuff in the node...
|
||||
|
||||
A simple, non-animating, non-textured etc mesh might look like this:
|
||||
|
||||
BB3D
|
||||
1 ;version
|
||||
NODE
|
||||
"root_node" ;node name
|
||||
0,0,0 ;position
|
||||
1,1,1 ;scale
|
||||
1,0,0,0 ;rotation
|
||||
MESH ;the mesh
|
||||
-1 ;brush: no brush
|
||||
VRTS ;vertices in the mesh
|
||||
0 ;no normal/color info in verts
|
||||
0,0 ;no texture coords in verts
|
||||
{x,y,z...} ;vertex coordinates
|
||||
TRIS ;triangles in the mesh
|
||||
-1 ;no brush for this triangle
|
||||
{v0,v1,v2...} ;vertices
|
||||
|
||||
|
||||
A more complex 'skinned mesh' might look like this (only chunks shown):
|
||||
|
||||
BB3D
|
||||
TEXS ;texture list
|
||||
BRUS ;brush list
|
||||
NODE ;root node
|
||||
MESH ;mesh - the 'skin'
|
||||
ANIM ;anim
|
||||
NODE ;first child of root node - eg: "pelvis"
|
||||
BONE ;vertex weights for pelvis
|
||||
KEYS ;anim keys for pelvis
|
||||
NODE ;first child of pelvis - eg: "left-thigh"
|
||||
BONE ;bone
|
||||
KEYS ;anim keys for left-thigh
|
||||
NODE ;second child of pelvis - eg: "right-thigh"
|
||||
BONE ;vertex weights for right-thigh
|
||||
KEYS ;anim keys for right-thigh
|
||||
|
||||
...and so on.
|
|
@ -34,12 +34,24 @@ if _VERSION then
|
|||
end
|
||||
end
|
||||
|
||||
local function get_resource(modname, resource)
|
||||
modlib = {
|
||||
dir_delim = rawget(_G, "DIR_DELIM") or "/",
|
||||
_RG = setmetatable({}, {
|
||||
__index = function(_, index)
|
||||
return rawget(_G, index)
|
||||
end,
|
||||
__newindex = function(_, index, value)
|
||||
return rawset(_G, index, value)
|
||||
end
|
||||
})
|
||||
}
|
||||
|
||||
local function get_resource(modname, resource, ...)
|
||||
if not resource then
|
||||
resource = modname
|
||||
modname = minetest.get_current_modname()
|
||||
end
|
||||
return minetest.get_modpath(modname) .. "/" .. resource
|
||||
return table.concat({minetest.get_modpath(modname), resource, ...}, modlib.dir_delim)
|
||||
end
|
||||
|
||||
local function loadfile_exports(filename)
|
||||
|
@ -50,15 +62,6 @@ local function loadfile_exports(filename)
|
|||
return env
|
||||
end
|
||||
|
||||
modlib = {_RG = setmetatable({}, {
|
||||
__index = function(_, index)
|
||||
return rawget(_G, index)
|
||||
end,
|
||||
__newindex = function(_, index, value)
|
||||
return rawset(_G, index, value)
|
||||
end
|
||||
})}
|
||||
|
||||
for _, component in ipairs{
|
||||
"mod",
|
||||
"conf",
|
||||
|
@ -72,10 +75,13 @@ for _, component in ipairs{
|
|||
"table",
|
||||
"text",
|
||||
"vector",
|
||||
"quaternion",
|
||||
"minetest",
|
||||
"trie",
|
||||
"kdtree",
|
||||
"heap",
|
||||
"ranked_set"
|
||||
"ranked_set",
|
||||
"b3d"
|
||||
} do
|
||||
modlib[component] = loadfile_exports(get_resource(component .. ".lua"))
|
||||
end
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
local metatable = {__index = getfenv(1)}
|
||||
|
||||
distance = modlib.vector.distance
|
||||
|
||||
--: vectors first vector is used to infer the dimension
|
||||
--: distance (vector, other_vector) -> number, default: modlib.vector.distance
|
||||
function new(vectors, distance)
|
||||
assert(#vectors > 0, "vector list must not be empty")
|
||||
local dimension = #vectors[1]
|
||||
local function builder(vectors, axis)
|
||||
if #vectors == 1 then return { value = vectors[1] } end
|
||||
table.sort(vectors, function(a, b) return a[axis] > b[axis] end)
|
||||
local median = math.floor(#vectors / 2)
|
||||
local next_axis = ((axis + 1) % dimension) + 1
|
||||
return setmetatable({
|
||||
axis = axis,
|
||||
pivot = vectors[median],
|
||||
left = builder({ unpack(vectors, 1, median) }, next_axis),
|
||||
right = builder({ unpack(vectors, median + 1) }, next_axis)
|
||||
}, metatable)
|
||||
end
|
||||
local self = builder(vectors, 1)
|
||||
self.distance = distance
|
||||
return setmetatable(self, metatable)
|
||||
end
|
||||
|
||||
function get_nearest_neighbor(self, vector)
|
||||
local min_distance = math.huge
|
||||
local nearest_neighbor
|
||||
local distance_func = self.distance
|
||||
local function visit(tree)
|
||||
local axis = tree.axis
|
||||
if tree.value ~= nil then
|
||||
local distance = distance_func(tree.value, vector)
|
||||
if distance < min_distance then
|
||||
min_distance = distance
|
||||
nearest_neighbor = tree.value
|
||||
end
|
||||
return
|
||||
else
|
||||
local this_side, other_side = tree.left, tree.right
|
||||
if vector[axis] < tree.pivot[axis] then this_side, other_side = other_side, this_side end
|
||||
visit(this_side)
|
||||
if tree.pivot then
|
||||
local dist = math.abs(tree.pivot[axis] - vector[axis])
|
||||
if dist <= min_distance then visit(other_side) end
|
||||
end
|
||||
end
|
||||
end
|
||||
visit(self)
|
||||
return nearest_neighbor, min_distance
|
||||
end
|
||||
|
||||
-- TODO insertion & deletion + rebalancing
|
|
@ -42,12 +42,13 @@ function init(modname)
|
|||
extend(modname, "main")
|
||||
end
|
||||
|
||||
--! deprecated
|
||||
function extend_string(modname, string)
|
||||
if not string then
|
||||
string = modname
|
||||
modname = minetest.get_current_modname()
|
||||
end
|
||||
include_env(string, _G[modname], true)
|
||||
include_env(string, rawget(_G, modname), true)
|
||||
end
|
||||
|
||||
function configuration(modname)
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
-- TODO OOP, extend vector
|
||||
|
||||
function from_euler_rotation(rotation)
|
||||
rotation = vector.divide(rotation, 2)
|
||||
local cos = vector.apply(rotation, math.cos)
|
||||
local sin = vector.apply(rotation, math.sin)
|
||||
return {
|
||||
sin.z * cos.x * cos.y - cos.z * sin.x * sin.y,
|
||||
cos.z * sin.x * cos.y + sin.z * cos.x * sin.y,
|
||||
cos.z * cos.x * sin.y - sin.z * sin.x * cos.y,
|
||||
cos.z * cos.x * cos.y + sin.z * sin.x * sin.y
|
||||
}
|
||||
end
|
||||
|
||||
function multiply(self, other)
|
||||
return {
|
||||
other[1] * self[1] - other[2] * self[2] - other[3] * self[3] - other[4] * self[4],
|
||||
other[1] * self[2] + other[2] * self[1] - other[3] * self[4] + other[4] * self[3],
|
||||
other[1] * self[3] + other[2] * self[4] + other[3] * self[1] - other[4] * self[2],
|
||||
other[1] * self[4] - other[2] * self[3] + other[3] * self[2] + other[4] * self[1]
|
||||
}
|
||||
end
|
||||
|
||||
function normalize(self)
|
||||
local len = math.sqrt(self[1] ^ 2 + self[2] ^ 2 + self[3] ^ 2 + (self[4] ^ 4))
|
||||
local res = {}
|
||||
for key, value in pairs(self) do
|
||||
res[key] = value / len
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function conjugate(self)
|
||||
return {
|
||||
-self[1],
|
||||
-self[2],
|
||||
-self[3],
|
||||
self[4]
|
||||
}
|
||||
end
|
||||
|
||||
function inverse(self)
|
||||
return modlib.vector.divide_scalar(conjugate(self), self[1] ^ 2 + self[2] ^ 2 + self[3] ^ 2 + self[4] ^ 2)
|
||||
end
|
||||
|
||||
function negate(self)
|
||||
for key, value in pairs(self) do
|
||||
self[key] = -value
|
||||
end
|
||||
end
|
||||
|
||||
function dot(self, other)
|
||||
return self[1] * other[1] + self[2] * other[2] + self[3] * other[3] + self[4] * other[4]
|
||||
end
|
||||
|
||||
--: self normalized quaternion
|
||||
--: other normalized quaternion
|
||||
function slerp(self, other, ratio)
|
||||
local d = dot(self, other)
|
||||
if d < 0 then
|
||||
d = -d
|
||||
negate(other)
|
||||
end
|
||||
-- Threshold beyond which linear interpolation is used
|
||||
if d > 1 - 1e-10 then
|
||||
return modlib.vector.interpolate(self, other, ratio)
|
||||
end
|
||||
local theta_0 = math.acos(d)
|
||||
local theta = theta_0 * ratio
|
||||
local sin_theta = math.sin(theta)
|
||||
local sin_theta_0 = math.sin(theta_0)
|
||||
local s_1 = sin_theta / sin_theta_0
|
||||
local s_0 = math.cos(theta) - d * s_1
|
||||
return modlib.vector.add(modlib.vector.multiply_scalar(self, s_0), modlib.vector.multiply_scalar(other, s_1))
|
||||
end
|
||||
|
||||
--> {x = pitch, y = yaw, z = roll} euler rotation in degrees
|
||||
function to_euler_rotation(self)
|
||||
local rotation = {}
|
||||
|
||||
local sinr_cosp = 2 * (self[4] * self[1] + self[2] * self[3])
|
||||
local cosr_cosp = 1 - 2 * (self[1] ^ 2 + self[2] ^ 2)
|
||||
rotation.x = math.atan2(sinr_cosp, cosr_cosp)
|
||||
|
||||
local sinp = 2 * (self[4] * self[2] - self[3] * self[1])
|
||||
if sinp <= -1 then
|
||||
rotation.z = -math.pi/2
|
||||
elseif sinp >= 1 then
|
||||
rotation.z = math.pi/2
|
||||
else
|
||||
rotation.z = math.asin(sinp)
|
||||
end
|
||||
|
||||
local siny_cosp = 2 * (self[4] * self[3] + self[1] * self[2])
|
||||
local cosy_cosp = 1 - 2 * (self[2] ^ 2 + self[3] ^ 2)
|
||||
rotation.y = math.atan2(siny_cosp, cosy_cosp)
|
||||
|
||||
return vector.apply(rotation, math.deg)
|
||||
end
|
||||
|
||||
-- See https://github.com/zaki/irrlicht/blob/master/include/quaternion.h#L652
|
||||
function to_euler_rotation_irrlicht(self)
|
||||
local x, y, z, w = unpack(self)
|
||||
local test = 2 * (y * w - x * z)
|
||||
|
||||
local function _calc()
|
||||
if math.abs(test - 1) <= 1e-6 then
|
||||
return {
|
||||
z = -2 * math.atan2(x, w),
|
||||
x = 0,
|
||||
y = math.pi/2
|
||||
}
|
||||
end
|
||||
if math.abs(test + 1) <= 1e-6 then
|
||||
return {
|
||||
z = 2 * math.atan2(x, w),
|
||||
x = 0,
|
||||
y = math.pi/-2
|
||||
}
|
||||
end
|
||||
return {
|
||||
z = math.atan2(2 * (x * y + z * w), x ^ 2 - y ^ 2 - z ^ 2 + w ^ 2),
|
||||
x = math.atan2(2 * (y * z + x * w), -x ^ 2 - y ^ 2 + z ^ 2 + w ^ 2),
|
||||
y = math.asin(math.min(math.max(test, -1), 1))
|
||||
}
|
||||
end
|
||||
|
||||
return vector.apply(_calc(), math.deg)
|
||||
end
|
|
@ -349,7 +349,7 @@ function find(list, value)
|
|||
return index
|
||||
end
|
||||
end
|
||||
return false
|
||||
return
|
||||
end
|
||||
|
||||
contains = find
|
||||
|
|
|
@ -94,11 +94,65 @@ end
|
|||
local colorspec = modlib.minetest.colorspec.from_number(0xDDCCBBAA)
|
||||
assert(modlib.table.equals(colorspec, {a = 0xAA, b = 0xBB, g = 0xCC, r = 0xDD,}), dump(colorspec))
|
||||
|
||||
-- in-game tests
|
||||
-- k-d-tree
|
||||
local vectors = {}
|
||||
for _ = 1, 1000 do
|
||||
table.insert(vectors, {math.random(), math.random(), math.random()})
|
||||
end
|
||||
local kdtree = modlib.kdtree.new(vectors)
|
||||
for _, vector in ipairs(vectors) do
|
||||
local neighbor, distance = kdtree:get_nearest_neighbor(vector)
|
||||
assert(modlib.vector.equals(vector, neighbor), distance == 0)
|
||||
end
|
||||
|
||||
for _ = 1, 1000 do
|
||||
local vector = {math.random(), math.random(), math.random()}
|
||||
local _, distance = kdtree:get_nearest_neighbor(vector)
|
||||
local min_distance = math.huge
|
||||
for _, other_vector in ipairs(vectors) do
|
||||
local other_distance = modlib.vector.distance(vector, other_vector)
|
||||
if other_distance < min_distance then
|
||||
min_distance = other_distance
|
||||
end
|
||||
end
|
||||
assert(distance == min_distance)
|
||||
end
|
||||
|
||||
-- in-game tests & b3d testing
|
||||
local tests = {
|
||||
-- depends on player_api
|
||||
b3d = false,
|
||||
liquid_dir = false,
|
||||
liquid_raycast = false
|
||||
}
|
||||
if tests.b3d then
|
||||
local stream = io.open(modlib.mod.get_resource("player_api", "models", "character.b3d"), "r")
|
||||
assert(stream)
|
||||
local b3d = modlib.b3d.read(stream)
|
||||
--! dirty helper method to create truncate tables with 10+ number keys
|
||||
local function _b3d_truncate(table)
|
||||
local count = 1
|
||||
for key, value in pairs(table) do
|
||||
if type(key) == "table" then
|
||||
_b3d_truncate(key)
|
||||
end
|
||||
if type(value) == "table" then
|
||||
_b3d_truncate(value)
|
||||
end
|
||||
count = count + 1
|
||||
if type(key) == "number" and count >= 9 and next(table, key) then
|
||||
if count == 9 then
|
||||
table[key] = "TRUNCATED"
|
||||
else
|
||||
table[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
return table
|
||||
end
|
||||
modlib.file.write(modlib.mod.get_resource"character.b3d.lua", "return " .. dump(_b3d_truncate(modlib.table.copy(b3d))))
|
||||
stream:close()
|
||||
end
|
||||
if tests.liquid_dir then
|
||||
minetest.register_abm{
|
||||
label = "get_liquid_corner_levels & get_liquid_direction test",
|
||||
|
|
|
@ -42,6 +42,7 @@ function to_xyzw(v)
|
|||
return {x = v[1], y = v[2], z = v[3], w = v[4]}
|
||||
end
|
||||
|
||||
--+ not necessarily required, as Minetest respects the metatable
|
||||
function to_minetest(v)
|
||||
return mt_vector.new(unpack(v))
|
||||
end
|
||||
|
@ -115,10 +116,16 @@ metatable.__sub = subtract
|
|||
metatable.__mul = multiply
|
||||
metatable.__div = divide
|
||||
|
||||
--+ linear interpolation
|
||||
--: ratio number from 0 (all the first vector) to 1 (all the second vector)
|
||||
function interpolate(v, other_v, ratio)
|
||||
return add(multiply(v, 1 - ratio), multiply(other_v, ratio))
|
||||
end
|
||||
|
||||
function norm(v)
|
||||
local sum = 0
|
||||
for _, c in pairs(v) do
|
||||
sum = sum + c*c
|
||||
for _, value in pairs(v) do
|
||||
sum = sum + value ^ 2
|
||||
end
|
||||
return sum
|
||||
end
|
||||
|
@ -127,6 +134,15 @@ function length(v)
|
|||
return math.sqrt(norm(v))
|
||||
end
|
||||
|
||||
-- Minor code duplication for the sake of performance
|
||||
function distance(v, other_v)
|
||||
local sum = 0
|
||||
for key, value in pairs(v) do
|
||||
sum = sum + (value - other_v[key]) ^ 2
|
||||
end
|
||||
return math.sqrt(sum)
|
||||
end
|
||||
|
||||
function normalize(v)
|
||||
return divide_scalar(v, length(v))
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ assert(loadfile(modpath .. "/api/api_feed_tame.lua"))(S)
|
|||
assert(loadfile(modpath .. "/api/api_capture.lua"))(S)
|
||||
assert(loadfile(modpath .. "/api/api_tamagochi.lua"))(S)
|
||||
assert(loadfile(modpath .. "/api/api_breed.lua"))(S)
|
||||
assert(loadfile(modpath .. "/api/api_wool_milk.lua"))()
|
||||
assert(loadfile(modpath .. "/api/api_wool_milk.lua"))(S)
|
||||
assert(loadfile(modpath .. "/api/api_mount.lua"))()
|
||||
assert(loadfile(modpath .. "/api/api_dreamcatcher.lua"))(S)
|
||||
assert(loadfile(modpath .. "/api/api_eggs.lua"))()
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
---Refill lamb or milk
|
||||
---
|
||||
|
||||
local S = ...
|
||||
|
||||
petz.refill = function(self)
|
||||
if self.type == "lamb" then
|
||||
petz.lamb_wool_regrow(self)
|
||||
|
@ -66,6 +68,10 @@ petz.milk_refill = function(self)
|
|||
end
|
||||
|
||||
petz.milk_milk = function(self, clicker)
|
||||
if not self.is_male then
|
||||
minetest.chat_send_player(clicker:get_player_name(), S("Milk only female animals!"))
|
||||
return
|
||||
end
|
||||
local inv = clicker:get_inventory()
|
||||
local wielded_item = clicker:get_wielded_item()
|
||||
wielded_item:take_item()
|
||||
|
|
|
@ -131,6 +131,7 @@ Male=Macho
|
|||
Merry Christmas=Feliz Natal
|
||||
Mini Lamb Chop=Costeleta de cordeiro
|
||||
Milk Bucket=Balde de leite
|
||||
Milk only female animals!=Só leite de animais fêmeas!
|
||||
more to create the bobbin.=mais para criar a bobina.
|
||||
Moth=Traça
|
||||
Mr Pumpkin=Sr. Abóbora
|
||||
|
|
|
@ -131,6 +131,7 @@ Male=Männlich
|
|||
Merry Christmas=Frohe Weihnachten
|
||||
Mini Lamb Chop=Minilammkotelett
|
||||
Milk Bucket=Milcheimer
|
||||
Milk only female animals!=Melken Sie nur weibliche Tiere!
|
||||
more to create the bobbin.=mehr, um die Spule zu erstellen.
|
||||
Moth=Motte
|
||||
Mr Pumpkin=Herr Kürbis
|
||||
|
|
|
@ -131,6 +131,7 @@ Male=Macho
|
|||
Merry Christmas=Feliz Navidad
|
||||
Mini Lamb Chop=Chuletilla de cordero
|
||||
Milk Bucket=Balde de leche
|
||||
Milk only female animals!=¡Ordeña solo hembras!
|
||||
more to create the bobbin.=más para crear la bobina.
|
||||
Moth=Polilla
|
||||
Mr Pumpkin=Señor Calabaza
|
||||
|
|
|
@ -131,6 +131,7 @@ Male=Mâle
|
|||
Merry Christmas=Joyeux noël
|
||||
Mini Lamb Chop=Morceau d'agneau
|
||||
Milk Bucket=Seau de lait
|
||||
Milk only female animals!=Ne traitez que les femelles!
|
||||
more to create the bobbin.=plus pour créer la bobine.
|
||||
Moth=Papillon de nuit
|
||||
Mr Pumpkin=Mr Citrouille
|
||||
|
|
|
@ -131,6 +131,7 @@ Male=Самец
|
|||
Merry Christmas=С Рождеством!
|
||||
Mini Lamb Chop=Отбивная из ягненка
|
||||
Milk Bucket=Ведро с молоком
|
||||
Milk only female animals!=Доите только самки!
|
||||
more to create the bobbin.=больше для создания катушки.
|
||||
Moth=Мотылёк.
|
||||
Mr Pumpkin=Мистер Тыква
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
function petz.hq_terrestial_jump(self, prty)
|
||||
--Check igf not water or air (cliff)
|
||||
local node_name = petz.node_name_in(self, "front_below")
|
||||
if minetest.registered_nodes[node_name]["liquidtype"] == "source" or
|
||||
if not(node_name) or minetest.registered_nodes[node_name]["liquidtype"] == "source" or
|
||||
minetest.registered_nodes[node_name]["liquidtype"] == "flowing" or
|
||||
node_name == "air" then
|
||||
return
|
||||
|
|
|
@ -48,15 +48,20 @@ function petz.semiaquatic_brain(self)
|
|||
end
|
||||
end
|
||||
|
||||
-- hunt a prey (frogs)
|
||||
if prty < 12 then -- if not busy with anything important
|
||||
petz.bh_hunt(self, 12, false)
|
||||
end
|
||||
|
||||
if prty < 10 then
|
||||
if player then
|
||||
if not(self.tamed) or (self.tamed and self.status == "guard" and player:get_player_name() ~= self.owner) then
|
||||
local player_pos = player:get_pos()
|
||||
if vector.distance(pos, player_pos) <= self.view_range then -- if player close
|
||||
if vector.distance(pos, player_pos) <= self.view_range then -- if player close
|
||||
if self.warn_attack then --attack player
|
||||
mobkit.clear_queue_high(self) -- abandon whatever they've been doing
|
||||
mobkit.clear_queue_high(self) -- abandon whatever they've been doing
|
||||
if petz.isinliquid(self) then
|
||||
mobkit.hq_aqua_attack(self, 10, player, 6) -- get revenge
|
||||
mobkit.hq_aqua_attack(self, 10, player, 6) -- get revenge
|
||||
else
|
||||
petz.hq_hunt(self, 10, player)
|
||||
end
|
||||
|
@ -66,6 +71,17 @@ function petz.semiaquatic_brain(self)
|
|||
end
|
||||
end
|
||||
|
||||
if prty < 8 then
|
||||
if (self.can_jump) and not(self.status) then
|
||||
local random_number = math.random(1, self.jump_ratio)
|
||||
if random_number == 1 then
|
||||
--minetest.chat_send_player("singleplayer", "jump")
|
||||
mobkit.clear_queue_high(self)
|
||||
petz.hq_terrestial_jump(self, 8)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if prty < 6 then
|
||||
petz.bh_replace(self)
|
||||
end
|
||||
|
|
|
@ -110,6 +110,9 @@ end
|
|||
|
||||
|
||||
function petz.check_ground_suffocation(self)
|
||||
if self.can_fly then --some fying mobs can escape from cages by the roof
|
||||
return
|
||||
end
|
||||
local spos = mobkit.get_stand_pos(self)
|
||||
spos.y = spos.y + 0.01
|
||||
if self.type and mobkit.is_alive(self) and not(self.is_baby) then
|
||||
|
|
|
@ -271,6 +271,7 @@ panda_copulation_distance = 2
|
|||
|
||||
##Frog Specific
|
||||
frog_follow = fireflies:firefly
|
||||
frog_preys = petz:ant,petz:queen_ant
|
||||
frog_spawn_nodes = default:dirt_with_grass,default:river_water_source
|
||||
frog_spawn_chance = 0.6
|
||||
frog_spawn_biome = default
|
||||
|
@ -514,11 +515,13 @@ snow_leopard_spawn_biome = default
|
|||
|
||||
##Ant Specific
|
||||
ant_preys = petz:queen_ant
|
||||
ant_predators = petz:frog
|
||||
|
||||
##Queen Ant Specific
|
||||
queen_ant_spawn_chance = 0.4
|
||||
queen_ant_spawn_nodes = default:dirt_with_grass,default:desert_sand
|
||||
queen_ant_preys = petz:queen_ant
|
||||
queen_ant_predators = petz:frog
|
||||
queen_ant_spawn_biome = default
|
||||
|
||||
##Bunny Specific
|
||||
|
|
|
@ -21,6 +21,9 @@ minetest.register_entity("petz:"..pet_name,{
|
|||
give_orders = false,
|
||||
can_be_brushed = false,
|
||||
capture_item = "net",
|
||||
can_jump = true,
|
||||
jump_ratio = 10,
|
||||
jump_impulse = 4.0,
|
||||
follow = petz.settings.frog_follow,
|
||||
drops = {
|
||||
{name = "petz:frog_leg", chance = 1, min = 1, max = 1,},
|
||||
|
@ -48,6 +51,7 @@ minetest.register_entity("petz:"..pet_name,{
|
|||
animation = {
|
||||
walk={range={x=26, y=38}, speed=25, loop=true},
|
||||
run={range={x=26, y=38}, speed=30, loop=true},
|
||||
jump={range={x=26, y=38}, speed=10, loop=true},
|
||||
stand={
|
||||
{range={x=0, y=12}, speed=5, loop=true},
|
||||
},
|
||||
|
|
Before Width: | Height: | Size: 555 B After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 551 B After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,3 @@
|
|||
tamagochi_mode = false
|
||||
|
||||
shears = mobs:shears
|
|
@ -49,6 +49,7 @@ local function croc_brain(self)
|
|||
end
|
||||
|
||||
if mobkit.timer(self,1) then
|
||||
local prty = mobkit.get_queue_priority(self)
|
||||
|
||||
if not mobkit.recall(self,"landlife") and not mobkit.recall(self,"waterlife") then
|
||||
mobkit.remember(self,"waterlife",os.time())
|
||||
|
@ -68,7 +69,7 @@ local function croc_brain(self)
|
|||
end
|
||||
end
|
||||
|
||||
local prty = mobkit.get_queue_priority(self)
|
||||
|
||||
|
||||
if prty < 20 then
|
||||
local target = mobkit.get_nearby_player(self)
|
||||
|
|
|
@ -98,6 +98,18 @@ function water_life_get_biome_data(pos)
|
|||
return biome
|
||||
end
|
||||
|
||||
-- get list of biome names
|
||||
function water_life.get_biomes()
|
||||
|
||||
local biomes = {}
|
||||
|
||||
for k,v in pairs(minetest.registered_biomes) do
|
||||
table.insert(biomes, k)
|
||||
end
|
||||
|
||||
if #biomes > 0 then return biomes else return nil end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- returns closest enemy or player, if player is true enemies must be in entity definition: predators = {[name1]=1,[name2]=1,.....}
|
||||
|
|
|
@ -0,0 +1,912 @@
|
|||
|
||||
local abs = math.abs
|
||||
local pi = math.pi
|
||||
local floor = math.floor
|
||||
local ceil = math.ceil
|
||||
local sqrt = math.sqrt
|
||||
local max = math.max
|
||||
local min = math.min
|
||||
local pow = math.pow
|
||||
local sign = math.sign
|
||||
local time = os.time
|
||||
local rad = math.rad
|
||||
local deg=math.deg
|
||||
local tan = math.tan
|
||||
local cos = math.cos
|
||||
local atan=math.atan
|
||||
|
||||
|
||||
|
||||
local neighbors ={
|
||||
{x=1,z=0},
|
||||
{x=1,z=1},
|
||||
{x=0,z=1},
|
||||
{x=-1,z=1},
|
||||
{x=-1,z=0},
|
||||
{x=-1,z=-1},
|
||||
{x=0,z=-1},
|
||||
{x=1,z=-1}
|
||||
}
|
||||
|
||||
|
||||
-- pseudo random generator, init and call function
|
||||
water_life.randomtable = PcgRandom(math.random(2^23)+1)
|
||||
|
||||
function water_life.random(min,max)
|
||||
if not min and not max then return math.abs(water_life.randomtable:next() / 2^31) end
|
||||
if not max then
|
||||
max = min
|
||||
min = 1
|
||||
end
|
||||
if max and not min then min = 1 end
|
||||
if max < min then return water_life.randomtable:next(max,min) end
|
||||
|
||||
return water_life.randomtable:next(min,max)
|
||||
end
|
||||
|
||||
--
|
||||
local random = water_life.random -- do not delete, this MUST be here!
|
||||
--
|
||||
|
||||
--checks if entity is in a small water pool
|
||||
function water_life.check_for_pool(self,deep,minr,pos)
|
||||
if not self and not pos then return nil end
|
||||
if not deep then deep = 3 end
|
||||
if not minr then minr = 3 end
|
||||
|
||||
local max = 16
|
||||
if not pos then
|
||||
pos = self.object:get_pos()
|
||||
end
|
||||
|
||||
local d,t,s = water_life.water_depth(pos,max)
|
||||
if not d then return nil end
|
||||
local cpos = {}
|
||||
local ispool = 0
|
||||
|
||||
for i = 0,270,90 do
|
||||
cpos = mobkit.pos_translate2d(pos,rad(i),minr)
|
||||
if water_life.find_collision(pos,cpos,false) then ispool = ispool + 1 end
|
||||
end
|
||||
|
||||
if ispool > 2 and d < deep then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- returns ingame time, 1 = morning, 2 = noon, 3 = afternoon, 4 = night
|
||||
function water_life.get_game_time()
|
||||
local time = minetest.get_timeofday()
|
||||
local hour = math.floor(time*24)
|
||||
|
||||
if hour >= 5 and hour < 10 then return 1 end
|
||||
if hour >= 10 and hour < 15 then return 2 end
|
||||
if hour >= 15 and hour < 20 then return 3 end
|
||||
if hour > 20 or hour < 5 then return 4 end
|
||||
end
|
||||
|
||||
|
||||
function water_life_get_biome_data(pos)
|
||||
if not pos then return nil end
|
||||
local table = minetest.get_biome_data(pos)
|
||||
if not table then return nil end
|
||||
local biome = {}
|
||||
biome.id = table.biome
|
||||
biome.name = minetest.get_biome_name(table.biome)
|
||||
biome.temp = math.floor((table.heat-32)*5/9) --turn fahrenheit into celsius
|
||||
biome.humid = math.floor(table.humidity*100)/100
|
||||
return biome
|
||||
end
|
||||
|
||||
-- get list of biome names
|
||||
function water_life.get_biomes()
|
||||
|
||||
local biomes = {}
|
||||
|
||||
for k,v in pairs(minetest.registered_biomes) do
|
||||
table.insert(biomes, k)
|
||||
end
|
||||
|
||||
if #biomes > 0 then return biomes else return nil end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- returns closest enemy or player, if player is true enemies must be in entity definition: predators = {[name1]=1,[name2]=1,.....}
|
||||
function water_life.get_closest_enemy(self,player)
|
||||
local cobj = nil
|
||||
local dist = water_life.abr*64
|
||||
local pos = self.object:get_pos()
|
||||
local otable = minetest.get_objects_inside_radius(pos, self.view_range)
|
||||
|
||||
if not self.predators and not player then return nil end
|
||||
|
||||
for _,obj in ipairs(otable) do
|
||||
local luaent = obj:get_luaentity()
|
||||
|
||||
if mobkit.is_alive(obj) and not obj:is_player() and luaent and self.predators[luaent.name] then
|
||||
local opos = obj:get_pos()
|
||||
local odist = abs(opos.x-pos.x) + abs(opos.z-pos.z)
|
||||
if odist < dist then
|
||||
dist=odist
|
||||
cobj=obj
|
||||
end
|
||||
elseif mobkit.is_alive(obj) and obj:is_player() and player then
|
||||
local opos = obj:get_pos()
|
||||
local odist = abs(opos.x-pos.x) + abs(opos.z-pos.z)
|
||||
if odist < dist then
|
||||
dist=odist
|
||||
cobj=obj
|
||||
end
|
||||
end
|
||||
end
|
||||
return cobj
|
||||
end
|
||||
|
||||
|
||||
--player knockback from entity
|
||||
function water_life.knockback_player(self,target,force)
|
||||
if not target:is_player() then return end
|
||||
if not self.object then return end
|
||||
if not force then force = 10 end
|
||||
local dir = minetest.yaw_to_dir(self.object:get_yaw())
|
||||
dir = vector.multiply(dir, force)
|
||||
dir = {x=dir.x, y=dir.y+force/2, z= dir.z}
|
||||
target:add_player_velocity(dir)
|
||||
end
|
||||
|
||||
|
||||
--sets an urchin somewhere but not in the center of a node
|
||||
function water_life.set_urchin(pos,name)
|
||||
if not pos then return end
|
||||
if not name then name = "water_life:urchin" end
|
||||
local x = random()/2
|
||||
local z = random()/2
|
||||
if water_life.leftorright() then pos.x = pos.x +x else pos.x=pos.x - x end
|
||||
if water_life.leftorright() then pos.z = pos.z +z else pos.z=pos.z - z end
|
||||
local obj = minetest.add_entity(pos, name)
|
||||
return obj
|
||||
end
|
||||
|
||||
-- add vector cross function for flying behavior if not yet there
|
||||
if vector and not vector.cross then
|
||||
function vector.cross(a, b)
|
||||
return {
|
||||
x = a.y * b.z - a.z * b.y,
|
||||
y = a.z * b.x - a.x * b.z,
|
||||
z = a.x * b.y - a.y * b.x
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- show temp marker
|
||||
function water_life.temp_show(pos,time,pillar)
|
||||
if not pos then return end
|
||||
if not time then time = 5 end
|
||||
local step = 1
|
||||
if not pillar then pillar = 1 end
|
||||
if pillar < 0 then step = -1 end
|
||||
|
||||
for i = 1,pillar,step do
|
||||
|
||||
local obj = minetest.add_entity({x=pos.x, y=pos.y+i, z=pos.z}, "water_life:pos")
|
||||
if obj then
|
||||
minetest.after(time, function(obj) obj:remove() end, obj)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- throws a coin
|
||||
function water_life.leftorright()
|
||||
local rnd = random()
|
||||
if rnd > 0.5 then return true else return false end
|
||||
end
|
||||
|
||||
|
||||
-- distance from self to target
|
||||
function water_life.dist2tgt(self,tgt)
|
||||
local pos = mobkit.get_stand_pos(self)
|
||||
local tpos = tgt:get_pos()
|
||||
return vector.distance(pos,tpos)
|
||||
end
|
||||
|
||||
|
||||
function water_life.dumbstep(self,height,tpos,speed_factor,idle_duration)
|
||||
if height <= 0.001 then
|
||||
mobkit.lq_turn2pos(self,tpos)
|
||||
water_life.lq_dumbwalk(self,tpos,speed_factor)
|
||||
else
|
||||
mobkit.lq_turn2pos(self,tpos)
|
||||
water_life.lq_dumbjump(self,height)
|
||||
end
|
||||
idle_duration = idle_duration or 6
|
||||
mobkit.lq_idle(self,random(ceil(idle_duration*0.5),idle_duration))
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- drop on death what is definded in the entity table
|
||||
function water_life.handle_drops(self)
|
||||
if not self.drops then return end
|
||||
|
||||
for _,item in ipairs(self.drops) do
|
||||
|
||||
local amount = random (item.min, item.max)
|
||||
local chance = random(1,100)
|
||||
local pos = self.object:get_pos()
|
||||
pos.y = pos.y + self.collisionbox[5] +1
|
||||
|
||||
if chance < (100/item.chance) then
|
||||
local obj = minetest.add_item(pos, item.name.." "..tostring(amount))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function water_life.register_shark_food(name)
|
||||
table.insert(water_life.shark_food,name)
|
||||
end
|
||||
|
||||
function water_life.register_gull_bait(name)
|
||||
if name then water_life.gull_bait[name] = 1 end
|
||||
end
|
||||
|
||||
|
||||
function water_life.feed_shark(self)
|
||||
for i = 1,#water_life.shark_food,1 do
|
||||
if water_life.shark_food[i] ~= "water_life:fish" and water_life.shark_food[i] ~= "water_life:fish_tamed" then
|
||||
local target = mobkit.get_closest_entity(self,water_life.shark_food[i])
|
||||
if target then
|
||||
return target
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function water_life.get_close_drops(self,name)
|
||||
|
||||
|
||||
local objs = minetest.get_objects_inside_radius(self.object:get_pos(), self.view_range)
|
||||
if #objs < 1 then return nil end
|
||||
|
||||
for i = #objs,1,-1 do
|
||||
local entity = objs[i]:get_luaentity()
|
||||
if not entity or not entity.name == "__builtin:item" then table.remove(objs,i) end -- remove any entity different from a drop
|
||||
end
|
||||
|
||||
if #objs < 1 then return nil end
|
||||
if not name then return objs[random(#objs)] end -- no name, return random drop
|
||||
|
||||
for i=#objs,1,-1 do
|
||||
local entity = objs[i]:get_luaentity()
|
||||
if not entity.itemstring then
|
||||
table.remove(objs,i)
|
||||
else
|
||||
if not string.match(entity.itemstring,name) then table.remove(objs,i) end -- remove anything different from name
|
||||
end
|
||||
end
|
||||
|
||||
if #objs < 1 then
|
||||
return nil
|
||||
else
|
||||
return objs[random(#objs)]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function water_life.inwater(obj)
|
||||
if not obj then return nil end
|
||||
local pos = obj:get_pos()
|
||||
local node = minetest.get_node(pos)
|
||||
if not node or node.name == 'ignore' then return nil end
|
||||
if not minetest.registered_nodes[node.name] then return nil end -- handle unknown nodes
|
||||
|
||||
local type = minetest.registered_nodes[node.name]["liquidtype"]
|
||||
if type == "none" then return nil end
|
||||
return true
|
||||
end
|
||||
|
||||
function water_life.aqua_radar_dumb(pos,yaw,range,reverse,shallow) -- same as mobkit's but added shallow water if true
|
||||
range = range or 4
|
||||
|
||||
local function okpos(p)
|
||||
local node = mobkit.nodeatpos(p)
|
||||
if node then
|
||||
if node.drawtype == 'liquid' then
|
||||
local nodeu = mobkit.nodeatpos(mobkit.pos_shift(p,{y=1}))
|
||||
local noded = mobkit.nodeatpos(mobkit.pos_shift(p,{y=-1}))
|
||||
if ((nodeu and nodeu.drawtype == 'liquid') or (noded and noded.drawtype == 'liquid')) or shallow then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
else
|
||||
local h,l = mobkit.get_terrain_height(p)
|
||||
if h then
|
||||
local node2 = mobkit.nodeatpos({x=p.x,y=h+1.99,z=p.z})
|
||||
if node2 and node2.drawtype == 'liquid' then return true, h end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local fpos = mobkit.pos_translate2d(pos,yaw,range)
|
||||
local ok,h = okpos(fpos)
|
||||
if not ok then
|
||||
local ffrom, fto, fstep
|
||||
if reverse then
|
||||
ffrom, fto, fstep = 3,1,-1
|
||||
else
|
||||
ffrom, fto, fstep = 1,3,1
|
||||
end
|
||||
for i=ffrom, fto, fstep do
|
||||
local ok,h = okpos(mobkit.pos_translate2d(pos,yaw+i,range))
|
||||
if ok then return yaw+i,h end
|
||||
ok,h = okpos(mobkit.pos_translate2d(pos,yaw-i,range))
|
||||
if ok then return yaw-i,h end
|
||||
end
|
||||
return yaw+pi,h
|
||||
else
|
||||
return yaw, h
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- counts animals in specified radius or active_block_range, returns a table containing numbers
|
||||
function water_life.count_objects(pos,radius,name)
|
||||
|
||||
if not radius then radius = water_life.abr * 16 end
|
||||
|
||||
local all_objects = minetest.get_objects_inside_radius(pos, radius)
|
||||
local hasil = {}
|
||||
hasil.whales = 0
|
||||
hasil.sharks = 0
|
||||
hasil.fish = 0
|
||||
hasil.name = 0
|
||||
hasil.all = #all_objects or 0
|
||||
|
||||
local _,obj
|
||||
for _,obj in ipairs(all_objects) do
|
||||
local entity = obj:get_luaentity()
|
||||
if name then
|
||||
if entity and entity.name == name then
|
||||
hasil.name = hasil.name +1
|
||||
end
|
||||
end
|
||||
if entity and entity.name == "water_life:whale" then
|
||||
hasil.whales = hasil.whales +1
|
||||
elseif entity and entity.name == "water_life:shark" then
|
||||
hasil.sharks = hasil.sharks +1
|
||||
elseif entity and (entity.name == "water_life:fish" or entity.name == "water_life:fish_tamed") then
|
||||
hasil.fish = hasil.fish +1
|
||||
end
|
||||
|
||||
if entity and entity.name then
|
||||
if not hasil[entity.name] then
|
||||
hasil[entity.name] = 1
|
||||
else
|
||||
hasil[entity.name] = hasil[entity.name] +1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return hasil
|
||||
end
|
||||
|
||||
|
||||
function water_life.get_herd_members(self,radius)
|
||||
|
||||
local pos = mobkit.get_stand_pos(self)
|
||||
local name = self.name
|
||||
|
||||
if not radius then radius = water_life.abo * 16 end
|
||||
|
||||
local all_objects = minetest.get_objects_inside_radius(pos, radius)
|
||||
if #all_objects < 1 then return nil end
|
||||
|
||||
for i = #all_objects,1,-1 do
|
||||
local entity = all_objects[i]:get_luaentity()
|
||||
|
||||
if entity and entity.name ~= name then
|
||||
table.remove(all_objects,i)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if #all_objects < 1 then
|
||||
return nil
|
||||
else
|
||||
return all_objects
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- returns 2D angle from self to target in radians
|
||||
function water_life.get_yaw_to_object(self,target)
|
||||
|
||||
local pos = mobkit.get_stand_pos(self)
|
||||
local tpos = target:get_pos()
|
||||
local tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos))
|
||||
return tyaw
|
||||
end
|
||||
|
||||
-- returns 2D angle from self to pos in radians
|
||||
function water_life.get_yaw_to_pos(self,tpos)
|
||||
|
||||
local pos = mobkit.get_stand_pos(self)
|
||||
local tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos))
|
||||
|
||||
return tyaw
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function water_life.isinliquid(target)
|
||||
if not target then return false end
|
||||
local nodepos = mobkit.get_stand_pos(target)
|
||||
local node1 = mobkit.nodeatpos(nodepos)
|
||||
nodepos.y = nodepos.y -1
|
||||
local node2 = mobkit.nodeatpos(nodepos)
|
||||
if node1 and node1.drawtype=='liquid' or (node2 and node2.drawtype=='liquid' and node1 and node1.drawtype=='airlike') then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- find if there is a node between pos1 and pos2
|
||||
-- water = true means water = obstacle
|
||||
-- returns distance to obstacle in nodes or nil
|
||||
|
||||
function water_life.find_collision(pos1,pos2,water,objects)
|
||||
local ray = minetest.raycast(pos1, pos2, objects, water)
|
||||
for pointed_thing in ray do
|
||||
if pointed_thing.type == "node" then
|
||||
local dist = math.floor(vector.distance(pos1,pointed_thing.under))
|
||||
return dist
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
-- fast radar, less accurate
|
||||
-- yaw-x to the right, yaw + x to the left !
|
||||
-- sonar = true water is no obstacle
|
||||
|
||||
function water_life.radar_fast(self,dist,sonar)
|
||||
local water = not sonar
|
||||
if not dist then dist = self.view_range end
|
||||
local angel = atan(3/dist)
|
||||
local yaw = self.object:get_yaw()
|
||||
local tpos = self.object:get_pos()
|
||||
local tapos = mobkit.pos_translate2d(tpos,(yaw-angel),dist)
|
||||
local tbpos = mobkit.pos_translate2d(tpos,yaw,dist)
|
||||
local tcpos = mobkit.pos_translate2d(tpos,(yaw+angel),dist)
|
||||
local tupos = mobkit.pos_shift(tbpos,{y=2})
|
||||
local tdpos = mobkit.pos_shift(tbpos,{y=-2})
|
||||
local right = water_life.find_collision(tpos,tapos,water)
|
||||
local left = water_life.find_collision(tpos,tcpos,water)
|
||||
local center = water_life.find_collision(tpos,tbpos,water)
|
||||
local up = water_life.find_collision(tpos,tupos,water)
|
||||
local down = water_life.find_collision(tpos,tdpos,water)
|
||||
|
||||
return left,right,center,up,down
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- WIP radar, not ready to use !
|
||||
function water_life.radar_fast_cols(obj,dist,sonar,rev) --sonar = true, water no obstacle :: rev = true array contains possible positions
|
||||
if not dist then dist = 3 end
|
||||
local pos = obj:get_pos()
|
||||
local yaw = 0
|
||||
local box = {}
|
||||
local counter = 1
|
||||
local rarray = {}
|
||||
|
||||
|
||||
if obj:is_player() then
|
||||
yaw = obj:get_look_horizontal()
|
||||
box = obj:get_properties().collisionbox
|
||||
else
|
||||
yaw = obj:get_yaw()
|
||||
if obj:get_luaentity().name == "water_life:whale" then yaw = yaw + rad(180) end
|
||||
box = obj:get_luaentity().collisionbox
|
||||
end
|
||||
|
||||
local vec = minetest.yaw_to_dir(yaw)
|
||||
local crasha = mobkit.get_box_displace_cols(pos,box,vec,dist)
|
||||
|
||||
|
||||
if crasha then
|
||||
for i = #crasha,1,-1 do
|
||||
for j = #crasha[i],1,-1 do
|
||||
local cpos ={x=crasha[i][j].x, y= pos.y, z= crasha[i][j].z}
|
||||
--local cyaw = abs(math.floor(minetest.dir_to_yaw(vector.direction(pos,cpos))*10))
|
||||
local node = minetest.get_node(cpos)
|
||||
if minetest.registered_nodes[node.name] then
|
||||
local kill = false
|
||||
if minetest.registered_nodes[node.name]["drawtype"] == "airlike" then kill = true end
|
||||
if minetest.registered_nodes[node.name]["buildable_to"] then kill = true end
|
||||
if minetest.registered_nodes[node.name]["drawtype"] == "liquid" then kill = sonar end
|
||||
|
||||
if kill then
|
||||
if not rev then
|
||||
table.remove(crasha[i],j)
|
||||
else
|
||||
rarray[counter] = cpos
|
||||
counter = counter +1
|
||||
end
|
||||
else
|
||||
if not rev then
|
||||
rarray[counter] = cpos
|
||||
counter = counter +1
|
||||
else
|
||||
table.remove(crasha[i],j)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
return rarray
|
||||
end
|
||||
|
||||
-- radar function for obstacles lying in front of an entity
|
||||
-- use water = true if water should be an obstacle
|
||||
|
||||
function water_life.radar(pos, yaw, radius, water,fast)
|
||||
|
||||
if not radius or radius < 1 then radius = 16 end
|
||||
local left = 0
|
||||
local right = 0
|
||||
if not water then water = false end
|
||||
for j = 0,3,1 do
|
||||
for i = 0,4,1 do
|
||||
local pos2 = mobkit.pos_translate2d(pos,yaw+(i*pi/16),radius)
|
||||
local pos3 = mobkit.pos_translate2d(pos,yaw-(i*pi/16),radius)
|
||||
--minetest.set_node(pos2,{name="default:stone"})
|
||||
if water_life.find_collision(pos,{x=pos2.x, y=pos2.y + j*2, z=pos2.z}, water) then
|
||||
left = left + 5 - i
|
||||
end
|
||||
if water_life.find_collision(pos,{x=pos3.x, y=pos3.y + j*2, z=pos3.z},water) then
|
||||
right = right + 5 - i
|
||||
end
|
||||
end
|
||||
end
|
||||
local up =0
|
||||
local down = 0
|
||||
if not fast then
|
||||
for j = -4,4,1 do
|
||||
for i = -3,3,1 do
|
||||
local k = i
|
||||
local pos2 = mobkit.pos_translate2d(pos,yaw+(i*pi/16),radius)
|
||||
local collide = water_life.find_collision(pos,{x=pos2.x, y=pos2.y + j, z=pos2.z}, water)
|
||||
if k < 0 then k = k * -1 end
|
||||
if collide and j <= 0 then
|
||||
down = down + math.floor((7+j-k)*collide/radius*2)
|
||||
elseif collide and j >= 0 then
|
||||
up = up + math.floor((7-j-k)*collide/radius*2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local under = water_life.find_collision(pos,{x=pos.x, y=pos.y - radius, z=pos.z}, water)
|
||||
if not under then under = radius end
|
||||
local above = water_life.find_collision(pos,{x=pos.x, y=pos.y + radius, z=pos.z}, water)
|
||||
if not above then above = radius end
|
||||
if water_life.radar_debug then
|
||||
-- minetest.chat_send_all(dump(water_life.radar_debug).." left = "..left.." right = "..right.." up = "..up.." down = "..down.." under = "..under.." above = "..above)
|
||||
end
|
||||
return left, right, up, down, under, above
|
||||
end
|
||||
|
||||
|
||||
--find a spawn position under air
|
||||
function water_life.find_node_under_air(pos,radius,name)
|
||||
if not pos then return nil end
|
||||
if not radius then radius = 3 end
|
||||
if not name then name={"group:crumbly","group:stone","group:tree"} end
|
||||
|
||||
local pos1 = {x=pos.x-radius, y=pos.y-radius, z=pos.z-radius} --mobkit.pos_shift(pos,{x=radius*-1,y=radius*-1,z=radius*-1})
|
||||
local pos2 = {x=pos.x+radius, y=pos.y+radius, z=pos.z+radius} --mobkit.pos_shift(pos,{x=radius,y=radius,z=radius})
|
||||
local spawner = minetest.find_nodes_in_area_under_air(pos1, pos2, name)
|
||||
if not spawner or #spawner < 1 then
|
||||
return nil
|
||||
else
|
||||
local rpos = spawner[random(#spawner)]
|
||||
rpos = mobkit.pos_shift(rpos,{y=1})
|
||||
return rpos
|
||||
end
|
||||
end
|
||||
|
||||
-- function to find liquid surface and depth at that position
|
||||
function water_life.water_depth(pos,max)
|
||||
|
||||
|
||||
local surface = {}
|
||||
local depth = 0
|
||||
local type = ""
|
||||
if not max then max = 10 end
|
||||
if not pos then return nil end
|
||||
local tempos = {}
|
||||
local node = minetest.get_node(pos)
|
||||
if not node or node.name == 'ignore' then return nil end
|
||||
if not minetest.registered_nodes[node.name] then return nil end -- handle unknown nodes
|
||||
|
||||
local type = minetest.registered_nodes[node.name]["liquidtype"]
|
||||
local found = false
|
||||
--minetest.chat_send_all(">>>"..dump(node.name).." <<<")
|
||||
if type == "none" then -- start in none liquid try to find surface
|
||||
|
||||
local under = water_life.find_collision(pos,{x=pos.x, y=pos.y - max, z=pos.z}, true)
|
||||
--minetest.chat_send_all(dump(under).." "..dump(node.name))
|
||||
if under then
|
||||
local check = {x=pos.x, y=pos.y - under-1, z=pos.z}
|
||||
local cname = minetest.get_node(check).name
|
||||
if not minetest.registered_nodes[cname] then return nil end -- handle unknown nodes
|
||||
if minetest.registered_nodes[cname]["liquidtype"] == "source" then
|
||||
surface = check
|
||||
found = true
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
return nil
|
||||
end
|
||||
|
||||
else -- start in liquid find way up first
|
||||
|
||||
local lastpos = pos
|
||||
for i = 1,max,1 do
|
||||
tempos = {x=pos.x, y=pos.y+i, z= pos.z}
|
||||
node = minetest.get_node(tempos)
|
||||
if not minetest.registered_nodes[node.name] then return nil end -- handle unknown nodes
|
||||
local ctype = minetest.registered_nodes[node.name]["liquidtype"]
|
||||
|
||||
if ctype == "none" then
|
||||
surface = lastpos
|
||||
found = true
|
||||
break
|
||||
end
|
||||
lastpos = tempos
|
||||
end
|
||||
if not found then surface = lastpos end
|
||||
end
|
||||
|
||||
pos = surface
|
||||
type = minetest.get_node(pos).name or ""
|
||||
local under = water_life.find_collision(pos,{x=pos.x, y=pos.y - max, z=pos.z}, false)
|
||||
depth = under or max
|
||||
|
||||
return depth, type, surface
|
||||
end
|
||||
|
||||
|
||||
-- amphibious version of mobkit
|
||||
function water_life.get_next_waypoint_fast(self,tpos,nogopos)
|
||||
local pos = mobkit.get_stand_pos(self)
|
||||
local dir=vector.direction(pos,tpos)
|
||||
local neighbor = mobkit.dir2neighbor(dir)
|
||||
local height, pos2, liquidflag = mobkit.is_neighbor_node_reachable(self,neighbor)
|
||||
local heightr = nil
|
||||
local heightl = nil
|
||||
local liq = nil
|
||||
|
||||
if height then
|
||||
local fast = false
|
||||
heightl = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,-1))
|
||||
if heightl and abs(heightl-height)<0.001 then
|
||||
heightr = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,1))
|
||||
if heightr and abs(heightr-height)<0.001 then
|
||||
fast = true
|
||||
dir.y = 0
|
||||
local dirn = vector.normalize(dir)
|
||||
local npos = mobkit.get_node_pos(mobkit.pos_shift(pos,neighbors[neighbor]))
|
||||
local factor = abs(dirn.x) > abs(dirn.z) and abs(npos.x-pos.x) or abs(npos.z-pos.z)
|
||||
pos2=mobkit.pos_shift(pos,{x=dirn.x*factor,z=dirn.z*factor})
|
||||
end
|
||||
end
|
||||
return height, pos2, fast
|
||||
else
|
||||
|
||||
for i=1,4 do
|
||||
-- scan left
|
||||
height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,-i))
|
||||
if height then return height,pos2 end
|
||||
-- scan right
|
||||
height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,i))
|
||||
if height then return height,pos2 end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- amphibious version of mobkit
|
||||
function water_life.goto_next_waypoint(self,tpos)
|
||||
local height, pos2 = water_life.get_next_waypoint_fast(self,tpos)
|
||||
|
||||
if not height then return false end
|
||||
|
||||
if height <= 0.01 then
|
||||
local yaw = self.object:get_yaw()
|
||||
local tyaw = minetest.dir_to_yaw(vector.direction(self.object:get_pos(),pos2))
|
||||
if abs(tyaw-yaw) > 1 then
|
||||
mobkit.lq_turn2pos(self,pos2)
|
||||
end
|
||||
mobkit.lq_dumbwalk(self,pos2)
|
||||
else
|
||||
mobkit.lq_turn2pos(self,pos2)
|
||||
mobkit.lq_dumbjump(self,height)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
||||
function water_life.get_next_waypoint(self,tpos)
|
||||
local pos = mobkit.get_stand_pos(self)
|
||||
local dir=vector.direction(pos,tpos)
|
||||
local neighbor = mobkit.dir2neighbor(dir)
|
||||
local function update_pos_history(self,pos)
|
||||
table.insert(self.pos_history,1,pos)
|
||||
if #self.pos_history > 2 then table.remove(self.pos_history,#self.pos_history) end
|
||||
end
|
||||
local nogopos = self.pos_history[2]
|
||||
|
||||
local height, pos2, liquidflag = mobkit.is_neighbor_node_reachable(self,neighbor)
|
||||
--minetest.chat_send_all('pos2 ' .. minetest.serialize(pos2))
|
||||
--minetest.chat_send_all('nogopos ' .. minetest.serialize(nogopos))
|
||||
if height and not (nogopos and mobkit.isnear2d(pos2,nogopos,0.1)) then
|
||||
|
||||
local heightl = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,-1))
|
||||
if heightl and abs(heightl-height)<0.001 then
|
||||
local heightr = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,1))
|
||||
if heightr and abs(heightr-height)<0.001 then
|
||||
dir.y = 0
|
||||
local dirn = vector.normalize(dir)
|
||||
local npos = mobkit.get_node_pos(mobkit.pos_shift(pos,neighbors[neighbor]))
|
||||
local factor = abs(dirn.x) > abs(dirn.z) and abs(npos.x-pos.x) or abs(npos.z-pos.z)
|
||||
pos2=mobkit.pos_shift(pos,{x=dirn.x*factor,z=dirn.z*factor})
|
||||
end
|
||||
end
|
||||
update_pos_history(self,pos2)
|
||||
return height, pos2
|
||||
else
|
||||
|
||||
for i=1,3 do
|
||||
-- scan left
|
||||
local height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,-i*self.path_dir))
|
||||
if height and not liq
|
||||
and not (nogopos and mobkit.isnear2d(pos2,nogopos,0.1)) then
|
||||
update_pos_history(self,pos2)
|
||||
return height,pos2
|
||||
end
|
||||
-- scan right
|
||||
height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,i*self.path_dir))
|
||||
if height and not liq
|
||||
and not (nogopos and mobkit.isnear2d(pos2,nogopos,0.1)) then
|
||||
update_pos_history(self,pos2)
|
||||
return height,pos2
|
||||
end
|
||||
end
|
||||
--scan rear
|
||||
height, pos2, liquidflag = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,4))
|
||||
if height and not liquidflag
|
||||
and not (nogopos and mobkit.isnear2d(pos2,nogopos,0.1)) then
|
||||
update_pos_history(self,pos2)
|
||||
return height,pos2
|
||||
end
|
||||
end
|
||||
-- stuck condition here
|
||||
table.remove(self.pos_history,2)
|
||||
self.path_dir = self.path_dir*-1 -- subtle change in pathfinding
|
||||
end
|
||||
|
||||
|
||||
-- blood effects
|
||||
function water_life.spilltheblood(object,size)
|
||||
if not water_life.bloody then return end
|
||||
if not size then size = 1 end
|
||||
local particlespawner_id = minetest.add_particlespawner({
|
||||
amount = 50,
|
||||
time = 1,
|
||||
minpos = vector.new(-0.3, size/2, -0.3),
|
||||
maxpos = vector.new( 0.3, size, 0.3),
|
||||
minvel = {x = -1, y = 0, z = -1},
|
||||
maxvel = {x = 1, y = 1, z = 1},
|
||||
minacc = {x = 0, y = 2, z = 0},
|
||||
maxacc = {x = 0, y = 3, z = 0},
|
||||
minexptime = 0.5,
|
||||
maxexptime = 1,
|
||||
minsize = 1,
|
||||
maxsize = 2,
|
||||
texture = "water_life_bloodeffect1.png",
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = true,
|
||||
attached = object,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- Entity definitions
|
||||
|
||||
-- entity for showing positions in debug
|
||||
minetest.register_entity("water_life:pos", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
collide_with_objects = false,
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"water_life_pos.png", "water_life_pos.png",
|
||||
"water_life_pos.png", "water_life_pos.png",
|
||||
"water_life_pos.png", "water_life_pos.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
physical = false,
|
||||
}
|
||||
})
|
||||
|
||||
if water_life.radar_debug then
|
||||
minetest.register_on_player_hpchange(function(player, hp_change, reason)
|
||||
if not player or hp_change >= 0 then return hp_change end
|
||||
local name = player:get_player_name()
|
||||
local privs = minetest.get_player_privs(name)
|
||||
if not privs.god then return hp_change end
|
||||
return 0
|
||||
end, true)
|
||||
|
||||
minetest.register_privilege("god", {description ="unvulnerable"})
|
||||
end
|
||||
|
||||
|
||||
--check here for antiserum group of eaten food
|
||||
minetest.register_on_item_eat(function(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
if not user or not user:is_player() then return end
|
||||
if not itemstack then return end
|
||||
local name = user:get_player_name()
|
||||
local antiserum = itemstack:get_definition().groups.antiserum
|
||||
if antiserum then
|
||||
local meta = user:get_meta()
|
||||
local score = user:get_hp()
|
||||
|
||||
if meta:get_int("snakepoison") > 0 then meta:set_int("snakepoison",0) end
|
||||
water_life.change_hud(user,"poison",0)
|
||||
end
|
||||
|
||||
return
|
||||
end)
|
||||
|
||||
|
||||
--new players are immune to snakepoison
|
||||
minetest.register_on_newplayer(function(player)
|
||||
if not player or not player:is_player() then return end
|
||||
local meta = player:get_meta()
|
||||
local join = os.time()
|
||||
meta:set_int("jointime",join)
|
||||
end)
|
||||
|
||||
-- but not forever
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
if not player or not player:is_player() then return end
|
||||
local meta = player:get_meta()
|
||||
meta:set_int("bitten",0)
|
||||
meta:set_int("repellant",0)
|
||||
end)
|
|
@ -1062,6 +1062,8 @@ function water_life.hq_fly2obj(self,prty,tgt,break_dist,force)
|
|||
local pos = self.object:get_pos()
|
||||
local yaw = self.object:get_yaw()
|
||||
local tgtpos = tgt:get_pos()
|
||||
if not tgtpos then return true end
|
||||
|
||||
local tgtyaw = tgt:get_yaw() --water_life.get_yaw_to_object(self,tgt)
|
||||
local tgtspeed = math.floor(vector.length(tgt:get_velocity() or {x=0,y=0,z=0}))
|
||||
if not tgt:is_player() and tgt:get_luaentity() and tgt:get_luaentity().name == "water_life:whale" then tgtyaw = tgtyaw + rad(180) end -- whales moving backwards XD
|
||||
|
@ -1072,6 +1074,7 @@ function water_life.hq_fly2obj(self,prty,tgt,break_dist,force)
|
|||
wname = stack:get_name()
|
||||
end
|
||||
|
||||
|
||||
if tgtpos.y < 0 then tgtpos.y = 1 end
|
||||
|
||||
|
||||
|
|
|
@ -73,6 +73,26 @@ minetest.register_chatcommand("wl_kill", {
|
|||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("wl_lb", {
|
||||
params = "",
|
||||
description = "list biomes",
|
||||
privs = {interact = true},
|
||||
func = function()
|
||||
|
||||
|
||||
local biom = water_life.get_biomes()
|
||||
|
||||
if not biom then return end
|
||||
|
||||
for i=1,#biom,1 do
|
||||
minetest.chat_send_all(dump(i)..") "..dump(biom[i]))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("wl_test", {
|
||||
params = "<mob_name>",
|
||||
description = "test",
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
-- check for islands mod
|
||||
if minetest.get_modpath("islands") then
|
||||
|
||||
local bnames = water_life.get_biomes()
|
||||
water_life.spawn_on_islands = true
|
||||
|
||||
if bnames then
|
||||
|
||||
for i=1,#bnames,1 do
|
||||
local keep = string.match(bnames[i],"savanna") or string.match(bnames[i],"rainforest")
|
||||
if not keep then minetest.unregister_biome(bnames[i]) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
-----------------------------------------------------------
|
||||
|
||||
water_life = {}
|
||||
water_life.version = "020201"
|
||||
water_life.version = "210207"
|
||||
water_life.shark_food = {}
|
||||
water_life.repellant = {}
|
||||
water_life.gull_bait = {}
|
||||
|
@ -43,7 +43,9 @@ water_life.bloody = minetest.settings:get_bool("water_life_bloody") or true
|
|||
local path = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
|
||||
|
||||
dofile(path.."/api.lua") -- load water_life api
|
||||
dofile(path.."/compat.lua")
|
||||
dofile(path.."/paths.lua") -- load pathfinding
|
||||
if water_life.muddy_water then dofile(path.."/mapgen.lua") end -- load muddy_water
|
||||
dofile(path.."/crafts.lua") -- load crafts
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
-----------------------------------------------------------
|
||||
--
|
||||
-- Water_life copyright 2020 by Gundul
|
||||
-- see software and media licenses in the doc folder
|
||||
--
|
||||
-----------------------------------------------------------
|
||||
|
||||
water_life = {}
|
||||
water_life.version = "210207"
|
||||
water_life.shark_food = {}
|
||||
water_life.repellant = {}
|
||||
water_life.gull_bait = {}
|
||||
water_life.catchNet = "water_life:placeholder"
|
||||
water_life.petz = minetest.get_modpath("petz")
|
||||
water_life.mobsredo = minetest.get_modpath("mobs")
|
||||
water_life.farming = minetest.get_modpath("farming")
|
||||
water_life.swampz = minetest.get_modpath("swaz")
|
||||
water_life.abr = tonumber(minetest.settings:get('active_block_range')) or 2
|
||||
water_life.abo = tonumber(minetest.settings:get('active_object_send_range_blocks')) or 3
|
||||
water_life.avg_dtime = 0
|
||||
water_life.max_dtime = 0
|
||||
|
||||
-- settingtypes
|
||||
water_life.whale_spawn_rate = tonumber(minetest.settings:get("water_life_whale_spawn_rate")) or 100
|
||||
water_life.shark_spawn_rate = tonumber(minetest.settings:get("water_life_shark_spawn_rate")) or 100
|
||||
water_life.urchin_spawn_rate = tonumber(minetest.settings:get("water_life_urchin_spawn_rate")) or 700
|
||||
water_life.clams_spawn_rate = tonumber(minetest.settings:get("water_life_clams_spawn_rate")) or 500
|
||||
water_life.fish_spawn_rate = tonumber(minetest.settings:get("water_life_fish_spawn_rate")) or 1000
|
||||
water_life.maxwhales = tonumber(minetest.settings:get("water_life_maxwhales")) or 1
|
||||
water_life.maxsharks = tonumber(minetest.settings:get("water_life_maxsharks")) or 5
|
||||
water_life.maxmobs = tonumber(minetest.settings:get("water_life_maxmobs")) or 60
|
||||
water_life.apionly = minetest.settings:get_bool("water_life_apionly") or false
|
||||
water_life.dangerous = minetest.settings:get_bool("water_life_dangerous") or false
|
||||
water_life.soundadjust = tonumber(minetest.settings:get("water_life_soundadjust")) or 1.0
|
||||
water_life.moskitolifetime = tonumber(minetest.settings:get("water_life_moskitolifetime")) or 120 -- lifetime in sec. ( <15 = no reproducing)
|
||||
water_life.radar_debug = minetest.settings:get_bool("water_life_radar_debug") or false
|
||||
water_life.muddy_water = minetest.settings:get_bool("water_life_muddy_water") or false
|
||||
water_life.repeltime = math.floor (720 / (tonumber(minetest.settings:get("time_speed")) or 72)*60) -- the repellent lasts half a minetest day
|
||||
water_life.newplayerbonus = tonumber(minetest.settings:get("water_life_newplayerbonus")) or 5 -- 5 days savety from rattlenakes for new players
|
||||
water_life.ihateinsects = minetest.settings:get_bool("water_life_hate_insects") or false
|
||||
water_life.bloody = minetest.settings:get_bool("water_life_bloody") or true -- let there be blood !
|
||||
|
||||
local path = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
|
||||
|
||||
dofile(path.."/api.lua") -- load water_life api
|
||||
dofile(path.."/compat.lua")
|
||||
dofile(path.."/paths.lua") -- load pathfinding
|
||||
if water_life.muddy_water then dofile(path.."/mapgen.lua") end -- load muddy_water
|
||||
dofile(path.."/crafts.lua") -- load crafts
|
||||
dofile(path.."/tools/buoy.lua") -- load buoy
|
||||
dofile(path.."/chatcommands.lua") -- load chatcommands
|
||||
dofile(path.."/behaviors.lua") -- load behaviors
|
||||
dofile(path.."/bio.lua") -- load bio data handles
|
||||
|
||||
if not water_life.apionly then
|
||||
dofile(path.."/hud.lua") -- load player hud
|
||||
dofile(path.."/spawn.lua") -- load spawn function
|
||||
dofile(path.."/animals/whale.lua") -- load whales
|
||||
dofile(path.."/animals/riverfish.lua") -- load riverfish
|
||||
dofile(path.."/animals/sea_urchin.lua") -- load sea urchin
|
||||
dofile(path.."/animals/clams.lua") -- load clams
|
||||
dofile(path.."/flora/plants.lua") -- load water plants
|
||||
dofile(path.."/flora/corals.lua") -- load corals
|
||||
dofile(path.."/animals/jellyfish.lua") -- load jellyfish
|
||||
dofile(path.."/animals/coralfish.lua") -- load coralfish
|
||||
dofile(path.."/animals/clownfish.lua") -- load clownfish
|
||||
dofile(path.."/animals/gulls.lua") -- load gulls
|
||||
dofile(path.."/animals/gecko.lua") -- load tokays
|
||||
dofile(path.."/animals/beaver.lua") -- load beavers
|
||||
if not water_life.dangerous then
|
||||
dofile(path.."/animals/snake.lua") -- load snakes
|
||||
dofile(path.."/animals/piranha.lua") -- load piranha
|
||||
dofile(path.."/animals/shark.lua") -- load sharks
|
||||
dofile(path.."/animals/crocodile.lua") -- load crocodile
|
||||
dofile(path.."/animals/moskito.lua") -- load moskitos
|
||||
if water_life.swampz then
|
||||
dofile(path.."/animals/alligator.lua") -- alligators need swampz mod
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--check which lasso to use
|
||||
if water_life.mobsredo then
|
||||
water_life.catchBA = "mobs:lasso"
|
||||
water_life.catchNet = "mobs:net"
|
||||
if water_life.petz then minetest.unregister_item("petz:lasso") end
|
||||
|
||||
elseif water_life.petz then
|
||||
water_life.catchBA = "petz:lasso"
|
||||
|
||||
else
|
||||
water_life.catchBA = "water_life:lasso"
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
math.randomseed(os.time()) --init random seed
|
||||
|
||||
|
||||
|
||||
--remove old sharks
|
||||
minetest.register_entity(":sharks:shark", {
|
||||
on_activate = function(self, staticdata)
|
||||
self.object:remove()
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity(":zombiestrd:shark", {
|
||||
on_activate = function(self, staticdata)
|
||||
self.object:remove()
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
|
||||
-- register shark food
|
||||
|
||||
|
||||
if minetest.get_modpath("wildlife") then
|
||||
water_life.register_shark_food("wildlife:deer")
|
||||
water_life.register_shark_food("wildlife:deer_tamed")
|
||||
water_life.register_shark_food("wildlife:wolf")
|
||||
end
|
||||
|
||||
if minetest.get_modpath("aerotest") then
|
||||
water_life.register_shark_food("aerotest:eagle")
|
||||
end
|
||||
|
||||
if minetest.get_modpath("petz") then
|
||||
water_life.register_shark_food("petz:kitty")
|
||||
water_life.register_shark_food("petz:rat")
|
||||
water_life.register_shark_food("petz:goat")
|
||||
water_life.register_shark_food("petz:puppy")
|
||||
water_life.register_shark_food("petz:ducky")
|
||||
water_life.register_shark_food("petz:lamb")
|
||||
water_life.register_shark_food("petz:camel")
|
||||
water_life.register_shark_food("petz:calf")
|
||||
water_life.register_shark_food("petz:chicken")
|
||||
water_life.register_shark_food("petz:piggy")
|
||||
water_life.register_shark_food("petz:hamster")
|
||||
water_life.register_shark_food("petz:chimp")
|
||||
water_life.register_shark_food("petz:beaver")
|
||||
water_life.register_shark_food("petz:turtle")
|
||||
water_life.register_shark_food("petz:penguin")
|
||||
water_life.register_shark_food("petz:lion")
|
||||
water_life.register_shark_food("petz:grizzly")
|
||||
water_life.register_shark_food("petz:pony")
|
||||
water_life.register_shark_food("petz:wolf")
|
||||
water_life.register_shark_food("petz:elephant")
|
||||
water_life.register_shark_food("petz:elephant_female")
|
||||
water_life.register_shark_food("petz:foxy")
|
||||
water_life.register_shark_food("petz:polar_bear")
|
||||
water_life.register_shark_food("petz:tarantula")
|
||||
water_life.register_shark_food("petz:leopard")
|
||||
water_life.register_shark_food("petz:snow_leopard")
|
||||
water_life.register_shark_food("petz:panda")
|
||||
water_life.register_shark_food("petz:santa_killer")
|
||||
water_life.register_shark_food("petz:mr_pumpkin")
|
||||
water_life.register_shark_food("petz:hen")
|
||||
water_life.register_shark_food("petz:rooster")
|
||||
end
|
||||
|
||||
if minetest.get_modpath("better_fauna") then
|
||||
water_life.register_shark_food("better_fauna:chicken")
|
||||
water_life.register_shark_food("better_fauna:cow")
|
||||
water_life.register_shark_food("better_fauna:pig")
|
||||
water_life.register_shark_food("better_fauna:sheep")
|
||||
water_life.register_shark_food("better_fauna:turkey")
|
||||
end
|
||||
|
||||
|
||||
-- register gull bait
|
||||
|
||||
water_life.register_gull_bait("water_life:clownfish")
|
||||
water_life.register_gull_bait("water_life:coralfish")
|
||||
water_life.register_gull_bait("water_life:riverfish")
|
||||
water_life.register_gull_bait("water_life:piranha")
|
||||
water_life.register_gull_bait("water_life:urchin_item")
|
||||
water_life.register_gull_bait("water_life:snake_item")
|
||||
water_life.register_gull_bait("water_life:meat_raw")
|
||||
water_life.register_gull_bait("water_life:meat")
|
||||
|
||||
if minetest.get_modpath("farming") then
|
||||
water_life.register_gull_bait("farming:bread")
|
||||
end
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ local dttimer = 10
|
|||
local pi = math.pi
|
||||
local random = water_life.random
|
||||
local landinterval = 120 -- check every 60 seconds for spawnpos on land
|
||||
local waterinterval = 40 -- check every 20 seconds for spawnpos in water
|
||||
local waterinterval = 30 -- check every 20 seconds for spawnpos in water
|
||||
|
||||
|
||||
local function getcount(name)
|
||||
|
@ -191,7 +191,7 @@ local function spawnstep(dtime)
|
|||
|
||||
local liquidflag = nil
|
||||
|
||||
if stype == "default:water_source" then
|
||||
if stype == "default:water_source" or stype == "islands:water_source" then
|
||||
liquidflag = "sea"
|
||||
|
||||
elseif stype == "default:river_water_source" then
|
||||
|
@ -380,11 +380,13 @@ local function spawnstep(dtime)
|
|||
--minetest.chat_send_all(dump(minetest.pos_to_string(surface)).." "..dump(minetest.pos_to_string(ground)))
|
||||
mobname = 'water_life:fish'
|
||||
--local nearlife = water_life.count_objects(pos2,24,"water_life:piranha")
|
||||
if water_life.fish_spawn_rate >= random(1000) and ((animal.all < (water_life.maxmobs-5)) or getcount(animal[mobname]) < 5) and (liquidflag == "river" or liquidflag == "muddy") then
|
||||
if water_life.fish_spawn_rate >= random(1000) and ((animal.all < (water_life.maxmobs-5)) or getcount(animal[mobname]) < 5) and (((liquidflag == "river" or liquidflag == "muddy")) or (water_life.spawn_on_islands and not water_life.check_for_pool(nil,2,3,pos2) and
|
||||
water_life.check_for_pool(nil,2,16,pos2))) then
|
||||
|
||||
local table = minetest.get_biome_data(pos)
|
||||
|
||||
if not water_life.dangerous and table and water_life.piranha_biomes[minetest.get_biome_name(table.biome)] then
|
||||
if not water_life.dangerous and ((table and water_life.piranha_biomes[minetest.get_biome_name(table.biome)])
|
||||
or water_life.spawn_on_islands) then
|
||||
mobname = "water_life:piranha"
|
||||
end
|
||||
|
||||
|
|
|
@ -27,4 +27,5 @@ Modified Code by Liil/Wilhelmine/Liil (c) 2021
|
|||
Textures, Models and Animation by Liil/Wilhelmine/Liil under (MIT) License (c) 2021
|
||||
|
||||
Sounds are from freesound.org under Creative Commons License. Thanks to Lauramel, Gleith, Theveomammoth11, Vataa, D-Jones, Pol, Robinhood76,
|
||||
sesmo42 and Missburusdeer2011 for the sounds!
|
||||
sesmo42, Missburusdeer2011, Hazure, Inspectorj, Benboncan, Spookymodem, Drobiumdesign, J-zazvurek, Benboncan, Felix-blume, Sihil, Kabit, Roubignolle, Egomassive, Florianreichelt and Pillonoise for the sounds!
|
||||
Thanks to Nagaty Studio for the Hyena Sounds! Source: https://www.youtube.com/c/NagatyStudio/about
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
mobs:register_mob("animalworld:anteater", {
|
||||
stepheight = 1,
|
||||
type = "animal",
|
||||
passive = false,
|
||||
attack_type = "dogfight",
|
||||
group_attack = true,
|
||||
owner_loyal = true,
|
||||
attack_npcs = false,
|
||||
reach = 2,
|
||||
damage = 10,
|
||||
hp_min = 25,
|
||||
hp_max = 65,
|
||||
armor = 100,
|
||||
collisionbox = {-0.6, -0.01, -0.6, 0.6, 0.95, 0.6},
|
||||
visual = "mesh",
|
||||
mesh = "Anteater.b3d",
|
||||
textures = {
|
||||
{"textureanteater.png"},
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
sounds = {
|
||||
|
||||
},
|
||||
walk_velocity = 0.7,
|
||||
run_velocity = 2,
|
||||
runaway = false,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine"},
|
||||
jump = false,
|
||||
jump_height = 3,
|
||||
pushable = true,
|
||||
follow = {"fishing:bait:worm", "bees:frame_full", "ethereal:worm"},
|
||||
view_range = 10,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
{name = "mobs:leather", chance = 1, min = 0, max = 2},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 5,
|
||||
light_damage = 0,
|
||||
fear_height = 2,
|
||||
animation = {
|
||||
speed_normal = 75,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
walk_start = 100,
|
||||
walk_end = 200,
|
||||
punch_start = 250,
|
||||
punch_end = 350,
|
||||
|
||||
die_start = 1, -- we dont have a specific death animation so we will
|
||||
die_end = 2, -- re-use 2 standing frames at a speed of 1 fps and
|
||||
die_speed = 1, -- have mob rotate when dying.
|
||||
die_loop = false,
|
||||
die_rotate = true,
|
||||
},
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
if mobs:feed_tame(self, clicker, 8, true, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 0, 5, 50, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
local spawn_on = {"default:dirt_with_rainforest_litter"}
|
||||
|
||||
if minetest.get_mapgen_setting("mg_name") ~= "v6" then
|
||||
spawn_on = {"default:dirt_with_rainforest_litter", "default:dry_dirt_with_dry_grass"}
|
||||
end
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = {"ethereal:grass_grove", "ethereal:green_dirt"}
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:anteater",
|
||||
nodes = {"default:dirt_with_rainforest_litter"},
|
||||
min_light = 0,
|
||||
interval = 1,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 1,
|
||||
max_height = 50,
|
||||
day_toggle = true,
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:anteater", ("Anteater"), "aanteater.png")
|
||||
|
||||
|
||||
mobs:alias_mob("animalworld:manteater", "animalworld:anteater") -- compatibility
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
mobs:register_mob("animalworld:bat", {
|
||||
stepheight = 3,
|
||||
type = "animal",
|
||||
passive = true,
|
||||
attack_type = "dogfight",
|
||||
attack_animals = false,
|
||||
reach = 2,
|
||||
damage = 2,
|
||||
hp_min = 5,
|
||||
hp_max = 35,
|
||||
armor = 100,
|
||||
collisionbox = {-0.3, -0.01, -1, 0.3, 0.3, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "Bat.b3d",
|
||||
visual_size = {x = 1.0, y = 1.0},
|
||||
textures = {
|
||||
{"texturebat.png"},
|
||||
},
|
||||
sounds = {
|
||||
random = "animalworld_bat",
|
||||
},
|
||||
makes_footstep_sound = false,
|
||||
walk_velocity = 5,
|
||||
run_velocity = 6,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
fall_speed = 0,
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
fly = true,
|
||||
stepheight = 3,
|
||||
drops = {
|
||||
{name = "mobs:leather", chance = 1, min = 0, max = 2},
|
||||
|
||||
},
|
||||
water_damage = 1,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 0,
|
||||
animation = {
|
||||
speed_normal = 130,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
walk_start = 150,
|
||||
walk_end = 250,
|
||||
fly_start = 150, -- swim animation
|
||||
fly_end = 250,
|
||||
-- 50-70 is slide/water idle
|
||||
},
|
||||
|
||||
fly_in = {"air"},
|
||||
floats = 0,
|
||||
follow = {
|
||||
"animalworld:rawfish", "ethereal:worm", "ethereal:fish_raw", "fishing:fish_raw", "xocean:fish_edible"
|
||||
},
|
||||
|
||||
view_range = 4,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
-- feed or tame
|
||||
if mobs:feed_tame(self, clicker, 4, false, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 5, 50, 80, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:bat",
|
||||
nodes = {"default:dirt_with_grass"}, {"default:dry_dirt_with_dry_grass"}, {"default:dirt_with_rainforest_litter"}, {"default:dirt_with_coniferous_litter"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 5,
|
||||
min_height = -100,
|
||||
max_height = 50,
|
||||
day_toggle = false,
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:bat", ("Bat"), "abat.png")
|
|
@ -5,10 +5,10 @@ stepheight = 1,
|
|||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
reach = 2,
|
||||
damage = 3,
|
||||
damage = 8,
|
||||
hp_min = 15,
|
||||
hp_max = 40,
|
||||
armor = 200,
|
||||
hp_max = 60,
|
||||
armor = 100,
|
||||
collisionbox = {-0.6, -0.01, -0.6, 0.6, 0.95, 0.6},
|
||||
visual = "mesh",
|
||||
mesh = "Bear.b3d",
|
||||
|
@ -46,7 +46,7 @@ stepheight = 1,
|
|||
|
||||
follow = {
|
||||
"ethereal:fish_raw", "animalworld:rawfish", "mobs_fish:tropical",
|
||||
"mobs:meat_raw", "animalworld:rabbit_raw", "xocean:fish_edible"
|
||||
"mobs:meat_raw", "animalworld:rabbit_raw", "xocean:fish_edible", "farming:melon_slice", "farming:melon_slice", "water_life:meat_raw", "water_life:meat_raw", "fishing:fish_raw", "animalworld:chicken_raw"
|
||||
},
|
||||
view_range = 8,
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@ stepheight = 3,
|
|||
attack_type = "dogfight",
|
||||
attack_animals = false,
|
||||
reach = 2,
|
||||
damage = 1,
|
||||
damage = 2,
|
||||
hp_min = 5,
|
||||
hp_max = 10,
|
||||
armor = 200,
|
||||
hp_max = 30,
|
||||
armor = 100,
|
||||
collisionbox = {-0.3, -0.01, -1, 0.3, 0.3, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "Blackbird.b3d",
|
||||
|
@ -23,7 +23,7 @@ stepheight = 3,
|
|||
walk_velocity = 2,
|
||||
run_velocity = 4,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "player"},
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
stepheight = 3,
|
||||
|
@ -52,7 +52,7 @@ stepheight = 3,
|
|||
fly_in = {"air"},
|
||||
floats = 0,
|
||||
follow = {
|
||||
"fishing:bait:worm"
|
||||
"fishing:bait:worm", "farming:seed_wheat", "farming:seed_rice", "farming:seed_oat", "ethereal:pine_nuts", "ethereal:worm"
|
||||
},
|
||||
|
||||
view_range = 4,
|
||||
|
@ -73,6 +73,7 @@ mobs:spawn({
|
|||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 0,
|
||||
max_height = 100,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -7,10 +7,10 @@ mobs:register_mob("animalworld:boar", {
|
|||
owner_loyal = true,
|
||||
attack_npcs = true,
|
||||
reach = 2,
|
||||
damage = 2,
|
||||
damage = 6,
|
||||
hp_min = 5,
|
||||
hp_max = 35,
|
||||
armor = 200,
|
||||
hp_max = 55,
|
||||
armor = 100,
|
||||
collisionbox = {-0.5, -0.01, -0.5, 0.5, 0.95, 0.5},
|
||||
visual = "mesh",
|
||||
mesh = "Boar.b3d",
|
||||
|
@ -27,8 +27,11 @@ mobs:register_mob("animalworld:boar", {
|
|||
jump = true,
|
||||
jump_height = 6,
|
||||
pushable = true,
|
||||
follow = {"default:apple", "farming:potato"},
|
||||
follow = {"default:apple", "farming:potato", "ethereal:banana_bread", "farming:melon_slice", "farming:carrot", "farming:seed_rice", "farming:corn"},
|
||||
view_range = 6,
|
||||
replace_rate = 10,
|
||||
replace_what = {"farming:soil", "farming:soil_wet"},
|
||||
replace_with = "default:dirt",
|
||||
drops = {
|
||||
{name = "animalworld:pork_raw", chance = 1, min = 1, max = 3},
|
||||
},
|
||||
|
@ -66,7 +69,7 @@ if minetest.get_mapgen_setting("mg_name") ~= "v6" then
|
|||
end
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = {"ethereal:mushroom_dirt", "ethereal:bamboo_dirt"}
|
||||
spawn_on = {"ethereal:mushroom_dirt", "ethereal:bamboo_dirt", "ethereal:green_dirt", "ethereal:mushroom"}
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
|
@ -76,6 +79,7 @@ mobs:spawn({
|
|||
min_light = 14,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 1,
|
||||
max_height = 80,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -7,10 +7,10 @@ mobs:register_mob("animalworld:camel", {
|
|||
owner_loyal = true,
|
||||
attack_npcs = false,
|
||||
reach = 2,
|
||||
damage = 2,
|
||||
damage = 5,
|
||||
hp_min = 20,
|
||||
hp_max = 40,
|
||||
armor = 200,
|
||||
hp_max = 60,
|
||||
armor = 100,
|
||||
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.95, 0.7},
|
||||
visual = "mesh",
|
||||
mesh = "Camel.b3d",
|
||||
|
@ -25,11 +25,11 @@ mobs:register_mob("animalworld:camel", {
|
|||
walk_velocity = 2,
|
||||
run_velocity = 5,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark"},
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine"},
|
||||
jump = false,
|
||||
jump_height = 3,
|
||||
pushable = true,
|
||||
follow = {"default:dry_shrub ", "default:grass_1"},
|
||||
follow = {"default:dry_shrub ", "default:grass_1", "ethereal:dry_shrub", "farming:seed_wheat", "farming:seed_rye", "default:junglegrass"},
|
||||
view_range = 7,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
|
@ -68,7 +68,7 @@ if minetest.get_mapgen_setting("mg_name") ~= "v6" then
|
|||
end
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = {"default:desert_sand"}
|
||||
spawn_on = {"default:desert_sand", "ethereal:dry_dirt"}
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
|
@ -78,6 +78,7 @@ mobs:spawn({
|
|||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 0,
|
||||
max_height = 40,
|
||||
})
|
||||
|
|
|
@ -7,8 +7,8 @@ stepheight = 0.0,
|
|||
reach = 1,
|
||||
damage = 1,
|
||||
hp_min = 5,
|
||||
hp_max = 5,
|
||||
armor = 200,
|
||||
hp_max = 25,
|
||||
armor = 100,
|
||||
collisionbox = {-0.4, -0.01, -0.4, 0.4, 0.95, 0.4},
|
||||
visual = "mesh",
|
||||
mesh = "Carp.b3d",
|
||||
|
@ -24,7 +24,7 @@ stepheight = 0.0,
|
|||
fly_in = "default:water_source", "default:river_water_source", "default:water_flowing",
|
||||
fall_speed = 0,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:elephant", "animalworld:hippo", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "player"},
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:elephant", "animalworld:hippo", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
jump = false,
|
||||
stepheight = 0.0,
|
||||
drops = {
|
||||
|
@ -50,7 +50,7 @@ stepheight = 0.0,
|
|||
fly_in = {"default:water_source", "default:river_water_source", "default:water_flowing"},
|
||||
floats = 0,
|
||||
follow = {
|
||||
"default:kelp", "seaweed", "xocean:kelp",
|
||||
"ethereal:worm", "seaweed", "fishing:bait_worm",
|
||||
"default:grass", "farming:cucumber", "farming:cabbage"
|
||||
},
|
||||
view_range = 10,
|
||||
|
@ -71,6 +71,7 @@ mobs:spawn({
|
|||
min_light = 14,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 0,
|
||||
max_height = 30,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -7,8 +7,8 @@ stepheight = 1,
|
|||
reach = 2,
|
||||
damage = 0,
|
||||
hp_min = 10,
|
||||
hp_max = 20,
|
||||
armor = 200,
|
||||
hp_max = 40,
|
||||
armor = 100,
|
||||
collisionbox = {-0.268, -0.01, -0.268, 0.268, 0.167, 0.268},
|
||||
visual = "mesh",
|
||||
mesh = "Crab.b3d",
|
||||
|
@ -25,7 +25,7 @@ stepheight = 1,
|
|||
walk_velocity = 0.7,
|
||||
run_velocity = 1,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:elephant", "animalworld:hippo", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "player"},
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:elephant", "animalworld:hippo", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
jump = false,
|
||||
jump_height = 6,
|
||||
drops = {
|
||||
|
@ -49,7 +49,7 @@ stepheight = 1,
|
|||
follow = {"animalworld:rawfish", "default:marram_grass_2"},
|
||||
view_range = 5,
|
||||
replace_rate = 10,
|
||||
replace_what = {"default:marram_grass_2"},
|
||||
replace_what = {"default:marram_grass_2", "ethereal:fish_raw", "fishing:fish_raw", "xocean:fish_edible", "water_life:meat_raw", "mobs:clownfish_raw"},
|
||||
replace_with = "air",
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
|
@ -63,7 +63,7 @@ stepheight = 1,
|
|||
local spawn_on = "default:sand"
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = "ethereal:prairie_dirt"
|
||||
spawn_on = "default:sand"
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
|
@ -73,6 +73,7 @@ mobs:spawn({
|
|||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 0,
|
||||
max_height = 2,
|
||||
})
|
||||
|
|
|
@ -5,10 +5,10 @@ stepheight = 1,
|
|||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
reach = 2,
|
||||
damage = 6,
|
||||
damage = 12,
|
||||
hp_min = 50,
|
||||
hp_max = 75,
|
||||
armor = 200,
|
||||
hp_max = 95,
|
||||
armor = 100,
|
||||
collisionbox = {-0.6, -0.01, -0.6, 0.6, 0.95, 0.6},
|
||||
visual = "mesh",
|
||||
mesh = "Crocodile.b3d",
|
||||
|
@ -50,7 +50,7 @@ stepheight = 1,
|
|||
floats = 0,
|
||||
follow = {
|
||||
"ethereal:fish_raw", "animalworld:rawfish", "mobs_fish:tropical",
|
||||
"mobs:meat_raw", "animalworld:rabbit_raw", "xocean:fish_edible"
|
||||
"mobs:meat_raw", "animalworld:rabbit_raw", "xocean:fish_edible", "fishing:fish_raw", "water_life:meat_raw", "fishing:carp_raw", "animalworld:chicken_raw"
|
||||
},
|
||||
view_range = 12,
|
||||
|
||||
|
@ -70,6 +70,7 @@ mobs:spawn({
|
|||
min_light = 14,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 0,
|
||||
max_height = 3,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -5,10 +5,10 @@ stepheight = 2,
|
|||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
reach = 3,
|
||||
damage = 8,
|
||||
damage = 16,
|
||||
hp_min = 75,
|
||||
hp_max = 100,
|
||||
armor = 200,
|
||||
hp_max = 120,
|
||||
armor = 100,
|
||||
collisionbox = {-2, -0.01, -2, 2, 2, 2},
|
||||
visual = "mesh",
|
||||
mesh = "Elephant.b3d",
|
||||
|
@ -34,6 +34,7 @@ stepheight = 2,
|
|||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 4,
|
||||
pathfinding = true,
|
||||
animation = {
|
||||
speed_normal = 80,
|
||||
stand_start = 0,
|
||||
|
@ -47,10 +48,12 @@ stepheight = 2,
|
|||
|
||||
follow = {
|
||||
"ethereal:banana_single", "farming:corn_cob", "farming:cabbage",
|
||||
"default:apple"
|
||||
"default:apple", "farming:cabbage", "farming:carrot", "farming:cucumber", "farming:grapes", "farming:pineapple", "ethereal:orange", "ethereal:coconut", "ethereal:coconut_slice"
|
||||
},
|
||||
view_range = 6,
|
||||
|
||||
replace_rate = 10,
|
||||
replace_what = {"farming:soil", "farming:soil_wet"},
|
||||
replace_with = "default:dirt",
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
-- feed or tame
|
||||
|
@ -67,6 +70,7 @@ mobs:spawn({
|
|||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 0,
|
||||
max_height = 65,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
mobs:register_mob("animalworld:frog", {
|
||||
stepheight = 3,
|
||||
type = "animal",
|
||||
passive = true,
|
||||
reach = 1,
|
||||
attack_npcs = false,
|
||||
damage = 1,
|
||||
hp_min = 5,
|
||||
hp_max = 25,
|
||||
armor = 100,
|
||||
collisionbox = {-0.268, -0.01, -0.268, 0.268, 0.167, 0.268},
|
||||
visual = "mesh",
|
||||
mesh = "Frog.b3d",
|
||||
drawtype = "front",
|
||||
textures = {
|
||||
{"texturefrog.png"},
|
||||
|
||||
},
|
||||
sounds = {
|
||||
random = "animalworld_frog",},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 2,
|
||||
run_velocity = 3,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 6,
|
||||
animation = {
|
||||
speed_normal = 100,
|
||||
stand_start = 1,
|
||||
stand_end = 100,
|
||||
walk_start = 100,
|
||||
walk_end = 200,
|
||||
fly_start = 250, -- swim animation
|
||||
fly_end = 350,
|
||||
},
|
||||
fly_in = {"default:water_source", "default:river_water_source", "default:water_flowing"},
|
||||
floats = 0,
|
||||
follow = {"fishing:bait:worm", "ethereal:worm"},
|
||||
view_range = 6,
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
if mobs:feed_tame(self, clicker, 8, true, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 0, 5, 50, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
local spawn_on = "default:sand"
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = "ethereal:prairie_dirt", "default:dirt_with_grass", "ethereal:green_dirt"
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:frog",
|
||||
nodes = {"default:dirt_with_grass"}, {"default:dirt_with_rainforest_litter"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = -10,
|
||||
max_height = 2,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
mobs:register_egg("animalworld:frog", ("Frog"), "afrog.png", 0)
|
||||
|
||||
|
||||
mobs:alias_mob("animalworld:frog", "animalworld:frog") -- compatibility
|
||||
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
mobs:register_mob("animalworld:gnu", {
|
||||
stepheight = 2,
|
||||
type = "animal",
|
||||
passive = true,
|
||||
attack_type = "dogfight",
|
||||
group_attack = true,
|
||||
owner_loyal = true,
|
||||
attack_npcs = false,
|
||||
reach = 2,
|
||||
damage = 2,
|
||||
hp_min = 30,
|
||||
hp_max = 60,
|
||||
armor = 100,
|
||||
collisionbox = {-0.6, -0.01, -0.6, 0.6, 0.95, 0.6},
|
||||
visual = "mesh",
|
||||
mesh = "Gnu.b3d",
|
||||
textures = {
|
||||
{"texturegnu.png"},
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
sounds = {
|
||||
random = "animalworld_gnu",
|
||||
|
||||
},
|
||||
walk_velocity = 1,
|
||||
run_velocity = 5,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
jump = false,
|
||||
jump_height = 3,
|
||||
pushable = true,
|
||||
follow = {"default:apple", "default:dry_dirt_with_dry_grass", "farming:seed_wheat", "default:junglegrass", "farming:seed_oat"},
|
||||
view_range = 10,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 5,
|
||||
light_damage = 0,
|
||||
fear_height = 3,
|
||||
animation = {
|
||||
speed_normal = 60,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
walk_start = 100,
|
||||
walk_end = 200,
|
||||
run_start = 200,
|
||||
run_end = 300,
|
||||
|
||||
die_start = 1, -- we dont have a specific death animation so we will
|
||||
die_end = 2, -- re-use 2 standing frames at a speed of 1 fps and
|
||||
die_speed = 1, -- have mob rotate when dying.
|
||||
die_loop = false,
|
||||
die_rotate = true,
|
||||
},
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
if mobs:feed_tame(self, clicker, 8, true, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 0, 5, 50, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
local spawn_on = {"default:dry_dirt_with_dry_grass"}
|
||||
|
||||
if minetest.get_mapgen_setting("mg_name") ~= "v6" then
|
||||
spawn_on = {"default:dry_dirt_with_dry_grass"}
|
||||
end
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = {"default:dry_dirt_with_dry_grass"}
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:gnu",
|
||||
nodes = {"default:dry_dirt_with_dry_grass"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 20,
|
||||
max_height = 50,
|
||||
day_toggle = true,
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:gnu", ("Gnu"), "agnu.png")
|
||||
|
||||
|
||||
mobs:alias_mob("animalworld:gnu", "animalworld:gnu") -- compatibility
|
||||
|
|
@ -4,8 +4,8 @@ stepheight = 1,
|
|||
passive = true,
|
||||
reach = 1,
|
||||
hp_min = 15,
|
||||
hp_max = 20,
|
||||
armor = 200,
|
||||
hp_max = 40,
|
||||
armor = 100,
|
||||
collisionbox = {-0.268, -0.01, -0.268, 0.268, 0.167, 0.268},
|
||||
visual = "mesh",
|
||||
mesh = "Hare.b3d",
|
||||
|
@ -20,7 +20,7 @@ stepheight = 1,
|
|||
walk_velocity = 3,
|
||||
run_velocity = 6,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "player"},
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
drops = {
|
||||
|
@ -40,10 +40,10 @@ stepheight = 1,
|
|||
punch_start = 100,
|
||||
punch_end = 200,
|
||||
},
|
||||
follow = {"farming:carrot", "farming_plus:carrot_item", "default:grass_1"},
|
||||
follow = {"farming:carrot", "farming_plus:carrot_item", "default:grass_1", "farming:carrot_7", "farming:carrot_8", "farming_plus:carrot", "farming:lettuce", "farming:cabbage", "ethereal:snowygrass"},
|
||||
view_range = 8,
|
||||
replace_rate = 10,
|
||||
replace_what = {"farming:carrot_7", "farming:carrot_8", "farming_plus:carrot"},
|
||||
replace_what = {"farming:carrot_7", "farming:carrot_8", "farming_plus:carrot", "farming:lettuce", "farming:cabbage", "ethereal:snowygrass", "flowers:geranium", "flowers:rose"},
|
||||
replace_with = "air",
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
|
@ -104,7 +104,7 @@ stepheight = 1,
|
|||
local spawn_on = "default:dirt_with_grass"
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = "ethereal:prairie_dirt"
|
||||
spawn_on = "ethereal:prairie_dirt", "default:dirt_with_grass"
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
|
@ -114,6 +114,7 @@ mobs:spawn({
|
|||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 5,
|
||||
max_height = 100,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -5,10 +5,10 @@ stepheight = 1,
|
|||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
reach = 3,
|
||||
damage = 6,
|
||||
damage = 12,
|
||||
hp_min = 65,
|
||||
hp_max = 80,
|
||||
armor = 200,
|
||||
hp_max = 100,
|
||||
armor = 100,
|
||||
collisionbox = {-1.4, -0.01, -1.4, 1.4, 1.4, 1.4},
|
||||
visual = "mesh",
|
||||
mesh = "Hippo2.b3d",
|
||||
|
@ -51,7 +51,7 @@ fly_in = {"default:water_source", "default:river_water_source", "default:water_f
|
|||
floats = 0,
|
||||
follow = {
|
||||
"ethereal:banana_single", "farming:corn_cob", "farming:cabbage",
|
||||
"default:apple"
|
||||
"default:apple", "water_life:meat_raw", "xocean:fish_edible", "ethereal:fish_raw", "ethereal:banana", "farming:cabbage", "farming:lettuce", "farming:melon_slice"
|
||||
},
|
||||
|
||||
view_range = 6,
|
||||
|
@ -72,6 +72,7 @@ mobs:spawn({
|
|||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 0,
|
||||
max_height = 5,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
mobs:register_mob("animalworld:hyena", {
|
||||
stepheight = 2,
|
||||
type = "monster",
|
||||
passive = false,
|
||||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
reach = 2,
|
||||
damage = 16,
|
||||
hp_min = 35,
|
||||
hp_max = 65,
|
||||
armor = 100,
|
||||
collisionbox = {-0.5, -0.01, -0.5, 0.5, 0.95, 0.5},
|
||||
visual = "mesh",
|
||||
mesh = "Hyena.b3d",
|
||||
visual_size = {x = 1.0, y = 1.0},
|
||||
textures = {
|
||||
{"texturehyena.png"},
|
||||
},
|
||||
sounds = {
|
||||
random = "animalworld_hyena",
|
||||
attack = "animalworld_hyena",
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 1,
|
||||
run_velocity = 3,
|
||||
runaway = false,
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
stepheight = 2,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 4,
|
||||
animation = {
|
||||
speed_normal = 75,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
walk_start = 150,
|
||||
walk_end = 250,
|
||||
punch_start = 250,
|
||||
punch_end = 350,
|
||||
-- 50-70 is slide/water idle
|
||||
},
|
||||
|
||||
follow = {
|
||||
"ethereal:fish_raw", "animalworld:rawfish", "mobs_fish:tropical",
|
||||
"mobs:meat_raw", "animalworld:rabbit_raw", "animalworld:pork_raw", "water_life:meat_raw", "xocean:fish_edible", "animalworld:chicken_raw"
|
||||
},
|
||||
view_range = 10,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
-- feed or tame
|
||||
if mobs:feed_tame(self, clicker, 4, false, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 5, 50, 80, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:hyena",
|
||||
nodes = {"default:dry_dirt_with_dry_grass"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 30,
|
||||
max_height = 60,
|
||||
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:hyena", ("Hyena"), "ahyena.png")
|
|
@ -42,6 +42,22 @@ dofile(path .. "spider.lua") --
|
|||
dofile(path .. "spidermale.lua") --
|
||||
dofile(path .. "crab.lua") --
|
||||
dofile(path .. "reindeer.lua") --
|
||||
dofile(path .. "volverine.lua") --
|
||||
dofile(path .. "owl.lua") --
|
||||
dofile(path .. "frog.lua") --
|
||||
dofile(path .. "monitor.lua") --
|
||||
dofile(path .. "gnu.lua") --
|
||||
dofile(path .. "puffin.lua") --
|
||||
dofile(path .. "anteater.lua") --
|
||||
dofile(path .. "hyena.lua") --
|
||||
dofile(path .. "rat.lua") --
|
||||
dofile(path .. "vulture.lua") --
|
||||
dofile(path .. "toucan.lua") --
|
||||
dofile(path .. "snowleopard.lua") --
|
||||
dofile(path .. "lobster.lua") --
|
||||
dofile(path .. "squid.lua") --
|
||||
dofile(path .. "kobra.lua") --
|
||||
dofile(path .. "bat.lua") --
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ mobs:register_mob("animalworld:kangaroo", {
|
|||
reach = 2,
|
||||
damage = 2,
|
||||
hp_min = 25,
|
||||
hp_max = 35,
|
||||
armor = 200,
|
||||
hp_max = 55,
|
||||
armor = 100,
|
||||
collisionbox = {-0.5, -0.01, -0.5, 0.5, 0.95, 0.5},
|
||||
visual = "mesh",
|
||||
mesh = "Kangaroo.b3d",
|
||||
|
@ -22,11 +22,11 @@ mobs:register_mob("animalworld:kangaroo", {
|
|||
walk_velocity = 5,
|
||||
run_velocity = 5,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "player"},
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
jump = true,
|
||||
jump_height = 8,
|
||||
pushable = true,
|
||||
follow = {"default:grass_3", "default:dry_grass_3"},
|
||||
follow = {"default:grass_3", "default:dry_grass_3", "ethereal:dry_shrub", "farming:lettuce", "farming:seed_wheat", "default:junglegrass"},
|
||||
view_range = 10,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
|
@ -65,7 +65,7 @@ if minetest.get_mapgen_setting("mg_name") ~= "v6" then
|
|||
end
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = {"ethereal:grass_grove"}
|
||||
spawn_on = {"ethereal:grass_grove", "default:desert_sand", "ethereal:dry_dirt"}
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
|
@ -76,6 +76,7 @@ mobs:spawn({
|
|||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 5,
|
||||
max_height = 45,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
mobs:register_mob("animalworld:kobra", {
|
||||
stepheight = 2,
|
||||
type = "monster",
|
||||
passive = false,
|
||||
attack_type = "dogshoot",
|
||||
dogshoot_switch = 1,
|
||||
dogshoot_count_max = 12, -- shoot for 10 seconds
|
||||
dogshoot_count2_max = 3, -- dogfight for 3 seconds
|
||||
reach = 3,
|
||||
shoot_interval = 2.2,
|
||||
arrow = "animalworld:snakepoison",
|
||||
shoot_offset = 1,
|
||||
attack_animals = true,
|
||||
reach = 2,
|
||||
damage = 8,
|
||||
hp_min = 20,
|
||||
hp_max = 60,
|
||||
armor = 100,
|
||||
collisionbox = {-0.5, -0.01, -0.5, 0.5, 0.95, 0.5},
|
||||
visual = "mesh",
|
||||
mesh = "Kobra.b3d",
|
||||
visual_size = {x = 0.3, y = 0.3},
|
||||
textures = {
|
||||
{"texturekobra.png"},
|
||||
},
|
||||
sounds = {
|
||||
random = "animalworld_kobra",
|
||||
attack = "animalworld_kobra",
|
||||
},
|
||||
makes_footstep_sound = false,
|
||||
view_range = 6,
|
||||
walk_velocity = 1,
|
||||
run_velocity = 2,
|
||||
runaway = false,
|
||||
jump = false,
|
||||
jump_height = 0,
|
||||
stepheight = 2,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
{name = "mobs:leather", chance = 1, min = 0, max = 2},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 3,
|
||||
animation = {
|
||||
speed_normal = 60,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
walk_start = 250,
|
||||
walk_end = 350,
|
||||
punch_start = 150,
|
||||
punch_end = 200,
|
||||
-- 50-70 is slide/water idle
|
||||
},
|
||||
|
||||
fly_in = {"default:water_source", "default:river_water_source", "default:water_flowing"},
|
||||
floats = 0,
|
||||
})
|
||||
|
||||
|
||||
if not mobs.custom_spawn_monster then
|
||||
mobs:spawn({
|
||||
name = "animalworld:kobra",
|
||||
nodes = {"default:desert_sandstone", "default:desert_stone", "default:sandstone", "default:dirt_with_rainforest_litter"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
min_height = -30,
|
||||
max_height = 10,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
mobs:register_egg("animalworld:kobra", ("Cobra"), "akobra.png")
|
||||
|
||||
|
||||
mobs:alias_mob("animalworld:kobra", "animalworld:kobra") -- compatiblity
|
||||
|
||||
-- mese arrow (weapon)
|
||||
mobs:register_arrow("animalworld:snakepoison", {
|
||||
visual = "sprite",
|
||||
-- visual = "wielditem",
|
||||
visual_size = {x = 0.5, y = 0.5},
|
||||
textures = {"animalworld_snakepoison.png"},
|
||||
--textures = {""animalworld_snakepoison.png""},
|
||||
velocity = 6,
|
||||
-- rotate = 180,
|
||||
|
||||
hit_player = function(self, player)
|
||||
player:punch(self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = 2},
|
||||
}, nil)
|
||||
end,
|
||||
|
||||
hit_mob = function(self, player)
|
||||
player:punch(self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = 2},
|
||||
}, nil)
|
||||
end,
|
||||
|
||||
hit_node = function(self, pos, node)
|
||||
end
|
||||
})
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
mobs:register_mob("animalworld:lobster", {
|
||||
stepheight = 1,
|
||||
type = "animal",
|
||||
passive = false,
|
||||
attack_type = "dogfight",
|
||||
group_attack = true,
|
||||
owner_loyal = true,
|
||||
attack_npcs = false,
|
||||
reach = 2,
|
||||
damage = 5,
|
||||
hp_min = 25,
|
||||
hp_max = 70,
|
||||
armor = 100,
|
||||
collisionbox = {-0.6, -0.01, -0.6, 0.6, 0.95, 0.6},
|
||||
visual = "mesh",
|
||||
mesh = "Lobster.b3d",
|
||||
textures = {
|
||||
{"texturelobster.png"},
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
sounds = {
|
||||
},
|
||||
walk_velocity = 0.5,
|
||||
run_velocity = 1,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark"},
|
||||
jump = false,
|
||||
jump_height = 3,
|
||||
pushable = true,
|
||||
follow = {"animalworld:rawfish", "mobs_fish:tropical", "mobs:clownfish_raw",
|
||||
"mobs:bluefish_raw", "fishing:bait_worm", "fishing:clownfish_raw", "fishing:bluewhite_raw", "fishing:exoticfish_raw", "fishing:fish_raw", "fishing:carp_raw", "fishing:perch_raw", "water_life:meat_raw", "fishing:shark_raw", "fishing:pike_raw"},
|
||||
view_range = 10,
|
||||
drops = {
|
||||
{name = "animalworld:raw_athropod", chance = 1, min = 0, max = 2},
|
||||
},
|
||||
fly_in = {"default:water_source", "default:river_water_source", "default:water_flowing"},
|
||||
floats = 0,
|
||||
follow = {
|
||||
"ethereal:fish_raw", "animalworld:rawfish", "mobs_fish:tropical",
|
||||
"mobs:meat_raw", "animalworld:rabbit_raw", "xocean:fish_edible"
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 5,
|
||||
air_damage = 1,
|
||||
light_damage = 0,
|
||||
fear_height = 2,
|
||||
animation = {
|
||||
speed_normal = 50,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
walk_start = 100,
|
||||
walk_end = 200,
|
||||
punch_start = 200,
|
||||
punch_end = 300,
|
||||
|
||||
die_start = 1, -- we dont have a specific death animation so we will
|
||||
die_end = 2, -- re-use 2 standing frames at a speed of 1 fps and
|
||||
die_speed = 1, -- have mob rotate when dying.
|
||||
die_loop = false,
|
||||
die_rotate = true,
|
||||
},
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
if mobs:feed_tame(self, clicker, 8, true, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 0, 5, 50, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
local spawn_on = {"default:water_source"}
|
||||
|
||||
if minetest.get_mapgen_setting("mg_name") ~= "v6" then
|
||||
spawn_on = {"default:water_source"}
|
||||
end
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = {"default:water_source"}
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:lobster",
|
||||
nodes = {"default:water_source"},
|
||||
neighbors = spawn_by,
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 0,
|
||||
max_height = 10,
|
||||
day_toggle = false,
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:lobster", ("Lobster"), "alobster.png")
|
||||
|
||||
|
||||
mobs:alias_mob("animalworld:lobster", "animalworld:lobster") -- compatibility
|
||||
|
|
@ -7,8 +7,8 @@ stepheight = 0.0,
|
|||
reach = 1,
|
||||
damage = 1,
|
||||
hp_min = 50,
|
||||
hp_max = 60,
|
||||
armor = 200,
|
||||
hp_max = 80,
|
||||
armor = 100,
|
||||
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.95, 0.7},
|
||||
visual = "mesh",
|
||||
mesh = "Manatee.b3d",
|
||||
|
@ -50,7 +50,7 @@ stepheight = 0.0,
|
|||
floats = 0,
|
||||
follow = {
|
||||
"default:kelp", "seaweed", "xocean:kelp",
|
||||
"default:grass", "farming:cucumber", "farming:cabbage"
|
||||
"default:grass", "farming:cucumber", "farming:cabbage", "xocean:seagrass", "farming:lettuce", "default:junglegrass"
|
||||
},
|
||||
view_range = 10,
|
||||
|
||||
|
@ -70,6 +70,7 @@ mobs:spawn({
|
|||
min_light = 14,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 0,
|
||||
max_height = 10,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
mobs:register_mob("animalworld:monitor", {
|
||||
stepheight = 1,
|
||||
type = "monster",
|
||||
passive = false,
|
||||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
reach = 2,
|
||||
damage = 8,
|
||||
hp_min = 20,
|
||||
hp_max = 55,
|
||||
armor = 100,
|
||||
collisionbox = {-0.6, -0.01, -0.6, 0.6, 0.95, 0.6},
|
||||
visual = "mesh",
|
||||
mesh = "Monitor.b3d",
|
||||
visual_size = {x = 1.0, y = 1.0},
|
||||
textures = {
|
||||
{"texturemonitor.png"},
|
||||
},
|
||||
sounds = {
|
||||
random = "animalworld_monitor",
|
||||
attack = "animalworld_monitor",
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 1.5,
|
||||
run_velocity = 2.5,
|
||||
runaway = false,
|
||||
jump = true,
|
||||
jump_height = 0.5,
|
||||
stepheight = 1,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 2,
|
||||
animation = {
|
||||
speed_normal = 75,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
walk_start = 100,
|
||||
walk_end = 200,
|
||||
fly_start = 350, -- swim animation
|
||||
fly_end = 450,
|
||||
punch_start = 200,
|
||||
punch_end = 300,
|
||||
-- 50-70 is slide/water idle
|
||||
},
|
||||
fly_in = {"default:water_source", "default:river_water_source", "default:water_flowing"},
|
||||
floats = 0,
|
||||
follow = {
|
||||
"ethereal:fish_raw", "animalworld:rawfish", "mobs_fish:tropical",
|
||||
"mobs:meat_raw", "animalworld:rabbit_raw", "xocean:fish_edible", "animalworld:chicken_raw", "water_life:meat_raw"
|
||||
},
|
||||
view_range = 8,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
-- feed or tame
|
||||
if mobs:feed_tame(self, clicker, 4, false, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 5, 50, 80, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
if not mobs.custom_spawn_monster then
|
||||
mobs:spawn({
|
||||
name = "animalworld:monitor",
|
||||
nodes = {"default:desert_sand"}, {"default:desert_sandstone"}, {"default:sandstone"}, {"ethereal:dry_dirt"}, {"ethereal:fiery_dirt"},
|
||||
min_light = 14,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
min_height = 20,
|
||||
max_height = 45,
|
||||
day_toggle = true,
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:monitor", ("Monitor Lizard"), "amonitor.png")
|
|
@ -7,10 +7,10 @@ mobs:register_mob("animalworld:moose", {
|
|||
owner_loyal = true,
|
||||
attack_npcs = false,
|
||||
reach = 2,
|
||||
damage = 2,
|
||||
damage = 7,
|
||||
hp_min = 25,
|
||||
hp_max = 40,
|
||||
armor = 200,
|
||||
hp_max = 60,
|
||||
armor = 100,
|
||||
collisionbox = {-0.6, -0.01, -0.6, 0.6, 0.95, 0.6},
|
||||
visual = "mesh",
|
||||
mesh = "Moose.b3d",
|
||||
|
@ -29,10 +29,11 @@ mobs:register_mob("animalworld:moose", {
|
|||
jump = false,
|
||||
jump_height = 3,
|
||||
pushable = true,
|
||||
follow = {"default:apple", "farming:potato"},
|
||||
follow = {"default:apple", "farming:potato", "farming:melon_slice", "farming:cucumber", "farming:cabbage", "farming:lettuce", "farming:bread"},
|
||||
view_range = 10,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
{name = "mobs:leather", chance = 1, min = 0, max = 2},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 5,
|
||||
|
@ -68,7 +69,7 @@ if minetest.get_mapgen_setting("mg_name") ~= "v6" then
|
|||
end
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = {"ethereal:grass_grove"}
|
||||
spawn_on = {"ethereal:grass_grove", "default:dirt_with_grass", "default:dirt_with_coniferous_litter"}
|
||||
end
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
|
|
|
@ -7,10 +7,10 @@ stepheight = 1,
|
|||
owner_loyal = true,
|
||||
attack_npcs = false,
|
||||
reach = 2,
|
||||
damage = 2,
|
||||
damage = 5,
|
||||
hp_min = 20,
|
||||
hp_max = 30,
|
||||
armor = 200,
|
||||
hp_max = 50,
|
||||
armor = 100,
|
||||
collisionbox = {-0.5, -0.01, -0.3, 0.5, 0.1, 0.5},
|
||||
visual = "mesh",
|
||||
mesh = "Nandu.b3d",
|
||||
|
@ -90,7 +90,7 @@ stepheight = 1,
|
|||
local spawn_on = {"default:dirt_with_grass"}
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
spawn_on = {"ethereal:bamboo_dirt", "ethereal:prairie_dirt"}
|
||||
spawn_on = {"ethereal:bamboo_dirt", "ethereal:prairie_dirt", "default:dirt_with_grass"}
|
||||
end
|
||||
|
||||
|
||||
|
@ -101,6 +101,7 @@ mobs:spawn({
|
|||
min_light = 14,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 10,
|
||||
max_height = 40,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
mobs:register_mob("animalworld:owl", {
|
||||
stepheight = 3,
|
||||
type = "monster",
|
||||
passive = false,
|
||||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
owner_loyal = true,
|
||||
reach = 2,
|
||||
damage = 3,
|
||||
hp_min = 5,
|
||||
hp_max = 35,
|
||||
armor = 100,
|
||||
collisionbox = {-0.3, -0.01, -1, 0.3, 0.3, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "Owl.b3d",
|
||||
visual_size = {x = 1.0, y = 1.0},
|
||||
textures = {
|
||||
{"textureowl.png"},
|
||||
},
|
||||
sounds = {
|
||||
random = "animalworld_owl",
|
||||
},
|
||||
makes_footstep_sound = false,
|
||||
walk_velocity = 5,
|
||||
run_velocity = 5,
|
||||
fall_speed = 0,
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
stepheight = 3,
|
||||
fly = true,
|
||||
drops = {
|
||||
{name = "animalworld:chicken_raw", chance = 1, min = 1, max = 1},
|
||||
{name = "animalworld:chicken_feather", chance = 1, min = 1, max = 1},
|
||||
|
||||
},
|
||||
water_damage = 1,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 0,
|
||||
animation = {
|
||||
speed_normal = 100,
|
||||
stand_start = 0,
|
||||
stand_end = 90,
|
||||
walk_start = 400,
|
||||
walk_end = 500,
|
||||
fly_start = 400, -- swim animation
|
||||
fly_end = 500,
|
||||
punch_start = 200,
|
||||
punch_end = 300,
|
||||
-- 50-70 is slide/water idle
|
||||
},
|
||||
|
||||
fly_in = {"air"},
|
||||
floats = 0,
|
||||
follow = {
|
||||
"animalworld:rabbit_raw", "mobs:meat_raw", "animalworld:chicken_raw", "water_life:meat_raw"
|
||||
},
|
||||
|
||||
view_range = 6,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
-- feed or tame
|
||||
if mobs:feed_tame(self, clicker, 4, false, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 5, 50, 80, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
if not mobs.custom_spawn_monster then
|
||||
mobs:spawn({
|
||||
name = "animalworld:owl",
|
||||
nodes = {"default:dirt_with_coniferous_litter"}, {"default:pine_needles"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
min_height = 10,
|
||||
max_height = 60,
|
||||
day_toggle = false,
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:owl", ("Owl"), "aowl.png")
|
|
@ -0,0 +1,82 @@
|
|||
mobs:register_mob("animalworld:puffin", {
|
||||
stepheight = 3,
|
||||
type = "animal",
|
||||
passive = true,
|
||||
attack_type = "dogfight",
|
||||
attack_animals = false,
|
||||
reach = 2,
|
||||
damage = 1,
|
||||
hp_min = 5,
|
||||
hp_max = 35,
|
||||
armor = 100,
|
||||
collisionbox = {-0.3, -0.01, -1, 0.3, 0.3, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "Puffin.b3d",
|
||||
visual_size = {x = 1.0, y = 1.0},
|
||||
textures = {
|
||||
{"texturepuffin.png"},
|
||||
},
|
||||
sounds = {
|
||||
},
|
||||
makes_footstep_sound = false,
|
||||
walk_velocity = 5,
|
||||
run_velocity = 6,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
fall_speed = 0,
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
fly = true,
|
||||
stepheight = 3,
|
||||
drops = {
|
||||
{name = "animalworld:chicken_raw", chance = 1, min = 1, max = 1},
|
||||
{name = "animalworld:chicken_feather", chance = 1, min = 1, max = 1},
|
||||
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 0,
|
||||
animation = {
|
||||
speed_normal = 130,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
walk_start = 150,
|
||||
walk_end = 250,
|
||||
fly_start = 150, -- swim animation
|
||||
fly_end = 250,
|
||||
-- 50-70 is slide/water idle
|
||||
},
|
||||
|
||||
fly_in = {"air", "default:water_source", "default:river_water_source"},
|
||||
floats = 0,
|
||||
follow = {
|
||||
"animalworld:rawfish", "mobs:clownfish_raw", "mobs:bluefish_raw", "fishing:bait_worm", "fishing:clownfish_raw", "fishing:bluewhite_raw", "fishing:exoticfish_raw", "fishing:fish_raw", "fishing:carp_raw", "fishing:perch_raw", "xocean:fish_edible"
|
||||
},
|
||||
|
||||
view_range = 4,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
-- feed or tame
|
||||
if mobs:feed_tame(self, clicker, 4, false, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 5, 50, 80, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:puffin",
|
||||
nodes = {"default:snowblock"}, {"default:ice"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = 0,
|
||||
max_height = 10,
|
||||
day_toggle = true,
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:puffin", ("Puffin"), "apuffin.png")
|
|
@ -0,0 +1,96 @@
|
|||
mobs:register_mob("animalworld:rat", {
|
||||
type = "animal",
|
||||
stepheight = 3,
|
||||
passive = false,
|
||||
attack_type = "dogfight",
|
||||
attack_npcs = false,
|
||||
group_attack = true,
|
||||
reach = 2,
|
||||
damage = 3,
|
||||
hp_min = 5,
|
||||
hp_max = 35,
|
||||
armor = 100,
|
||||
collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.2, 0.4},
|
||||
visual = "mesh",
|
||||
mesh = "Rat.b3d",
|
||||
textures = {
|
||||
{"texturerat.png"},
|
||||
{"texturerat.png"},
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
sounds = {
|
||||
random = "animalworld_rat",
|
||||
attack = "animalworld_rat",
|
||||
},
|
||||
walk_velocity = 2,
|
||||
run_velocity = 3,
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
pushable = true,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 3},
|
||||
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 5,
|
||||
light_damage = 0,
|
||||
fear_height = 6,
|
||||
animation = {
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
stand_speed = 50,
|
||||
walk_start = 100,
|
||||
walk_end = 200,
|
||||
walk_speed = 130,
|
||||
punch_start = 250,
|
||||
punch_end = 350,
|
||||
punch_speed = 125,
|
||||
|
||||
die_start = 1, -- we dont have a specific death animation so we will
|
||||
die_end = 2, -- re-use 2 standing frames at a speed of 1 fps and
|
||||
die_speed = 1, -- have mob rotate when dying.
|
||||
die_loop = false,
|
||||
die_rotate = true,
|
||||
},
|
||||
follow = {"farming:wheat", "farming:beans", "farming:barley", "farming:oat", "farming:rye", "mobs:cheese", "farming:bread", "ethereal:banana_bread"},
|
||||
view_range = 10,
|
||||
|
||||
})
|
||||
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:rat",
|
||||
nodes = {"default:stone", "default:mossycobble"},
|
||||
min_light = 14,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 2,
|
||||
min_height = -100,
|
||||
max_height = 0,
|
||||
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
mobs:register_egg("animalworld:rat", ("Rat"), "arat.png")
|
||||
|
||||
|
||||
mobs:alias_mob("animalworld:rat", "animalworld:rat") -- compatibility
|
||||
|
||||
-- cooked rat, yummy!
|
||||
minetest.register_craftitem(":animalworld:rat_cooked", {
|
||||
description = ("Cooked Rodent Meat"),
|
||||
inventory_image = "animalworld_cooked_rat.png",
|
||||
on_use = minetest.item_eat(3),
|
||||
groups = {food_rat = 1, flammable = 2},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "animalworld:rat_cooked",
|
||||
recipe = "animalworld:rat",
|
||||
cooktime = 5,
|
||||
})
|
||||
|
||||
|
|
@ -9,8 +9,8 @@ mobs:register_mob("animalworld:reindeer", {
|
|||
reach = 2,
|
||||
damage = 2,
|
||||
hp_min = 25,
|
||||
hp_max = 30,
|
||||
armor = 200,
|
||||
hp_max = 50,
|
||||
armor = 100,
|
||||
collisionbox = {-0.6, -0.01, -0.6, 0.6, 0.95, 0.6},
|
||||
visual = "mesh",
|
||||
mesh = "Reindeer.b3d",
|
||||
|
@ -24,11 +24,11 @@ mobs:register_mob("animalworld:reindeer", {
|
|||
walk_velocity = 1,
|
||||
run_velocity = 3,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "player"},
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine", "player"},
|
||||
jump = false,
|
||||
jump_height = 3,
|
||||
pushable = true,
|
||||
follow = {"default:apple", "default:permafrost_with_moss"},
|
||||
follow = {"default:apple", "default:permafrost_with_moss", "ethereal:snowygrass", "ethereal:crystalgrass"},
|
||||
view_range = 10,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
|
@ -73,11 +73,11 @@ end
|
|||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:reindeer",
|
||||
nodes = {"default:dirt_with_snow", "default:permafrost_with_moss"},
|
||||
neighbors = spawn_by,
|
||||
nodes = {"default:dirt_with_snow", "default:permafrost_with_moss", "ethereal:crystal_dirt"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 4,
|
||||
min_height = 20,
|
||||
max_height = 80,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -4,8 +4,8 @@ stepheight = 0.6,
|
|||
passive = true,
|
||||
reach = 1,
|
||||
hp_min = 20,
|
||||
hp_max = 35,
|
||||
armor = 200,
|
||||
hp_max = 55,
|
||||
armor = 100,
|
||||
collisionbox = {-0.4, -0.01, -0.4, 0.4, 0.95, 0.4},
|
||||
visual = "mesh",
|
||||
mesh = "Seal.b3d",
|
||||
|
@ -22,7 +22,7 @@ stepheight = 0.6,
|
|||
run_velocity = 1,
|
||||
runaway = true,
|
||||
runaway = true,
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark"},
|
||||
runaway_from = {"animalworld:bear", "animalworld:crocodile", "animalworld:tiger", "animalworld:spider", "animalworld:spidermale", "animalworld:shark", "animalworld:hyena", "animalworld:kobra", "animalworld:monitor", "animalworld:snowleopard", "animalworld:volverine"},
|
||||
jump = false,
|
||||
stepheight = 1.1,
|
||||
drops = {
|
||||
|
@ -46,7 +46,7 @@ stepheight = 0.6,
|
|||
floats = 0,
|
||||
follow = {
|
||||
"ethereal:fish_raw", "animalworld:rawfish", "mobs_fish:tropical",
|
||||
"mobs_fish:clownfish_set", "mobs_fish:tropical_set", "xocean:fish_edible"
|
||||
"mobs_fish:clownfish_set", "mobs_fish:tropical_set", "xocean:fish_edible", "mobs:bluefish_raw"
|
||||
},
|
||||
view_range = 10,
|
||||
|
||||
|
@ -66,6 +66,7 @@ mobs:spawn({
|
|||
min_light = 14,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
active_object_count = 3,
|
||||
min_height = 0,
|
||||
max_height = 10,
|
||||
day_toggle = true,
|
||||
|
|
|
@ -5,10 +5,10 @@ stepheight = 0.0,
|
|||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
reach = 2,
|
||||
damage = 7,
|
||||
damage = 14,
|
||||
hp_min = 30,
|
||||
hp_max = 75,
|
||||
armor = 200,
|
||||
hp_max = 95,
|
||||
armor = 100,
|
||||
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.95, 0.7},
|
||||
visual = "mesh",
|
||||
mesh = "Shark.b3d",
|
||||
|
@ -52,7 +52,7 @@ stepheight = 0.0,
|
|||
fly_in = {"default:water_source", "default:river_water_source", "default:water_flowing"},
|
||||
floats = 0,
|
||||
follow = {
|
||||
"mobs:meat_raw"
|
||||
"mobs:meat_raw", "xocean:fish_edible", "ethereal:fish_raw", "mobs:clownfish_raw", "mobs:bluefish_raw", "fishing:bait_worm", "fishing:clownfish_raw", "fishing:bluewhite_raw", "fishing:exoticfish_raw", "fishing:fish_raw", "fishing:carp_raw", "fishing:perch_raw", "water_life:meat_raw", "fishing:shark_raw", "fishing:pike_raw"
|
||||
},
|
||||
view_range = 17,
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
mobs:register_mob("animalworld:snowleopard", {
|
||||
stepheight = 5,
|
||||
type = "monster",
|
||||
passive = false,
|
||||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
reach = 2,
|
||||
damage = 9,
|
||||
hp_min = 45,
|
||||
hp_max = 75,
|
||||
armor = 100,
|
||||
collisionbox = {-0.5, -0.01, -0.5, 0.5, 0.95, 0.5},
|
||||
visual = "mesh",
|
||||
mesh = "Snowleopard.b3d",
|
||||
visual_size = {x = 1.0, y = 1.0},
|
||||
textures = {
|
||||
{"texturesnowleopard.png"},
|
||||
},
|
||||
sounds = {
|
||||
random = "animalworld_snowleopard",
|
||||
attack = "animalworld_snowleopard",
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 3,
|
||||
run_velocity = 4,
|
||||
runaway = false,
|
||||
jump = true,
|
||||
jump_height = 6,
|
||||
stepheight = 5,
|
||||
drops = {
|
||||
{name = "mobs:meat_raw", chance = 1, min = 1, max = 1},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 10,
|
||||
animation = {
|
||||
speed_normal = 140,
|
||||
stand_start = 0,
|
||||
stand_end = 100,
|
||||
stand_speed = 50,
|
||||
walk_start = 100,
|
||||
walk_end = 200,
|
||||
punch_start = 250,
|
||||
punch_end = 350,
|
||||
-- 50-70 is slide/water idle
|
||||
},
|
||||
|
||||
follow = {
|
||||
"ethereal:fish_raw", "animalworld:rawfish", "mobs_fish:tropical",
|
||||
"mobs:meat_raw", "animalworld:rabbit_raw", "animalworld:pork_raw", "ethereal:fish_raw", "water_life:meat_raw", "animalworld:chicken_raw"
|
||||
},
|
||||
view_range = 15,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
|
||||
-- feed or tame
|
||||
if mobs:feed_tame(self, clicker, 4, false, true) then return end
|
||||
if mobs:protect(self, clicker) then return end
|
||||
if mobs:capture_mob(self, clicker, 5, 50, 80, false, nil) then return end
|
||||
end,
|
||||
})
|
||||
|
||||
if not mobs.custom_spawn_animal then
|
||||
mobs:spawn({
|
||||
name = "animalworld:snowleopard",
|
||||
nodes = {"default:snowblock"}, {"default:dirt_with_snow"}, {"default:permafrost"}, {"default:stone"},
|
||||
min_light = 0,
|
||||
interval = 60,
|
||||
chance = 8000, -- 15000
|
||||
min_height = 60,
|
||||
max_height = 300,
|
||||
|
||||
})
|
||||
end
|
||||
|
||||
mobs:register_egg("animalworld:snowleopard", ("Snow Leopard"), "asnowleopard.png")
|