write something there

This commit is contained in:
N-Nachtigal 2025-05-04 16:01:41 +02:00
commit b4b6c08f4f
8546 changed files with 309825 additions and 0 deletions

21
mods/animalia/.luacheckrc Normal file
View file

@ -0,0 +1,21 @@
max_line_length = 120
globals = {
"minetest",
"VoxelArea",
"mob_core",
"creatura",
"animalia",
"farming",
"mcl_player",
"player_api",
"armor"
}
read_globals = {
"vector",
"ItemStack",
table = {fields = {"copy"}}
}
ignore = {"212/self", "212/this"}

21
mods/animalia/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 ElCeejo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

882
mods/animalia/api/api.lua Normal file
View file

@ -0,0 +1,882 @@
---------
-- API --
---------
-- Math --
local abs = math.abs
local atan2 = math.atan2
local cos = math.cos
local deg = math.deg
local min = math.min
local pi = math.pi
local pi2 = pi * 2
local rad = math.rad
local random = math.random
local sin = math.sin
local sqrt = math.sqrt
local function diff(a, b) -- Get difference between 2 angles
return atan2(sin(b - a), cos(b - a))
end
local function interp_angle(a, b, w)
local cs = (1 - w) * cos(a) + w * cos(b)
local sn = (1 - w) * sin(a) + w * sin(b)
return atan2(sn, cs)
end
local function lerp_step(a, b, dtime, rate)
return min(dtime * rate, abs(diff(a, b)) % (pi2))
end
local function clamp(val, _min, _max)
if val < _min then
val = _min
elseif _max < val then
val = _max
end
return val
end
-- Vector Math --
local vec_add, vec_dir, vec_dist, vec_divide, vec_len, vec_multi, vec_normal,
vec_round, vec_sub = vector.add, vector.direction, vector.distance, vector.divide,
vector.length, vector.multiply, vector.normalize, vector.round, vector.subtract
local dir2yaw = minetest.dir_to_yaw
local yaw2dir = minetest.yaw_to_dir
------------
-- Common --
------------
function animalia.get_average_pos(vectors)
local sum = {x = 0, y = 0, z = 0}
for _, vec in pairs(vectors) do sum = vec_add(sum, vec) end
return vec_divide(sum, #vectors)
end
function animalia.correct_name(str)
if str then
if str:match(":") then str = str:split(":")[2] end
return (string.gsub(" " .. str, "%W%l", string.upper):sub(2):gsub("_", " "))
end
end
---------------------
-- Local Utilities --
---------------------
local function activate_nametag(self)
self.nametag = self:recall("nametag") or nil
if not self.nametag then return end
self.object:set_properties({
nametag = self.nametag,
nametag_color = "#FFFFFF"
})
end
animalia.animate_player = {}
if minetest.get_modpath("default")
and minetest.get_modpath("player_api") then
animalia.animate_player = player_api.set_animation
elseif minetest.get_modpath("mcl_player") then
animalia.animate_player = mcl_player.player_set_animation
end
-----------------------
-- Dynamic Animation --
-----------------------
function animalia.rotate_to_pitch(self)
local rot = self.object:get_rotation()
if self._anim == "fly" then
local vel = vec_normal(self.object:get_velocity())
local step = min(self.dtime * 5, abs(diff(rot.x, vel.y)) % (pi2))
local n_rot = interp_angle(rot.x, vel.y, step)
self.object:set_rotation({
x = clamp(n_rot, -0.75, 0.75),
y = rot.y,
z = rot.z
})
elseif rot.x ~= 0 then
self.object:set_rotation({
x = 0,
y = rot.y,
z = rot.z
})
end
end
function animalia.move_head(self, tyaw, pitch)
local data = self.head_data
if not data then return end
local yaw = self.object:get_yaw()
local pitch_offset = data.pitch_correction or 0
local bone = data.bone or "Head.CTRL"
local _, rot = self.object:get_bone_position(bone)
if not rot then return end
local n_yaw = (tyaw ~= yaw and diff(tyaw, yaw) / 2) or 0
if abs(deg(n_yaw)) > 45 then n_yaw = 0 end
local dir = yaw2dir(n_yaw)
dir.y = pitch or 0
local n_pitch = (sqrt(dir.x^2 + dir.y^2) / dir.z)
if abs(deg(n_pitch)) > 45 then n_pitch = 0 end
if self.dtime then
local yaw_w = lerp_step(rad(rot.z), tyaw, self.dtime, 3)
n_yaw = interp_angle(rad(rot.z), n_yaw, yaw_w)
local rad_offset = rad(pitch_offset)
local pitch_w = lerp_step(rad(rot.x), n_pitch + rad_offset, self.dtime, 3)
n_pitch = interp_angle(rad(rot.x), n_pitch + rad_offset, pitch_w)
end
local pitch_max = pitch_offset + 45
local pitch_min = pitch_offset - 45
self.object:set_bone_position(bone, data.offset,
{x = clamp(deg(n_pitch), pitch_min, pitch_max), y = 0, z = clamp(deg(n_yaw), -45, 45)})
end
function animalia.head_tracking(self)
if not self.head_data then return end
-- Calculate Head Position
local yaw = self.object:get_yaw()
local pos = self.object:get_pos()
if not pos then return end
local y_dir = yaw2dir(yaw)
local offset_h = self.head_data.pivot_h
local offset_v = self.head_data.pivot_v
pos = {
x = pos.x + y_dir.x * offset_h,
y = pos.y + offset_v,
z = pos.z + y_dir.z * offset_h
}
local vel = self.object:get_velocity()
if vec_len(vel) > 2 then
self.head_tracking = nil
animalia.move_head(self, yaw, 0)
return
end
local player = self.head_tracking
local plyr_pos = player and player:get_pos()
if plyr_pos then
plyr_pos.y = plyr_pos.y + 1.4
local dir = vec_dir(pos, plyr_pos)
local tyaw = dir2yaw(dir)
if abs(diff(yaw, tyaw)) > pi / 10
and self._anim == "stand" then
self:turn_to(tyaw, 1)
end
animalia.move_head(self, tyaw, dir.y)
return
elseif self:timer(6)
and random(4) < 2 then
local players = creatura.get_nearby_players(self, 6)
self.head_tracking = #players > 0 and players[random(#players)]
end
animalia.move_head(self, yaw, 0)
end
---------------
-- Utilities --
---------------
function animalia.alias_mob(old_mob, new_mob)
minetest.register_entity(":" .. old_mob, {
on_activate = function(self)
local pos = self.object:get_pos()
minetest.add_entity(pos, new_mob)
self.object:remove()
end,
})
end
------------------------
-- Environment Access --
------------------------
function animalia.has_shared_owner(obj1, obj2)
local ent1 = obj1 and obj1:get_luaentity()
local ent2 = obj2 and obj2:get_luaentity()
if ent1
and ent2 then
return ent1.owner and ent2.owner and ent1.owner == ent2.owner
end
return false
end
function animalia.get_attack_score(entity, attack_list)
local pos = entity.stand_pos
if not pos then return end
local order = entity.order or "wander"
if order ~= "wander" then return 0 end
local target = entity._target or (entity.attacks_players and creatura.get_nearby_player(entity))
local tgt_pos = target and target:get_pos()
if not tgt_pos
or not entity:is_pos_safe(tgt_pos)
or (target:is_player()
and minetest.is_creative_enabled(target:get_player_name())) then
target = creatura.get_nearby_object(entity, attack_list)
tgt_pos = target and target:get_pos()
end
if not tgt_pos then entity._target = nil return 0 end
if target == entity.object then entity._target = nil return 0 end
if animalia.has_shared_owner(entity.object, target) then entity._target = nil return 0 end
local dist = vec_dist(pos, tgt_pos)
local score = (entity.tracking_range - dist) / entity.tracking_range
if entity.trust
and target:is_player()
and entity.trust[target:get_player_name()] then
local trust = entity.trust[target:get_player_name()]
local trust_score = ((entity.max_trust or 10) - trust) / (entity.max_trust or 10)
score = score - trust_score
end
entity._target = target
return score * 0.5, {entity, target}
end
function animalia.get_nearby_mate(self)
local pos = self.object:get_pos()
if not pos then return end
local objects = creatura.get_nearby_objects(self, self.name)
for _, object in ipairs(objects) do
local obj_pos = object and object:get_pos()
local ent = obj_pos and object:get_luaentity()
if obj_pos
and ent.growth_scale == 1
and ent.gender ~= self.gender
and ent.breeding then
return object
end
end
end
function animalia.find_collision(self, dir)
local pos = self.object:get_pos()
local pos2 = vec_add(pos, vec_multi(dir, 16))
local ray = minetest.raycast(pos, pos2, false, false)
for pointed_thing in ray do
if pointed_thing.type == "node" then
return pointed_thing.under
end
end
return nil
end
function animalia.random_drop_item(self, item, chance)
local pos = self.object:get_pos()
if not pos then return end
if random(chance) < 2 then
local object = minetest.add_item(pos, ItemStack(item))
object:add_velocity({
x = random(-2, 2),
y = 1.5,
z = random(-2, 2)
})
end
end
---------------
-- Particles --
---------------
function animalia.particle_spawner(pos, texture, type, min_pos, max_pos)
type = type or "float"
min_pos = min_pos or vec_sub(pos, 1)
max_pos = max_pos or vec_add(pos, 1)
if type == "float" then
minetest.add_particlespawner({
amount = 16,
time = 0.25,
minpos = min_pos,
maxpos = max_pos,
minvel = {x = 0, y = 0.2, z = 0},
maxvel = {x = 0, y = 0.25, z = 0},
minexptime = 0.75,
maxexptime = 1,
minsize = 4,
maxsize = 4,
texture = texture,
glow = 1,
})
elseif type == "splash" then
minetest.add_particlespawner({
amount = 6,
time = 0.25,
minpos = {x = pos.x - 7/16, y = pos.y + 0.6, z = pos.z - 7/16},
maxpos = {x = pos.x + 7/16, y = pos.y + 0.6, z = pos.z + 7/16},
minvel = {x = -1, y = 2, z = -1},
maxvel = {x = 1, y = 5, z = 1},
minacc = {x = 0, y = -9.81, z = 0},
maxacc = {x = 0, y = -9.81, z = 0},
minsize = 2,
maxsize = 4,
collisiondetection = true,
texture = texture,
})
end
end
function animalia.add_food_particle(self, item_name)
local pos, yaw = self.object:get_pos(), self.object:get_yaw()
if not pos then return end
local head = self.head_data
local offset_h = (head and head.pivot_h) or self.width
local offset_v = (head and head.pivot_v) or self.height
local head_pos = {
x = pos.x + sin(yaw) * -offset_h,
y = pos.y + offset_v,
z = pos.z + cos(yaw) * offset_h
}
local def = minetest.registered_items[item_name]
local image = def.inventory_image
if def.tiles then
image = def.tiles[1].name or def.tiles[1]
end
if image then
local crop = "^[sheet:4x4:" .. random(0,3) .. "," .. random(0,3)
minetest.add_particlespawner({
pos = head_pos,
time = 0.5,
amount = 12,
collisiondetection = true,
collision_removal = true,
vel = {min = {x = -1, y = 1, z = -1}, max = {x = 1, y = 2, z = 1}},
acc = {x = 0, y = -9.8, z = 0},
size = {min = 1, max = 2},
texture = image .. crop
})
end
end
function animalia.add_break_particle(pos)
pos = vec_round(pos)
local def = creatura.get_node_def(pos)
local texture = (def.tiles and def.tiles[1]) or def.inventory_image
texture = texture .. "^[resize:8x8"
minetest.add_particlespawner({
amount = 6,
time = 0.1,
minpos = {
x = pos.x,
y = pos.y - 0.49,
z = pos.z
},
maxpos = {
x = pos.x,
y = pos.y - 0.49,
z = pos.z
},
minvel = {x=-1, y=1, z=-1},
maxvel = {x=1, y=2, z=1},
minacc = {x=0, y=-5, z=0},
maxacc = {x=0, y=-9, z=0},
minexptime = 1,
maxexptime = 1.5,
minsize = 1,
maxsize = 2,
collisiondetection = true,
vertical = false,
texture = texture,
})
end
----------
-- Mobs --
----------
function animalia.death_func(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end
function animalia.get_dropped_food(self, item, radius)
local pos = self.object:get_pos()
if not pos then return end
local objects = minetest.get_objects_inside_radius(pos, radius or self.tracking_range)
for _, object in ipairs(objects) do
local ent = object:get_luaentity()
if ent
and ent.name == "__builtin:item"
and ent.itemstring
and ((item and ent.itemstring:match(item))
or self:follow_item(ItemStack(ent.itemstring))) then
return object, object:get_pos()
end
end
end
function animalia.eat_dropped_item(self, item)
local pos = self.object:get_pos()
if not pos then return end
local food = item or animalia.get_dropped_food(self, nil, self.width + 1)
local food_ent = food and food:get_luaentity()
if food_ent then
local food_pos = food:get_pos()
local stack = ItemStack(food_ent.itemstring)
if stack
and stack:get_count() > 1 then
stack:take_item()
food_ent.itemstring = stack:to_string()
else
food:remove()
end
self.object:set_yaw(dir2yaw(vec_dir(pos, food_pos)))
animalia.add_food_particle(self, stack:get_name())
if self.on_eat_drop then
self:on_eat_drop()
end
return true
end
end
function animalia.protect_from_despawn(self)
self._despawn = self:memorize("_despawn", false)
self.despawn_after = self:memorize("despawn_after", false)
end
function animalia.despawn_inactive_mob(self)
local os_time = os.time()
self._last_active = self:recall("_last_active")
if self._last_active
and self.despawn_after then
local last_active = self._last_active
if os_time - last_active > self.despawn_after then
self.object:remove()
return true
end
end
end
function animalia.set_nametag(self, clicker)
local plyr_name = clicker and clicker:get_player_name()
if not plyr_name then return end
local item = clicker:get_wielded_item()
if item
and item:get_name() ~= "animalia:nametag" then
return
end
local name = item:get_meta():get_string("name")
if not name
or name == "" then
return
end
self.nametag = self:memorize("nametag", name)
self.despawn_after = self:memorize("despawn_after", false)
activate_nametag(self)
if not minetest.is_creative_enabled(plyr_name) then
item:take_item()
clicker:set_wielded_item(item)
end
return true
end
function animalia.initialize_api(self)
-- Set Gender
self.gender = self:recall("gender") or nil
if not self.gender then
local genders = {"male", "female"}
self.gender = self:memorize("gender", genders[random(2)])
-- Reset Texture ID
self.texture_no = nil
end
-- Taming/Breeding
self.food = self:recall("food") or 0
self.gotten = self:recall("gotten") or false
self.breeding = false
self.breeding_cooldown = self:recall("breeding_cooldown") or 0
-- Textures/Scale
activate_nametag(self)
if self.growth_scale then
self:memorize("growth_scale", self.growth_scale) -- This is for spawning children
end
self.growth_scale = self:recall("growth_scale") or 1
self:set_scale(self.growth_scale)
local child_textures = self.growth_scale < 0.8 and self.child_textures
local textures = (not child_textures and self[self.gender .. "_textures"]) or self.textures
if child_textures then
if not self.texture_no
or self.texture_no > #child_textures then
self.texture_no = random(#child_textures)
end
self:set_texture(self.texture_no, child_textures)
elseif textures then
if not self.texture_no then
self.texture_no = random(#textures)
end
self:set_texture(self.texture_no, textures)
end
if self.growth_scale < 0.8
and self.child_mesh then
self.object:set_properties({
mesh = self.child_mesh
})
end
end
function animalia.step_timers(self)
local breed_cd = self.breeding_cooldown or 30
local trust_cd = self.trust_cooldown or 0
self.breeding_cooldown = (breed_cd > 0 and breed_cd - self.dtime) or 0
self.trust_cooldown = (trust_cd > 0 and trust_cd - self.dtime) or 0
if self.breeding
and self.breeding_cooldown <= 30 then
self.breeding = false
end
self:memorize("breeding_cooldown", self.breeding_cooldown)
self:memorize("trust_cooldown", self.trust_cooldown)
self:memorize("_last_active", os.time())
end
function animalia.do_growth(self, interval)
if self.growth_scale
and self.growth_scale < 0.9 then
if self:timer(interval) then
self.growth_scale = self.growth_scale + 0.1
self:set_scale(self.growth_scale)
if self.growth_scale < 0.8
and self.child_textures then
local tex_no = self.texture_no
if not self.child_textures[tex_no] then
tex_no = 1
end
self:set_texture(tex_no, self.child_textures)
elseif self.growth_scale == 0.8 then
if self.child_mesh then self:set_mesh() end
if self.male_textures
and self.female_textures then
if #self.child_textures == 1 then
self.texture_no = random(#self[self.gender .. "_textures"])
end
self:set_texture(self.texture_no, self[self.gender .. "_textures"])
else
if #self.child_textures == 1 then
self.texture_no = random(#self.textures)
end
self:set_texture(self.texture_no, self.textures)
end
if self.on_grown then
self:on_grown()
end
end
self:memorize("growth_scale", self.growth_scale)
end
end
end
function animalia.random_sound(self)
if self:timer(8)
and random(4) < 2 then
self:play_sound("random")
end
end
function animalia.add_trust(self, player, amount)
if self.trust_cooldown > 0 then return end
self.trust_cooldown = 60
local plyr_name = player:get_player_name()
local trust = self.trust[plyr_name] or 0
if trust > 4 then return end
self.trust[plyr_name] = trust + (amount or 1)
self:memorize("trust", self.trust)
end
function animalia.feed(self, clicker, tame, breed)
local yaw = self.object:get_yaw()
local pos = self.object:get_pos()
if not pos then return end
local name = clicker:is_player() and clicker:get_player_name()
local item, item_name = self:follow_wielded_item(clicker)
if item_name then
-- Eat Animation
local head = self.head_data
local offset_h = (head and head.pivot_h) or 0.5
local offset_v = (head and head.pivot_v) or 0.5
local head_pos = {
x = pos.x + sin(yaw) * -offset_h,
y = pos.y + offset_v,
z = pos.z + cos(yaw) * offset_h
}
local def = minetest.registered_items[item_name]
if def.inventory_image then
minetest.add_particlespawner({
pos = head_pos,
time = 0.1,
amount = 3,
collisiondetection = true,
collision_removal = true,
vel = {min = {x = -1, y = 3, z = -1}, max = {x = 1, y = 4, z = 1}},
acc = {x = 0, y = -9.8, z = 0},
size = {min = 2, max = 4},
texture = def.inventory_image
})
end
-- Increase Health
local feed_no = (self.feed_no or 0) + 1
local max_hp = self.max_health
local hp = self.hp
hp = hp + (max_hp / 5)
if hp > max_hp then hp = max_hp end
self.hp = hp
-- Tame/Breed
if feed_no >= 5 then
feed_no = 0
if tame then
self.owner = self:memorize("owner", name)
minetest.add_particlespawner({
pos = {min = vec_sub(pos, self.width), max = vec_add(pos, self.width)},
time = 0.1,
amount = 12,
vel = {min = {x = 0, y = 3, z = 0}, max = {x = 0, y = 4, z = 0}},
size = {min = 4, max = 6},
glow = 16,
texture = "creatura_particle_green.png"
})
end
if breed then
if self.breeding then return false end
if self.breeding_cooldown <= 0 then
self.breeding = true
self.breeding_cooldown = 60
animalia.particle_spawner(pos, "heart.png", "float")
end
end
self._despawn = self:memorize("_despawn", false)
self.despawn_after = self:memorize("despawn_after", false)
end
self.feed_no = feed_no
-- Take item
if not minetest.is_creative_enabled(name) then
item:take_item()
clicker:set_wielded_item(item)
end
return true
end
end
function animalia.mount(self, player, params)
if not creatura.is_alive(player)
or (player:get_attach()
and player:get_attach() ~= self.object) then
return
end
local plyr_name = player:get_player_name()
if (player:get_attach()
and player:get_attach() == self.object)
or not params then
player:set_detach()
player:set_properties({
visual_size = {
x = 1,
y = 1
}
})
player:set_eye_offset()
if minetest.get_modpath("player_api") then
animalia.animate_player(player, "stand", 30)
if player_api.player_attached then
player_api.player_attached[plyr_name] = false
end
end
return
end
if minetest.get_modpath("player_api") then
player_api.player_attached[plyr_name] = true
end
self.rider = player
player:set_attach(self.object, "Torso", params.pos, params.rot)
player:set_eye_offset({x = 0, y = 20, z = 5}, {x = 0, y = 15, z = 15})
self:clear_utility()
minetest.after(0.4, function()
animalia.animate_player(player, "sit" , 30)
end)
end
function animalia.punch(self, puncher, ...)
if self.hp <= 0 then return end
creatura.basic_punch_func(self, puncher, ...)
self._puncher = puncher
if self.flee_puncher
and (self:get_utility() or "") ~= "animalia:flee_from_target" then
self:clear_utility()
end
end
function animalia.find_crop(self)
local pos = self.object:get_pos()
if not pos then return end
local nodes = minetest.find_nodes_in_area(vec_sub(pos, 6), vec_add(pos, 6), "group:crop") or {}
if #nodes < 1 then return end
return nodes[math.random(#nodes)]
end
function animalia.eat_crop(self, pos)
local node_name = minetest.get_node(pos).name
local new_name = node_name:sub(1, #node_name - 1) .. (tonumber(node_name:sub(-1)) or 2) - 1
local new_def = minetest.registered_nodes[new_name]
if not new_def then return false end
local p2 = new_def.place_param2 or 1
minetest.set_node(pos, {name = new_name, param2 = p2})
animalia.add_food_particle(self, new_name)
return true
end
function animalia.eat_turf(mob, pos)
for name, sub_name in pairs(mob.consumable_nodes) do
if minetest.get_node(pos).name == name then
--add_break_particle(turf_pos)
minetest.set_node(pos, {name = sub_name})
mob.collected = mob:memorize("collected", false)
--creatura.action_idle(mob, 1, "eat")
return true
end
end
end
--------------
-- Spawning --
--------------
animalia.registered_biome_groups = {}
function animalia.register_biome_group(name, def)
animalia.registered_biome_groups[name] = def
animalia.registered_biome_groups[name].biomes = {}
end
local function assign_biome_group(name)
local def = minetest.registered_biomes[name]
local turf = def.node_top
local heat = def.heat_point or 0
local humidity = def.humidity_point or 50
local y_min = def.y_min
local y_max = def.y_max
for group, params in pairs(animalia.registered_biome_groups) do -- k, v in pairs
if name:find(params.name_kw or "")
and turf and turf:find(params.turf_kw or "")
and heat >= params.min_heat
and heat <= params.max_heat
and humidity >= params.min_humidity
and humidity <= params.max_humidity
and (not params.min_height or y_min >= params.min_height)
and (not params.max_height or y_max <= params.max_height) then
table.insert(animalia.registered_biome_groups[group].biomes, name)
end
end
end
minetest.register_on_mods_loaded(function()
for name in pairs(minetest.registered_biomes) do
assign_biome_group(name)
end
end)
animalia.register_biome_group("temperate", {
name_kw = "",
turf_kw = "grass",
min_heat = 45,
max_heat = 70,
min_humidity = 0,
max_humidity = 50
})
animalia.register_biome_group("urban", {
name_kw = "",
turf_kw = "grass",
min_heat = 0,
max_heat = 100,
min_humidity = 0,
max_humidity = 100
})
animalia.register_biome_group("grassland", {
name_kw = "",
turf_kw = "grass",
min_heat = 45,
max_heat = 90,
min_humidity = 0,
max_humidity = 80
})
animalia.register_biome_group("boreal", {
name_kw = "",
turf_kw = "litter",
min_heat = 10,
max_heat = 55,
min_humidity = 0,
max_humidity = 80
})
animalia.register_biome_group("ocean", {
name_kw = "ocean",
turf_kw = "",
min_heat = 0,
max_heat = 100,
min_humidity = 0,
max_humidity = 100,
max_height = 0
})
animalia.register_biome_group("swamp", {
name_kw = "",
turf_kw = "",
min_heat = 55,
max_heat = 90,
min_humidity = 55,
max_humidity = 90,
max_height = 10,
min_height = -20
})
animalia.register_biome_group("tropical", {
name_kw = "",
turf_kw = "litter",
min_heat = 70,
max_heat = 90,
min_humidity = 65,
max_humidity = 90
})
animalia.register_biome_group("cave", {
name_kw = "under",
turf_kw = "",
min_heat = 0,
max_heat = 100,
min_humidity = 0,
max_humidity = 100,
max_height = 5
})
animalia.register_biome_group("common", {
name_kw = "",
turf_kw = "",
min_heat = 25,
max_heat = 75,
min_humidity = 20,
max_humidity = 80,
min_height = 1
})

251
mods/animalia/api/lasso.lua Normal file
View file

@ -0,0 +1,251 @@
-----------
-- Lasso --
-----------
local abs = math.abs
local vec_add, vec_dir, vec_dist, vec_len = vector.add, vector.direction, vector.distance, vector.length
local dir2rot = vector.dir_to_rotation
-- Entities --
local using_lasso = {}
minetest.register_entity("animalia:lasso_entity", {
visual = "mesh",
mesh = "animalia_lasso_entity.b3d",
textures = {"animalia_lasso_entity.png"},
pointable = false,
on_activate = function(self)
self.object:set_armor_groups({immortal = 1})
end,
_scale = 1,
on_step = function(self)
local pos, parent = self.object:get_pos(), (self.object:get_attach() or self._attached)
local pointed_ent = self._point_to and self._point_to:get_luaentity()
local point_to = self._point_to and self._point_to:get_pos()
if not pos or not parent or not point_to then self.object:remove() return end
if type(parent) == "string" then
parent = minetest.get_player_by_name(parent)
if not parent then self.object:remove() return end
local tgt_pos = parent:get_pos()
tgt_pos.y = tgt_pos.y + 1
point_to.y = point_to.y + pointed_ent.height * 0.5
local dist = vec_dist(pos, tgt_pos)
if dist > 0.5 then
self.object:set_pos(tgt_pos)
else
self.object:move_to(tgt_pos)
end
self.object:set_rotation(dir2rot(vec_dir(tgt_pos, point_to)))
elseif parent.x then
point_to.y = point_to.y + pointed_ent.height * 0.5
self.object:set_rotation(dir2rot(vec_dir(pos, point_to)))
else
self.object:remove()
return
end
local size = vec_dist(pos, point_to)
if abs(size - self._scale) > 0.1 then
self.object:set_properties({
visual_size = {x = 1, y = 1, z = size}
})
self._scale = size
end
end
})
local function remove_from_fence(self)
local pos = self.object:get_pos()
local mob = self._mob and self._mob:get_luaentity()
if not mob then
self.object:remove()
return
end
mob._lassod_to = nil
mob:forget("_lassod_to")
mob._lasso_ent:remove()
local dirs = {
{x = 0.5, y = 0, z = 0},
{x = -0.5, y = 0, z = 0},
{x = 0, y = 0.5, z = 0},
{x = 0, y = -0.5, z = 0},
{x = 0, y = 0, z = 0.5},
{x = 0, y = 0, z = -0.5}
}
for i = 1, 6 do
local i_pos = vec_add(pos, dirs[i])
if not creatura.get_node_def(i_pos).walkable then
minetest.add_item(i_pos, "animalia:lasso")
break
end
end
self.object:remove()
end
minetest.register_entity("animalia:tied_lasso_entity", {
collisionbox = {-0.25,-0.25,-0.25, 0.25,0.25,0.25},
visual = "cube",
visual_size = {x = 0.3, y = 0.3},
mesh = "model",
textures = {
"animalia_tied_lasso_entity.png",
"animalia_tied_lasso_entity.png",
"animalia_tied_lasso_entity.png",
"animalia_tied_lasso_entity.png",
"animalia_tied_lasso_entity.png",
"animalia_tied_lasso_entity.png",
},
on_activate = function(self)
self.object:set_armor_groups({immortal = 1})
end,
on_step = function(self)
local mob = self._mob and self._mob:get_luaentity()
if not mob then remove_from_fence(self) return end
end,
on_rightclick = remove_from_fence,
on_punch = remove_from_fence
})
-- API --
local function add_lasso(self, origin)
local pos = self.object:get_pos()
if not pos then return end
local object = minetest.add_entity(pos, "animalia:lasso_entity")
local ent = object and object:get_luaentity()
if not ent then return end
-- Attachment point of entity
ent._attached = origin
if type(origin) ~= "string" then
--local player = minetest.get_player_by_name(origin)
--object:set_attach(player)
--else
object:set_pos(origin)
end
self._lassod_to = origin
ent._point_to = self.object
self:memorize("_lassod_to", origin)
return object
end
local function get_rope_velocity(pos1, pos2, dist)
local force = dist / 10
local vel = vector.new((pos2.x - pos1.x) * force, ((pos2.y - pos1.y) / (24 + dist)), (pos2.z - pos1.z) * force)
return vel
end
function animalia.initialize_lasso(self)
self._lassod_to = self:recall("_lassod_to") or self:recall("lasso_origin")
if self._lassod_to then
local origin = self._lassod_to
if type(origin) == "table"
and minetest.get_item_group(minetest.get_node(origin).name, "fence") > 0 then
local object = minetest.add_entity(origin, "animalia:tied_lasso_entity")
object:get_luaentity()._mob = self.object
self._lasso_ent = add_lasso(self, origin)
elseif type(origin) == "string" then
self._lassod_to = origin
self._lasso_ent = add_lasso(self, origin)
else
self:forget("_lassod_to")
end
end
end
function animalia.update_lasso_effects(self)
local pos = self.object:get_pos()
if not creatura.is_alive(self) then return end
if self._lassod_to then
local lasso = self._lassod_to
self._lasso_ent = self._lasso_ent or add_lasso(self, lasso)
if type(lasso) == "string" then
using_lasso[lasso] = self
local name = lasso
lasso = minetest.get_player_by_name(lasso)
if lasso then
if lasso:get_wielded_item():get_name() ~= "animalia:lasso" then
using_lasso[name] = nil
self._lasso_ent:remove()
self._lasso_ent = nil
self._lassod_to = nil
self:forget("_lassod_to")
return
end
local lasso_pos = lasso:get_pos()
local dist = vec_dist(pos, lasso_pos)
local vel = self.object:get_velocity()
if not vel or dist < 8 and self.touching_ground then return end
if vec_len(vel) < 8 then
self.object:add_velocity(get_rope_velocity(pos, lasso_pos, dist))
end
return
end
elseif type(lasso) == "table" then
local dist = vec_dist(pos, lasso)
local vel = self.object:get_velocity()
if not vel or dist < 8 and self.touching_ground then return end
if vec_len(vel) < 8 then
self.object:add_velocity(get_rope_velocity(pos, lasso, dist))
end
return
end
end
if self._lasso_ent then
self._lasso_ent:remove()
self._lasso_ent = nil
self._lassod_to = nil
self:forget("_lassod_to")
end
end
-- Item
minetest.register_craftitem("animalia:lasso", {
description = "Lasso",
inventory_image = "animalia_lasso.png",
on_secondary_use = function(_, placer, pointed)
local ent = pointed.ref and pointed.ref:get_luaentity()
if ent
and (ent.name:match("^animalia:")
or ent.name:match("^monstrum:")) then
if not ent.catch_with_lasso then return end
local name = placer:get_player_name()
if not ent._lassod_to
and not using_lasso[name] then
using_lasso[name] = ent
ent._lassod_to = name
ent:memorize("_lassod_to", name)
elseif ent._lassod_to
and ent._lassod_to == name then
using_lasso[name] = nil
ent._lassod_to = nil
ent:forget("_lassod_to")
end
end
end,
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local pos = minetest.get_pointed_thing_position(pointed_thing)
if minetest.get_item_group(minetest.get_node(pos).name, "fence") > 0 then
local name = placer:get_player_name()
local ent = using_lasso[name]
if ent
and ent._lassod_to
and ent._lassod_to == name then
using_lasso[name] = nil
ent._lasso_ent:set_detach()
ent._lasso_ent:set_pos(pos)
ent._lasso_ent:get_luaentity()._attached = pos
ent._lassod_to = pos
ent:memorize("_lassod_to", pos)
local fence_obj = minetest.add_entity(pos, "animalia:tied_lasso_entity")
fence_obj:get_luaentity()._mob = ent.object
fence_obj:get_luaentity()._lasso_obj = ent._lasso_ent
itemstack:take_item(1)
end
end
end
return itemstack
end
})

View file

@ -0,0 +1,82 @@
--------------------------------------
-- Convert Better Fauna to Animalia --
--------------------------------------
for i = 1, #animalia.mobs do
local new_mob = animalia.mobs[i]
local old_mob = "better_fauna:" .. new_mob:split(":")[2]
minetest.register_entity(":" .. old_mob, {
on_activate = mob_core.on_activate
})
minetest.register_alias_force("better_fauna:spawn_" .. new_mob:split(":")[2],
"animalia:spawn_" .. new_mob:split(":")[2])
end
minetest.register_globalstep(function(dtime)
local mobs = minetest.luaentities
for _, mob in pairs(mobs) do
if mob
and mob.name:match("better_fauna:") then
local pos = mob.object:get_pos()
if not pos then return end
if mob.name:find("lasso_fence_ent") then
if pos then
minetest.add_entity(pos, "animalia:lasso_fence_ent")
end
mob.object:remove()
elseif mob.name:find("lasso_visual") then
if pos then
minetest.add_entity(pos, "animalia:lasso_visual")
end
mob.object:remove()
end
for i = 1, #animalia.mobs do
local ent = animalia.mobs[i]
local new_name = ent:split(":")[2]
local old_name = mob.name:split(":")[2]
if new_name == old_name then
if pos then
local new_mob = minetest.add_entity(pos, ent)
local mem = nil
if mob.memory then
mem = mob.memory
end
minetest.after(0.1, function()
if mem then
new_mob:get_luaentity().memory = mem
new_mob:get_luaentity():on_activate(new_mob, nil, dtime)
end
end)
end
mob.object:remove()
end
end
end
end
end)
-- Tools
minetest.register_alias_force("better_fauna:net", "animalia:net")
minetest.register_alias_force("better_fauna:lasso", "animalia:lasso")
minetest.register_alias_force("better_fauna:cat_toy", "animalia:cat_toy")
minetest.register_alias_force("better_fauna:saddle", "animalia:saddle")
minetest.register_alias_force("better_fauna:shears", "animalia:shears")
-- Drops
minetest.register_alias_force("better_fauna:beef_raw", "animalia:beef_raw")
minetest.register_alias_force("better_fauna:beef_cooked", "animalia:beef_cooked")
minetest.register_alias_force("better_fauna:bucket_milk", "animalia:bucket_milk")
minetest.register_alias_force("better_fauna:leather", "animalia:leather")
minetest.register_alias_force("better_fauna:chicken_egg", "animalia:chicken_egg")
minetest.register_alias_force("better_fauna:chicken_raw", "animalia:poultry_raw")
minetest.register_alias_force("better_fauna:chicken_cooked", "animalia:poultry_cooked")
minetest.register_alias_force("better_fauna:feather", "animalia:feather")
minetest.register_alias_force("better_fauna:mutton_raw", "animalia:mutton_raw")
minetest.register_alias_force("better_fauna:mutton_cooked", "animalia:mutton_cooked")
minetest.register_alias_force("better_fauna:porkchop_raw", "animalia:porkchop_raw")
minetest.register_alias_force("better_fauna:porkchop_cooked", "animalia:porkchop_cooked")
minetest.register_alias_force("better_fauna:turkey_raw", "animalia:poultry_raw")
minetest.register_alias_force("better_fauna:turkey_cooked", "animalia:poultry_cooked")

528
mods/animalia/api/libri.lua Normal file
View file

@ -0,0 +1,528 @@
-----------
-- Libri --
-----------
local libri = {}
local path = minetest.get_modpath(minetest.get_current_modname())
local color = minetest.colorize
local libri_bg = {
"formspec_version[3]",
"size[16,10]",
"background[-0.7,-0.5;17.5,11.5;animalia_libri_bg.png]"
}
local libri_btn_next = "image_button[15,9;1,1;animalia_libri_icon_next.png;btn_next;;true;false]"
local libri_btn_last = "image_button[1,9;1,1;animalia_libri_icon_last.png;btn_last;;true;false]"
local libri_drp_font_scale = "dropdown[17,0;0.75,0.5;drp_font_scale;0.25,0.5,0.75,1;1]"
local function correct_string(str)
if str then
if str:match(":") then str = str:split(":")[2] end
return (string.gsub(" " .. str, "%W%l", string.upper):sub(2):gsub("_", " "))
end
end
local pages = {}
local generate_mobs = {
["animalia:bat"] = "Bat",
["animalia:cat"] = "Cat",
["animalia:chicken"] = "Chicken",
["animalia:cow"] = "Cow",
["animalia:opossum"] = "Opossum",
["animalia:owl"] = "Owl",
["animalia:tropical_fish"] = "Tropical Fish",
["animalia:fox"] = "Fox",
["animalia:frog"] = "Frog",
["animalia:grizzly_bear"] = "Grizzly Bear",
["animalia:horse"] = "Horse",
["animalia:pig"] = "Pig",
["animalia:rat"] = "Rat",
["animalia:reindeer"] = "Reindeer",
["animalia:sheep"] = "Sheep",
["animalia:song_bird"] = "Song Bird",
["animalia:turkey"] = "Turkey",
["animalia:wolf"] = "Wolf",
}
local spawn_biomes = {
["animalia:bat"] = "cave",
["animalia:cat"] = "urban",
["animalia:chicken"] = "tropical",
["animalia:cow"] = "grassland",
["animalia:opossum"] = "temperate",
["animalia:owl"] = "temperate",
["animalia:tropical_fish"] = "ocean",
["animalia:fox"] = "boreal",
["animalia:frog"] = "swamp",
["animalia:grizzly_bear"] = "boreal",
["animalia:horse"] = "grassland",
["animalia:pig"] = "temperate",
["animalia:rat"] = "urban",
["animalia:reindeer"] = "boreal",
["animalia:sheep"] = "grassland",
["animalia:song_bird"] = "temperate",
["animalia:turkey"] = "boreal",
["animalia:wolf"] = "boreal",
}
-----------
-- Pages --
-----------
local function get_spawn_biomes(name)
local biomes = asuna.features.animals[name] or {}
return (#biomes > 0) and biomes or {"grassland"}
end
local function can_lasso(name)
return tostring(minetest.registered_entities[name].catch_with_lasso or false)
end
local function can_net(name)
return tostring(minetest.registered_entities[name].catch_with_net or false)
end
local function max_health(name)
return minetest.registered_entities[name].max_health or 20
end
local function mob_textures(name, mesh_no)
local def = minetest.registered_entities[name]
local textures = def.textures
if def.male_textures
or def.female_textures then
textures = {unpack(def.male_textures), unpack(def.female_textures)}
end
if def.mesh_textures then
textures = def.mesh_textures[mesh_no or 1]
end
return textures
end
local biome_cubes = {}
local function generate_page(mob)
local name = mob:split(":")[2]
local def = minetest.registered_entities[mob]
if not def then return end
local page = {
{ -- Info
element_type = "label",
center_text = true,
font_size = 20,
offset = {x = 8, y = 1.5},
file = "animalia_libri_" .. name .. ".txt"
},
{ -- Image
element_type = "model",
offset = {x = 1.5, y = 1.5},
size = {x = 5, y = 5},
mesh_iter = def.meshes and 1,
texture_iter = 1,
text = "mesh;" .. def.mesh .. ";" .. mob_textures(mob)[1] .. ";-30,225;false;false;0,0;0"
},
{ -- Spawn Biome
element_type = "image",
offset = {x = 0.825, y = 8.15},
size = {x = 1, y = 1},
biome_iter = 1,
text = biome_cubes[get_spawn_biomes(mob)[1]]
},
{ -- Biome Label
element_type = "tooltip",
offset = {x = 0.825, y = 8.15},
size = {x = 1, y = 1},
biome_iter = 1,
text = correct_string(get_spawn_biomes(mob)[1])
},
libri.render_element({ -- Health Icon
element_type = "image",
offset = {x = 2.535, y = 8.15},
size = {x = 1, y = 1},
text = "animalia_libri_health_fg.png"
}),
libri.render_element({ -- Health Amount
element_type = "label",
offset = {x = 3.25, y = 9},
text = "x" .. max_health(mob) / 2
}),
libri.render_element({ -- Lasso Icon
element_type = "item_image",
offset = {x = 4.25, y = 8.15},
size = {x = 1, y = 1},
text = "animalia:lasso"
}),
libri.render_element({ -- Lasso Indication Icon
element_type = "image",
offset = {x = 4.75, y = 8.75},
size = {x = 0.5, y = 0.5},
text = "animalia_libri_" .. can_lasso(mob) .. "_icon.png"
}),
libri.render_element({ -- Net Icon
element_type = "item_image",
offset = {x = 6, y = 8.15},
size = {x = 1, y = 1},
text = "animalia:net"
}),
libri.render_element({ -- Net Indication Icon
element_type = "image",
offset = {x = 6.5, y = 8.75},
size = {x = 0.5, y = 0.5},
text = "animalia_libri_" .. can_net(mob) .. "_icon.png"
}),
libri.render_element({ -- Styling
element_type = "image",
offset = {x = -0.7, y = -0.5},
size = {x = 17.5, y = 11.5},
text = "animalia_libri_info_fg.png"
})
}
pages[mob] = page
end
minetest.register_on_mods_loaded(function()
-- Register Biome Cubes
for name, def in pairs(minetest.registered_biomes) do
if def.node_top then
local tiles = {
"unknown_node.png",
"unknown_node.png",
"unknown_node.png"
}
local n_def = minetest.registered_nodes[def.node_top]
if n_def then
local def_tiles = table.copy(n_def.tiles or n_def.textures)
for i, tile in ipairs(def_tiles) do
if tile.name then
def_tiles[i] = tile.name
end
end
tiles = (#def_tiles > 0 and def_tiles) or tiles
end
biome_cubes[name] = minetest.inventorycube(tiles[1], tiles[3], tiles[3])
else
biome_cubes[name] = minetest.inventorycube("unknown_node.png", "unknown_node.png", "unknown_node.png")
end
end
pages = {
["home_1"] = {
{ -- Info
element_type = "label",
center_text = true,
font_size = 24,
offset = {x = 0, y = 1.5},
file = "animalia_libri_home.txt"
},
{
element_type = "mobs",
start_iter = 0,
offset = {x = 10.25, y = 1.5}
}
},
["home_2"] = {
{
element_type = "mobs",
start_iter = 4,
offset = {x = 1.75, y = 1.5}
}
},
["home_3"] = {
{
element_type = "mobs",
start_iter = 12,
offset = {x = 1.75, y = 1.5}
}
}
}
for mob in pairs(generate_mobs) do
generate_page(mob)
end
end)
---------
-- API --
---------
local function get_item_list(list, offset_x, offset_y) -- Creates a visual list of items for Libri formspecs
local size = 1 / ((#list < 3 and #list) or 3)
if size < 0.45 then size = 0.45 end
local spacing = size * 0.5
local total_scale = size + spacing
local max_horiz = 3
local form = ""
for i, item in ipairs(list) do
local vert_multi = math.floor((i - 1) / max_horiz)
local horz_multi = (total_scale * max_horiz) * vert_multi
local pos_x = offset_x + ((total_scale * i) - horz_multi)
local pos_y = offset_y + (total_scale * vert_multi )
form = form .. "item_image[" .. pos_x .. "," .. pos_y .. ";" .. size .. "," .. size .. ";" .. item .. "]"
end
return form
end
function libri.generate_list(meta, offset, start_iter)
local chapters = minetest.deserialize(meta:get_string("chapters")) or {}
local i = 0
local elements = ""
local offset_x = offset.x
local offset_y = offset.y
for mob in pairs(chapters) do
if not mob then break end
i = i + 1
if i > start_iter then
local mob_name = mob:split(":")[2]
local offset_txt = offset_x .. "," .. offset_y
local element = "button[" .. offset_txt .. ";4,1;btn_" .. mob_name .. ";" .. correct_string(mob_name) .. "]"
elements = elements .. element
offset_y = offset_y + 2
if offset_y > 7.5 then
offset_x = offset_x + 8.5
if offset_x > 10.25 then
return elements
end
offset_y = 1.5
end
end
end
return elements
end
function libri.render_element(def, meta, playername)
local chapters = (meta and minetest.deserialize(meta:get_string("chapters"))) or {}
local chap_no = 0
for _ in pairs(chapters) do
chap_no = chap_no + 1
end
local offset_x = def.offset.x
local offset_y = def.offset.y
local form = ""
-- Add text
if def.element_type == "label" then
local font_size_x = (animalia.libri_font_size[playername] or 1)
local font_size = (def.font_size or 16) * font_size_x
if def.file then
local filename = path .. "/libri/" .. def.file
local file = io.open(filename)
if file then
local text = ""
for line in file:lines() do
text = text .. line .. "\n"
end
local total_offset = (offset_x + (0.35 - 0.35 * font_size_x)) .. "," .. offset_y
form = form ..
"hypertext[" .. total_offset .. ";8,9;text;<global color=#000000 size="..
font_size .. " halign=center>" .. text .. "]"
file:close()
end
else
form = form .. "style_type[label;font_size=" .. font_size .. "]"
local line = def.text
form = form .. "label[" .. offset_x .. "," .. offset_y .. ";" .. color("#000000", line .. "\n") .. "]"
end
elseif def.element_type == "mobs" then
form = form .. libri.generate_list(meta, def.offset, def.start_iter)
if chap_no > def.start_iter + 4 then form = form .. libri_btn_next end
if def.start_iter > 3 then form = form .. libri_btn_last end
else
-- Add Images/Interaction
local render_element = false
if def.unlock_key
and #chapters > 0 then
for _, chapter in ipairs(chapters) do
if chapter
and chapter == def.unlock_key then
render_element = true
break
end
end
elseif not def.unlock_key then
render_element = true
end
if render_element then
local offset = def.offset.x .. "," .. def.offset.y
local size = def.size.x .. "," .. def.size.y
form = form .. def.element_type .. "[" .. offset .. ";" .. size .. ";" .. def.text .. "]"
end
end
return form
end
local function get_page(key, meta, playername)
local form = table.copy(libri_bg)
local chapters = minetest.deserialize(meta:get_string("chapters")) or {}
local chap_no = 0
for _ in pairs(chapters) do
chap_no = chap_no + 1
end
local page = pages[key]
for _, element in ipairs(page) do
if type(element) == "table" then
local element_rendered = libri.render_element(element, meta, playername)
table.insert(form, element_rendered)
else
table.insert(form, element)
end
end
table.insert(form, "style[drp_font_scale;noclip=true]")
table.insert(form, libri_drp_font_scale)
table.insert(form, "style[drp_font_scale;noclip=true]")
local def = minetest.registered_entities[key]
if def then
if def.follow then
table.insert(form, get_item_list(def.follow, 12.45, 8.05))
end
if def.drops then
local drops = {}
for i = 1, #def.drops do
table.insert(drops, def.drops[i].name)
end
table.insert(form, get_item_list(drops, 8, 8.05))
end
end
return table.concat(form, "")
end
-- Iterate through Animal textures and Biomes
local libri_players = {}
local function iterate_libri_images()
for page, elements in pairs(pages) do
if page ~= "home" then
for _, info in ipairs(elements) do
if info.texture_iter then
local def = minetest.registered_entities[page]
local textures = mob_textures(page, info.mesh_iter)
local tex_i = info.texture_iter
info.texture_iter = (textures[tex_i + 1] and tex_i + 1) or 1
local mesh_i = info.mesh_iter
if info.texture_iter < 2 then -- Only iterate mesh if all textures have been shown
info.mesh_iter = def.meshes and ((def.meshes[mesh_i + 1] and mesh_i + 1) or 1)
textures = mob_textures(page, info.mesh_iter)
end
local mesh = (info.mesh_iter and def.meshes[info.mesh_iter]) or def.mesh
info.text = "mesh;" .. mesh .. ";" .. textures[info.texture_iter] .. ";-30,225;false;false;0,0;0]"
end
if info.biome_iter then
local mobname = page:split(":")[2]
local biomes = asuna.features.animals[mobname] or {}
if biomes[info.biome_iter + 1] then
info.biome_iter = info.biome_iter + 1
else
info.biome_iter = 1
end
local spawn_biome = biomes[info.biome_iter] or "grassland"
if info.element_type == "image" then
info.text = biome_cubes[spawn_biome]
else
info.text = correct_string(asuna.biomes[spawn_biome].name)
end
end
end
end
end
for name, page in pairs(libri_players) do
local player = minetest.get_player_by_name(name)
if player
and spawn_biomes[page] then
local meta = player:get_wielded_item():get_meta()
minetest.show_formspec(name, "animalia:libri_" .. page:split(":")[2], get_page(page, meta, name))
end
end
minetest.after(2, iterate_libri_images)
end
iterate_libri_images()
-- Craftitem
minetest.register_craftitem("animalia:libri_animalia", {
description = "Libri Animalia",
inventory_image = "animalia_libri_animalia.png",
stack_max = 1,
groups = {book = 1},
on_place = function(itemstack, player)
local meta = itemstack:get_meta()
if meta:get_string("pages") ~= "" then meta:set_string("pages", "") end
local name = player:get_player_name()
minetest.show_formspec(name, "animalia:libri_home_1", get_page("home_1", meta, name))
libri_players[name] = "home_1"
end,
on_secondary_use = function(itemstack, player, pointed)
local meta = itemstack:get_meta()
if meta:get_string("pages") ~= "" then meta:set_string("pages", "") end
local chapters = minetest.deserialize(meta:get_string("chapters")) or {}
if pointed
and pointed.type == "object" then
local ent = pointed.ref and pointed.ref:get_luaentity()
if ent
and pages[ent.name]
and not chapters[ent.name] then
chapters[ent.name] = true
itemstack:get_meta():set_string("chapters", minetest.serialize(chapters))
player:set_wielded_item(itemstack)
end
return itemstack
end
local name = player:get_player_name()
minetest.show_formspec(name, "animalia:libri_home_1", get_page("home_1", meta, name))
libri_players[name] = "home_1"
end
})
minetest.register_on_player_receive_fields(function(player, formname, fields)
local plyr_name = player:get_player_name()
local wielded_item = player:get_wielded_item()
local meta = wielded_item:get_meta()
if formname:match("animalia:libri_") then
for page in pairs(pages) do
if not page:match("^home") then
local name = page:split(":")[2]
if fields["btn_" .. name] then
minetest.show_formspec(plyr_name, "animalia:libri_" .. name, get_page(page, meta, plyr_name))
libri_players[plyr_name] = page
return true
end
end
end
if fields.btn_next then
local current_no = tonumber(formname:sub(-1))
local page = "home_" .. current_no + 1
if pages[page] then
minetest.show_formspec(plyr_name, "animalia:libri_" .. page, get_page(page, meta, plyr_name))
libri_players[plyr_name] = page
return true
end
end
if fields.btn_last then
local current_no = tonumber(formname:sub(-1))
local page = "home_" .. current_no - 1
if pages[page] then
minetest.show_formspec(plyr_name, "animalia:libri_" .. page, get_page(page, meta, plyr_name))
libri_players[plyr_name] = page
return true
end
end
if fields.drp_font_scale then
animalia.libri_font_size[plyr_name] = fields.drp_font_scale
local page = libri_players[plyr_name]
if not page then return end
minetest.show_formspec(plyr_name, "animalia:libri_" .. page, get_page(page, meta, plyr_name))
end
if fields.quit or fields.key_enter then
libri_players[plyr_name] = nil
end
end
end)

2065
mods/animalia/api/mob_ai.lua Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,409 @@
--------------
-- Spawning --
--------------
local random = math.random
local function table_contains(tbl, val)
for _, v in pairs(tbl) do
if v == val then
return true
end
end
return false
end
local common_spawn_chance = tonumber(minetest.settings:get("animalia_common_chance")) or 60000
local ambient_spawn_chance = tonumber(minetest.settings:get("animalia_ambient_chance")) or 9000
local pest_spawn_chance = tonumber(minetest.settings:get("animalia_pest_chance")) or 3000
local predator_spawn_chance = tonumber(minetest.settings:get("animalia_predator_chance")) or 45000
-- Get Biomes -- already happens via Asuna/biomes.lua
--[[local chicken_biomes = {}
local frog_biomes = {}
local pig_biomes = {}
local function insert_all(tbl, tbl2)
for i = 1, #tbl2 do
table.insert(tbl, tbl2[i])
end
end
minetest.register_on_mods_loaded(function()
insert_all(chicken_biomes, animalia.registered_biome_groups["grassland"].biomes)
insert_all(chicken_biomes, animalia.registered_biome_groups["tropical"].biomes)
insert_all(pig_biomes, animalia.registered_biome_groups["temperate"].biomes)
insert_all(pig_biomes, animalia.registered_biome_groups["boreal"].biomes)
insert_all(frog_biomes, animalia.registered_biome_groups["swamp"].biomes)
insert_all(frog_biomes, animalia.registered_biome_groups["tropical"].biomes)
end)]]
creatura.register_abm_spawn("animalia:grizzly_bear", {
chance = predator_spawn_chance,
min_height = -1,
max_height = 31000,
min_group = 1,
max_group = 1,
biomes = asuna.features.animals.grizzly_bear,
nodes = {"group:sand","group:soil","group:snowy"},
})
creatura.register_abm_spawn("animalia:chicken", {
chance = common_spawn_chance,
spawn_active = true,
min_height = 0,
max_height = 31000,
min_group = 3,
max_group = 5,
spawn_cap = 3,
biomes = asuna.features.animals.chicken,
nodes = {"group:soil"},
})
creatura.register_abm_spawn("animalia:cat", {
chance = common_spawn_chance,
min_height = 0,
max_height = 31000,
min_group = 1,
max_group = 2,
biomes = asuna.features.animals.cat,
nodes = {"group:soil"},
neighbors = {"group:wood"}
})
creatura.register_abm_spawn("animalia:cow", {
chance = common_spawn_chance,
spawn_active = true,
min_height = 0,
max_height = 31000,
min_group = 3,
max_group = 4,
spawn_cap = 3,
biomes = asuna.features.animals.cow,
nodes = {"group:soil"},
neighbors = {"air", "group:grass", "group:flora"}
})
creatura.register_abm_spawn("animalia:fox", {
chance = predator_spawn_chance,
min_height = 0,
max_height = 31000,
min_group = 1,
max_group = 2,
biomes = asuna.features.animals.fox,
nodes = {"group:soil"},
})
creatura.register_abm_spawn("animalia:horse", {
chance = common_spawn_chance,
spawn_active = true,
min_height = 0,
max_height = 31000,
min_group = 3,
max_group = 4,
spawn_cap = 3,
biomes = asuna.features.animals.horse,
nodes = {"group:soil"},
neighbors = {"air", "group:grass", "group:flora"}
})
creatura.register_abm_spawn("animalia:rat", {
chance = pest_spawn_chance,
interval = 60,
min_height = -1,
max_height = 31000,
min_group = 1,
max_group = 3,
spawn_in_nodes = true,
biomes = asuna.features.animals.rat,
nodes = {"group:crop"}
})
creatura.register_abm_spawn("animalia:owl", {
chance = predator_spawn_chance,
interval = 60,
min_height = 3,
max_height = 31000,
min_group = 1,
max_group = 1,
spawn_cap = 1,
biomes = asuna.features.animals.owl,
nodes = {"group:leaves"}
})
creatura.register_abm_spawn("animalia:opossum", {
chance = predator_spawn_chance,
interval = 60,
min_height = -1,
max_height = 31000,
min_group = 1,
max_group = 2,
biomes = asuna.features.animals.opossum,
nodes = {"group:soil", "group:leaves"}
})
creatura.register_abm_spawn("animalia:pig", {
chance = common_spawn_chance,
spawn_active = true,
min_height = 0,
max_height = 31000,
min_group = 2,
max_group = 3,
spawn_cap = 3,
biomes = asuna.features.animals.pig,
nodes = {"group:soil"},
})
creatura.register_abm_spawn("animalia:reindeer", {
chance = common_spawn_chance,
spawn_active = true,
min_height = 0,
max_height = 31000,
min_group = 6,
max_group = 8,
spawn_cap = 3,
biomes = asuna.features.animals.reindeer,
nodes = {"group:soil"},
})
creatura.register_abm_spawn("animalia:sheep", {
chance = common_spawn_chance,
spawn_active = true,
min_height = 0,
max_height = 31000,
min_group = 3,
max_group = 6,
spawn_cap = 3,
biomes = asuna.features.animals.sheep,
nodes = {"group:soil"},
neighbors = {"air", "group:grass", "group:flora"}
})
creatura.register_abm_spawn("animalia:turkey", {
chance = common_spawn_chance,
spawn_active = true,
min_height = 0,
max_height = 31000,
min_group = 3,
max_group = 4,
spawn_cap = 3,
biomes = asuna.features.animals.turkey,
nodes = {"group:soil"},
})
creatura.register_abm_spawn("animalia:wolf", {
chance = predator_spawn_chance,
min_height = 0,
max_height = 31000,
min_group = 2,
max_group = 3,
biomes = asuna.features.animals.wolf,
nodes = {"group:soil"},
})
-- Ambient Spawning
creatura.register_abm_spawn("animalia:bat", {
chance = ambient_spawn_chance,
interval = 30,
min_light = 0,
min_height = -31000,
max_height = 1,
min_group = 3,
max_group = 5,
spawn_cap = 6,
biomes = asuna.features.animals.bat,
nodes = {"group:stone"}
})
creatura.register_abm_spawn("animalia:song_bird", {
chance = ambient_spawn_chance,
interval = 60,
min_light = 0,
min_height = 1,
max_height = 31000,
min_group = 6,
max_group = 12,
spawn_cap = 6,
biomes = asuna.features.animals.song_bird,
nodes = {"group:leaves", "animalia:nest_song_bird"},
neighbors = {"group:leaves"}
})
creatura.register_on_spawn("animalia:song_bird", function(self, pos)
local nests = minetest.find_nodes_in_area_under_air(
{x = pos.x - 16, y = pos.y - 16, z = pos.z - 16},
{x = pos.x + 16, y = pos.y + 16, z = pos.z + 16},
"animalia:nest_song_bird"
)
if nests[1] then return end
local node = minetest.get_node(pos)
if node.name == "air" then
minetest.set_node(pos, {name = "animalia:nest_song_bird"})
else
local nodes = minetest.find_nodes_in_area_under_air(
{x = pos.x - 3, y = pos.y - 3, z = pos.z - 3},
{x = pos.x + 3, y = pos.y + 7, z = pos.z + 3},
"group:leaves"
)
if nodes[1] then
pos = nodes[1]
minetest.set_node({x = pos.x, y = pos.y + 1, z = pos.z}, {name = "animalia:nest_song_bird"})
end
end
end)
creatura.register_abm_spawn("animalia:frog", {
chance = ambient_spawn_chance * 0.75,
interval = 60,
min_light = 0,
min_height = -1,
max_height = 8,
min_group = 1,
max_group = 2,
--neighbors = {"group:water"},
nodes = {"group:soil"}
})
creatura.register_on_spawn("animalia:frog", function(self, pos)
local biome_data = minetest.get_biome_data(pos)
local biome_name = minetest.get_biome_name(biome_data.biome)
if table_contains(animalia.registered_biome_groups["tropical"].biomes, biome_name) then
self:set_mesh(3)
elseif table_contains(animalia.registered_biome_groups["temperate"].biomes, biome_name)
or table_contains(animalia.registered_biome_groups["boreal"].biomes, biome_name) then
self:set_mesh(1)
elseif table_contains(animalia.registered_biome_groups["grassland"].biomes, biome_name) then
self:set_mesh(2)
else
self.object:remove()
end
local activate = self.activate_func
activate(self)
end)
creatura.register_abm_spawn("animalia:tropical_fish", {
chance = ambient_spawn_chance,
min_height = -40,
max_height = -2,
min_group = 6,
max_group = 12,
biomes = asuna.features.animals.tropical_fish,
nodes = {"group:water","mapgen_water_source"},
neighbors = {"group:coral","group:sand"},
spawn_in_nodes = true,
})
-- World Gen Spawning
minetest.register_node("animalia:spawner", {
description = "???",
drawtype = "airlike",
walkable = false,
pointable = false,
buildable_to = true,
paramtype = "light",
sunlight_propagates = true,
groups = {oddly_breakable_by_hand = 1, not_in_creative_inventory = 1}
})
minetest.register_decoration({
name = "animalia:world_gen_spawning",
deco_type = "simple",
place_on = {"group:stone", "group:sand", "group:soil"},
sidelen = 1,
fill_ratio = 0.0001, -- One node per chunk
decoration = "animalia:spawner"
})
local function do_on_spawn(pos, obj)
local name = obj and obj:get_luaentity().name
if not name then return end
local spawn_functions = creatura.registered_on_spawns[name] or {}
if #spawn_functions > 0 then
for _, func in ipairs(spawn_functions) do
func(obj:get_luaentity(), pos)
if not obj:get_yaw() then break end
end
end
end
minetest.register_abm({
label = "[animalia] World Gen Spawning",
nodenames = {"animalia:spawner"},
interval = 10, -- TODO: Set this to 1 if world is singleplayer and just started
chance = 16,
action = function(pos, _, active_object_count)
minetest.remove_node(pos)
if active_object_count > 4 then return end
local spawnable_mobs = {}
local current_biome = minetest.get_biome_name(minetest.get_biome_data(pos).biome)
local spawn_definitions = creatura.registered_mob_spawns
for mob, def in pairs(spawn_definitions) do
if mob:match("^animalia:")
and def.biomes
and table_contains(def.biomes, current_biome) then
table.insert(spawnable_mobs, mob)
end
end
if #spawnable_mobs > 0 then
local mob_to_spawn = spawnable_mobs[math.random(#spawnable_mobs)]
local spawn_definition = creatura.registered_mob_spawns[mob_to_spawn]
local group_size = random(spawn_definition.min_group or 1, spawn_definition.max_group or 1)
local obj
local mob_positions
if group_size > 1 then
mob_positions = {}
local offset
local spawn_pos
for _ = 1, group_size do
offset = group_size * 0.5
spawn_pos = creatura.get_ground_level({
x = pos.x + random(-offset, offset),
y = pos.y,
z = pos.z + random(-offset, offset)
}, 3)
if not creatura.is_pos_moveable(spawn_pos, 0.5, 0.5) then
table.insert(mob_positions,pos)
else
table.insert(mob_positions,spawn_pos)
end
end
else
mob_positions = { pos }
end
for _,mpos in ipairs(mob_positions) do
local node = minetest.get_node(vector.offset(mpos,0,-1,0)).name
for _,target in ipairs(spawn_definition.nodes) do
if node == target or (target:find("^group:") and minetest.get_item_group(node,target:sub(7)) > 0) then
do_on_spawn(mpos, minetest.add_entity(mpos, mob_to_spawn))
minetest.log("action","[Animalia] [ABM Spawning] Spawned " .. mob_to_spawn .. " at " .. minetest.pos_to_string(mpos))
goto next_mpos
end
end
::next_mpos::
end
end
end
})

View file

@ -0,0 +1,22 @@
local mod_storage = minetest.get_mod_storage()
local data = {
spawn_points = minetest.deserialize(mod_storage:get_string("spawn_points")) or {},
libri_font_size = minetest.deserialize(mod_storage:get_string("libri_font_size")) or {},
}
local function save()
mod_storage:set_string("spawn_points", minetest.serialize(data.spawn_points))
mod_storage:set_string("libri_font_size", minetest.serialize(data.libri_font_size))
end
minetest.register_on_shutdown(save)
minetest.register_on_leaveplayer(save)
local function periodic_save()
save()
minetest.after(120, periodic_save)
end
minetest.after(120, periodic_save)
return data

View file

@ -0,0 +1,796 @@
----------------
-- Craftitems --
----------------
local random = math.random
local vec_add, vec_sub = vector.add, vector.subtract
local color = minetest.colorize
local function correct_name(str)
if str then
if str:match(":") then str = str:split(":")[2] end
return (string.gsub(" " .. str, "%W%l", string.upper):sub(2):gsub("_", " "))
end
end
local function register_egg(name, def)
minetest.register_entity(def.mob .. "_egg_entity", {
hp_max = 1,
physical = true,
collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
visual = "sprite",
visual_size = {x = 0.5, y = 0.5},
textures = {def.inventory_image .. ".png"},
initial_sprite_basepos = {x = 0, y = 0},
is_visible = true,
on_step = function(self, _, moveresult)
local pos = self.object:get_pos()
if not pos then return end
if moveresult.collides then
for _, collision in ipairs(moveresult.collisions) do
if collision.type == "nodes" then
minetest.add_particlespawner({
amount = 6,
time = 0.1,
minpos = {x = pos.x - 7/16, y = pos.y - 5/16, z = pos.z - 7/16},
maxpos = {x = pos.x + 7/16, y = pos.y - 5/16, z = pos.z + 7/16},
minvel = {x = -1, y = 2, z = -1},
maxvel = {x = 1, y = 5, z = 1},
minacc = {x = 0, y = -9.8, z = 0},
maxacc = {x = 0, y = -9.8, z = 0},
collisiondetection = true,
collision_removal = true,
texture = "animalia_egg_fragment.png"
})
break
elseif collision.type == "object" then
collision.object:punch(self.object, 2.0, {full_punch_interval = 0.1, damage_groups = {fleshy = 1}}, nil)
break
end
end
if random(3) < 2 then
local object = minetest.add_entity(pos, def.mob)
local ent = object and object:get_luaentity()
if not ent then return end
ent.growth_scale = 0.7
animalia.initialize_api(ent)
animalia.protect_from_despawn(ent)
end
self.object:remove()
end
end
})
minetest.register_craftitem(name, {
description = def.description,
inventory_image = def.inventory_image .. ".png",
on_use = function(itemstack, player)
local pos = player:get_pos()
minetest.sound_play("default_place_node_hard", {
pos = pos,
gain = 1.0,
max_hear_distance = 5,
})
local vel = 19
local gravity = 9
local object = minetest.add_entity({
x = pos.x,
y = pos.y + 1.5,
z = pos.z
}, def.mob .. "_egg_entity")
local dir = player:get_look_dir()
object:set_velocity({
x = dir.x * vel,
y = dir.y * vel,
z = dir.z * vel
})
object:set_acceleration({
x = dir.x * -3,
y = -gravity,
z = dir.z * -3
})
itemstack:take_item()
return itemstack
end,
groups = {food_egg = 1, flammable = 2},
})
minetest.register_craftitem(name .. "_fried", {
description = "Fried " .. def.description,
inventory_image = def.inventory_image .. "_fried.png",
on_use = minetest.item_eat(4),
groups = {food_egg = 1, flammable = 2},
})
minetest.register_craft({
type = "cooking",
recipe = name,
output = name .. "_fried",
})
end
local function mob_storage_use(itemstack, player, pointed)
local ent = pointed.ref and pointed.ref:get_luaentity()
if ent
and (ent.name:match("^animalia:")
or ent.name:match("^monstrum:")) then
local desc = itemstack:get_short_description()
if itemstack:get_count() > 1 then
local name = itemstack:get_name()
local inv = player:get_inventory()
if inv:room_for_item("main", {name = name}) then
itemstack:take_item(1)
inv:add_item("main", name)
end
return itemstack
end
local plyr_name = player:get_player_name()
local meta = itemstack:get_meta()
local mob = meta:get_string("mob") or ""
if mob == "" then
animalia.protect_from_despawn(ent)
meta:set_string("mob", ent.name)
meta:set_string("staticdata", ent:get_staticdata())
local ent_name = correct_name(ent.name)
local ent_gender = correct_name(ent.gender)
desc = desc .. " \n" .. color("#a9a9a9", ent_name) .. "\n" .. color("#a9a9a9", ent_gender)
if ent.trust
and ent.trust[plyr_name] then
desc = desc .. "\n Trust: " .. color("#a9a9a9", ent.trust[plyr_name])
end
meta:set_string("description", desc)
player:set_wielded_item(itemstack)
ent.object:remove()
return itemstack
else
minetest.chat_send_player(plyr_name,
"This " .. desc .. " already contains a " .. correct_name(mob))
end
end
end
-----------
-- Drops --
-----------
minetest.register_craftitem("animalia:leather", {
description = "Leather",
inventory_image = "animalia_leather.png",
groups = {flammable = 2, leather = 1},
})
minetest.register_craftitem("animalia:feather", {
description = "Feather",
inventory_image = "animalia_feather.png",
groups = {flammable = 2, feather = 1},
})
minetest.register_craftitem("animalia:pelt_bear", {
description = "Bear Pelt",
inventory_image = "animalia_pelt_bear.png",
groups = {flammable = 2, pelt = 1},
})
-- Meat --
minetest.register_craftitem("animalia:beef_raw", {
description = "Raw Beef",
inventory_image = "animalia_beef_raw.png",
on_use = minetest.item_eat(1),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craftitem("animalia:beef_cooked", {
description = "Steak",
inventory_image = "animalia_beef_cooked.png",
on_use = minetest.item_eat(8),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craft({
type = "cooking",
recipe = "animalia:beef_raw",
output = "animalia:beef_cooked",
})
minetest.register_craftitem("animalia:mutton_raw", {
description = "Raw Mutton",
inventory_image = "animalia_mutton_raw.png",
on_use = minetest.item_eat(1),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craftitem("animalia:mutton_cooked", {
description = "Cooked Mutton",
inventory_image = "animalia_mutton_cooked.png",
on_use = minetest.item_eat(6),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craft({
type = "cooking",
recipe = "animalia:mutton_raw",
output = "animalia:mutton_cooked",
})
minetest.register_craftitem("animalia:rat_raw", {
description = "Raw Rat",
inventory_image = "animalia_rat_raw.png",
on_use = minetest.item_eat(1),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craftitem("animalia:rat_cooked", {
description = "Cooked Rat",
inventory_image = "animalia_rat_cooked.png",
on_use = minetest.item_eat(2),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craft({
type = "cooking",
recipe = "animalia:rat_raw",
output = "animalia:rat_cooked",
})
minetest.register_craftitem("animalia:porkchop_raw", {
description = "Raw Porkchop",
inventory_image = "animalia_porkchop_raw.png",
on_use = minetest.item_eat(1),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craftitem("animalia:porkchop_cooked", {
description = "Cooked Porkchop",
inventory_image = "animalia_porkchop_cooked.png",
on_use = minetest.item_eat(7),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craft({
type = "cooking",
recipe = "animalia:porkchop_raw",
output = "animalia:porkchop_cooked",
})
minetest.register_craftitem("animalia:poultry_raw", {
description = "Raw Poultry",
inventory_image = "animalia_poultry_raw.png",
on_use = minetest.item_eat(1),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craftitem("animalia:poultry_cooked", {
description = "Cooked Poultry",
inventory_image = "animalia_poultry_cooked.png",
on_use = minetest.item_eat(6),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craft({
type = "cooking",
recipe = "animalia:poultry_raw",
output = "animalia:poultry_cooked",
})
minetest.register_craftitem("animalia:venison_raw", {
description = "Raw Venison",
inventory_image = "animalia_venison_raw.png",
on_use = minetest.item_eat(1),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craftitem("animalia:venison_cooked", {
description = "Venison Steak",
inventory_image = "animalia_venison_cooked.png",
on_use = minetest.item_eat(10),
groups = {flammable = 2, meat = 1, food_meat = 1},
})
minetest.register_craft({
type = "cooking",
recipe = "animalia:venison_raw",
output = "animalia:venison_cooked",
})
register_egg("animalia:chicken_egg", {
description = "Chicken Egg",
inventory_image = "animalia_egg",
mob = "animalia:chicken"
})
register_egg("animalia:turkey_egg", {
description = "Turkey Egg",
inventory_image = "animalia_egg",
mob = "animalia:turkey"
})
register_egg("animalia:song_bird_egg", {
description = "Song Bird Egg",
inventory_image = "animalia_song_bird_egg",
mob = "animalia:bird"
})
----------
-- Misc --
----------
minetest.register_craftitem("animalia:bucket_milk", {
description = "Bucket of Milk",
inventory_image = "animalia_milk_bucket.png",
stack_max = 1,
on_use = minetest.item_eat(8, "bucket:bucket_empty"),
groups = {food_milk = 1, flammable = 3},
})
minetest.register_craftitem("animalia:bucket_guano", {
description = "Bucket of Guano",
inventory_image = "animalia_guano_bucket.png",
stack_max = 1,
groups = {flammable = 3},
on_place = function(itemstack, placer, pointed)
local pos = pointed.under
local node = minetest.get_node(pos)
if node
and node.on_rightclick then
return node.on_rightclick(pos, node, placer, itemstack)
end
if minetest.is_protected(pos, placer:get_player_name()) then
return
end
local crops = minetest.find_nodes_in_area_under_air(
vec_sub(pos, 5),
vec_add(pos, 5),
{"group:grass", "group:plant", "group:flora", "group:crop"}
) or {}
local crops_grown = 0
for _, crop in ipairs(crops) do
local crop_name = minetest.get_node(crop).name
local growth_stage = tonumber(crop_name:sub(-1)) or 1
local new_name = crop_name:sub(1, #crop_name - 1) .. (growth_stage + 1)
local new_def = minetest.registered_nodes[new_name]
if new_def then
local p2 = new_def.place_param2 or 1
minetest.set_node(crop, {name = new_name, param2 = p2})
crops_grown = crops_grown + 1
end
end
if crops_grown < 1 then minetest.set_node(pointed.above, {name = "animalia:guano"}) end
local meta = itemstack:get_meta()
local og_item = meta:get_string("original_item")
if og_item == "" then og_item = "bucket:bucket_empty" end
itemstack:replace(ItemStack(og_item))
return itemstack
end
})
minetest.register_node("animalia:nest_song_bird", {
description = "Song Bird Nest",
paramtype = "light",
drawtype = "mesh",
mesh = "animalia_nest.obj",
tiles = {"animalia_nest.png"},
sunlight_propagates = true,
walkable = false,
stack_max = 1,
groups = {snappy = 3, flammable = 3, falling_node = 1},
selection_box = {
type = "fixed",
fixed = {-5 / 16, -0.5, -5 / 16, 5 / 16, -0.31, 5 / 16},
},
node_box = {
type = "fixed",
fixed = {-5 / 16, -0.5, -5 / 16, 5 / 16, -0.31, 5 / 16},
},
drop = {
items = {
{
items = {"animalia:song_bird_egg"},
rarity = 2,
},
{
items = {"animalia:song_bird_egg 2"},
rarity = 4,
},
{
items = {"default:stick"},
}
}
},
})
-----------
-- Tools --
-----------
minetest.register_craftitem("animalia:cat_toy", {
description = "Cat Toy",
inventory_image = "animalia_cat_toy.png",
wield_image = "animalia_cat_toy.png^[transformFYR90",
stack_max = 1
})
local nametag = {}
local function get_rename_formspec(meta)
local tag = meta:get_string("name") or ""
local form = {
"size[8,4]",
"field[0.5,1;7.5,0;name;" .. minetest.formspec_escape("Enter name:") .. ";" .. tag .. "]",
"button_exit[2.5,3.5;3,1;set_name;" .. minetest.formspec_escape("Set Name") .. "]"
}
return table.concat(form, "")
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "animalia:set_name" and fields.name then
local name = player:get_player_name()
if not nametag[name] then
return
end
local itemstack = nametag[name]
if string.len(fields.name) > 64 then
fields.name = string.sub(fields.name, 1, 64)
end
local meta = itemstack:get_meta()
meta:set_string("name", fields.name)
meta:set_string("description", fields.name)
player:set_wielded_item(itemstack)
if fields.quit or fields.key_enter then
nametag[name] = nil
end
end
end)
local function nametag_rightclick(itemstack, player, pointed_thing)
if pointed_thing
and pointed_thing.type == "object" then
return
end
local name = player:get_player_name()
nametag[name] = itemstack
local meta = itemstack:get_meta()
minetest.show_formspec(name, "animalia:set_name", get_rename_formspec(meta))
end
minetest.register_craftitem("animalia:nametag", {
description = "Nametag",
inventory_image = "animalia_nametag.png",
on_rightclick = nametag_rightclick,
on_secondary_use = nametag_rightclick
})
minetest.register_craftitem("animalia:saddle", {
description = "Saddle",
inventory_image = "animalia_saddle.png",
})
minetest.register_tool("animalia:shears", {
description = "Shears",
inventory_image = "animalia_shears.png",
groups = {flammable = 2}
})
minetest.register_craftitem("animalia:net", {
description = "Animal Net",
inventory_image = "animalia_net.png",
stack_max = 1,
on_secondary_use = mob_storage_use,
on_place = function(itemstack, placer, pointed_thing)
local pos = pointed_thing.above
if pos then
local under = minetest.get_node(pointed_thing.under)
local node = minetest.registered_nodes[under.name]
if node and node.on_rightclick then
return node.on_rightclick(pointed_thing.under, under, placer,
itemstack)
end
if pos and not minetest.is_protected(pos, placer:get_player_name()) then
local mob = itemstack:get_meta():get_string("mob")
local staticdata = itemstack:get_meta():get_string("staticdata")
if mob ~= "" then
pos.y = pos.y +
math.abs(
minetest.registered_entities[mob]
.collisionbox[2])
minetest.add_entity(pos, mob, staticdata)
itemstack:get_meta():set_string("mob", nil)
itemstack:get_meta():set_string("staticdata", nil)
itemstack:get_meta():set_string("description", "Animal Net")
end
end
end
return itemstack
end
})
-----------
-- Armor --
-----------
if minetest.get_modpath("3d_armor") then
if armor
and armor.attributes then
table.insert(armor.attributes, "heavy_pelt")
minetest.register_on_punchplayer(function(player, hitter, _, _, _, damage)
local name = player:get_player_name()
if name
and (armor.def[name].heavy_pelt or 0) > 0 then
local hit_ip = hitter:is_player()
if hit_ip and minetest.is_protected(player:get_pos(), "") then
return
else
local player_pos = player:get_pos()
if not player_pos then return end
local biome_data = minetest.get_biome_data(player_pos)
if biome_data.heat < 50 then
player:set_hp(player:get_hp() - (damage / 1.5))
return true
end
end
end
end)
end
armor:register_armor("animalia:coat_bear_pelt", {
description = "Bear Pelt Coat",
inventory_image = "animalia_inv_coat_bear_pelt.png",
groups = {armor_torso = 1, armor_heal = 0, armor_heavy_pelt = 1, armor_use = 1000},
armor_groups = {fleshy = 5}
})
minetest.register_craft({
output = "animalia:coat_bear_pelt",
recipe = {
{"animalia:pelt_bear", "", "animalia:pelt_bear"},
{"animalia:pelt_bear", "animalia:pelt_bear", "animalia:pelt_bear"},
{"animalia:pelt_bear", "animalia:pelt_bear", "animalia:pelt_bear"}
}
})
end
-----------
-- Nodes --
-----------
minetest.register_node("animalia:guano", {
description = "Guano",
tiles = {"animalia_guano.png"},
paramtype = "light",
buildable_to = true,
floodable = true,
walkable = false,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, -0.25, 0.5},
},
},
groups = {crumbly = 3, falling_node = 1, not_in_creative_inventory = 1},
on_punch = function(pos, _, player)
local inv = player:get_inventory()
local stack = ItemStack("animalia:bucket_guano")
if not inv:room_for_item("main", stack) then return end
local item = player:get_wielded_item()
local item_name = item:get_name()
if item_name:match("bucket_empty") then
item:take_item()
stack:get_meta():set_string("original_item", item_name)
inv:add_item("main", stack)
player:set_wielded_item(item)
minetest.remove_node(pos)
end
end
})
minetest.register_node("animalia:crate", {
description = "Animal Crate",
tiles = {"animalia_crate.png", "animalia_crate.png", "animalia_crate_side.png"},
groups = {choppy = 2},
stack_max = 1,
on_secondary_use = mob_storage_use,
preserve_metadata = function(_, _, oldmeta, drops)
for _, stack in pairs(drops) do
if stack:get_name() == "animalia:crate" then
local meta = stack:get_meta()
meta:set_string("mob", oldmeta["mob"])
meta:set_string("staticdata", oldmeta["staticdata"])
meta:set_string("description", oldmeta["description"])
end
end
end,
after_place_node = function(pos, placer, itemstack)
local meta = itemstack:get_meta()
local mob = meta:get_string("mob")
if mob ~= "" then
local nmeta = minetest.get_meta(pos)
nmeta:set_string("mob", mob)
nmeta:set_string("infotext", "Contains a " .. correct_name((mob)))
nmeta:set_string("staticdata", meta:get_string("staticdata"))
nmeta:set_string("description", meta:get_string("description"))
itemstack:take_item()
placer:set_wielded_item(itemstack)
end
end,
on_rightclick = function(pos, _, clicker)
if minetest.is_protected(pos, clicker:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
local mob = meta:get_string("mob")
local staticdata = meta:get_string("staticdata")
if mob ~= "" then
local above = {
x = pos.x,
y = pos.y + 1,
z = pos.z
}
if creatura.get_node_def(above).walkable then
return
end
minetest.add_entity(above, mob, staticdata)
meta:set_string("mob", nil)
meta:set_string("infotext", nil)
meta:set_string("staticdata", nil)
meta:set_string("description", "Animal Crate")
end
end
})
--------------
-- Crafting --
--------------
local steel_ingot = "default:steel_ingot"
minetest.register_on_mods_loaded(function()
if minetest.registered_items[steel_ingot] then return end
for name, _ in pairs(minetest.registered_items) do
if name:find("ingot")
and (name:find("steel")
or name:find("iron")) then
steel_ingot = name
break
end
end
end)
minetest.register_craft({
output = "animalia:cat_toy",
recipe = {
{"", "", "group:thread"},
{"", "group:stick", "group:thread"},
{"group:stick", "", "group:feather"}
}
})
minetest.register_craft({
output = "animalia:cat_toy",
recipe = {
{"", "", "farming:string"},
{"", "group:stick", "farming:string"},
{"group:stick", "", "group:feather"}
}
})
minetest.register_craft({
output = "animalia:lasso",
recipe = {
{"", "group:thread", "group:thread"},
{"", "group:leather", "group:thread"},
{"group:thread", "", ""}
}
})
minetest.register_craft({
output = "animalia:lasso",
recipe = {
{"", "farming:string", "farming:string"},
{"", "group:leather", "farming:string"},
{"farming:string", "", ""}
}
})
minetest.register_craft({
output = "animalia:net",
recipe = {
{"group:thread", "", "group:thread"},
{"group:thread", "", "group:thread"},
{"group:stick", "group:thread", ""}
}
})
minetest.register_craft({
output = "animalia:net",
recipe = {
{"farming:string", "", "farming:string"},
{"farming:string", "", "farming:string"},
{"group:stick", "farming:string", ""}
}
})
minetest.register_craft({
output = "animalia:crate",
recipe = {
{"group:wood", "group:wood", "group:wood"},
{"group:wood", "animalia:net", "group:wood"},
{"group:wood", "group:wood", "group:wood"}
}
})
minetest.register_craft({
output = "animalia:saddle",
recipe = {
{"group:leather", "group:leather", "group:leather"},
{"group:leather", steel_ingot, "group:leather"},
{"group:thread", "", "group:thread"}
}
})
minetest.register_craft({
output = "animalia:saddle",
recipe = {
{"group:leather", "group:leather", "group:leather"},
{"group:leather", steel_ingot, "group:leather"},
{"farming:string", "", "farming:string"}
}
})
minetest.register_craft({
output = "animalia:shears",
recipe = {
{"", steel_ingot, ""},
{"", "group:leather", steel_ingot}
}
})
minetest.register_craft({
output = "animalia:libri_animalia",
recipe = {
{"", "", ""},
{"animalia:feather", "", ""},
{"group:book", "group:color_green", ""}
}
})
minetest.register_craft({
output = "animalia:libri_animalia",
recipe = {
{"", "", ""},
{"animalia:feather", "", ""},
{"group:book", "group:unicolor_green", ""}
}
})
minetest.register_craft({
output = "animalia:libri_animalia 2",
recipe = {
{"", "", ""},
{"animalia:libri_animalia", "group:book", ""},
{"", "", ""}
}
})
minetest.register_on_craft(function(itemstack, _, old_craft_grid)
if itemstack:get_name() == "animalia:libri_animalia"
and itemstack:get_count() > 1 then
for _, old_libri in pairs(old_craft_grid) do
if old_libri:get_meta():get_string("chapters") then
local chapters = old_libri:get_meta():get_string("chapters")
itemstack:get_meta():set_string("chapters", chapters)
return itemstack
end
end
end
end)

205
mods/animalia/init.lua Normal file
View file

@ -0,0 +1,205 @@
animalia = {}
local path = minetest.get_modpath("animalia")
local storage = dofile(path .. "/api/storage.lua")
animalia.spawn_points = storage.spawn_points
animalia.libri_font_size = storage.libri_font_size
animalia.pets = {}
animalia.bones = minetest.registered_items["bonemeal:bone"] and {name = "bonemeal:bone", min = 1, max = 2, chance = 2} or nil
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
animalia.pets[name] = {}
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
animalia.pets[name] = nil
end)
-- Daytime Tracking
animalia.is_day = true
local function is_day()
local time = (minetest.get_timeofday() or 0) * 24000
animalia.is_day = time < 19500 and time > 4500
minetest.after(10, is_day)
end
is_day()
-- Player Effects
animalia.player_effects = {}
local function player_effect_step()
for player, data in pairs(animalia.player_effects) do
if player then
local timer = data.timer - 1
animalia.player_effects[player].timer = timer
local func = data.func
func(minetest.get_player_by_name(player))
if timer <= 0 then
animalia.player_effects[player] = nil
end
end
end
minetest.after(1, player_effect_step)
end
player_effect_step()
function animalia.set_player_effect(player_name, effect, timer)
animalia.player_effects[player_name] = {
func = effect,
timer = timer or 5
}
end
-- Create lists of items for reuse
animalia.food_wheat = {}
animalia.food_seeds = {}
animalia.food_crops = {}
animalia.food_bear = {}
minetest.register_on_mods_loaded(function()
if minetest.get_modpath("farming")
and farming.registered_plants then
for _, def in pairs(farming.registered_plants) do
if def.crop then
table.insert(animalia.food_crops, def.crop)
end
end
end
for name in pairs(minetest.registered_items) do
if (name:match(":wheat")
or minetest.get_item_group(name, "food_wheat") > 0)
and not name:find("seed") then
table.insert(animalia.food_wheat, name)
end
if name:match(":seed_")
or name:match("_seed") then
table.insert(animalia.food_seeds, name)
end
if (minetest.get_item_group(name, "food_berry") > 0
and not name:find("seed"))
or minetest.get_item_group(name, "food_fish") > 0 then
table.insert(animalia.food_bear, name)
end
end
end)
-- Load Files
local function load_file(filepath, filename)
if io.open(filepath .. "/" .. filename, "r") then
dofile(filepath .. "/" .. filename)
else
minetest.log("action", "[Creatura] The file " .. filename .. " could not be loaded.")
end
end
dofile(path.."/api/api.lua")
dofile(path.."/api/mob_ai.lua")
dofile(path.."/api/lasso.lua")
dofile(path.."/craftitems.lua")
animalia.animals = {
"animalia:bat",
"animalia:song_bird",
"animalia:cat",
"animalia:chicken",
"animalia:cow",
"animalia:fox",
"animalia:frog",
"animalia:grizzly_bear",
"animalia:horse",
"animalia:opossum",
"animalia:owl",
"animalia:pig",
"animalia:rat",
"animalia:reindeer",
"animalia:sheep",
"animalia:turkey",
"animalia:tropical_fish",
"animalia:wolf",
}
dofile(path.."/api/api.lua")
load_file(path .. "/mobs", "bat.lua")
load_file(path .. "/mobs", "bear.lua")
load_file(path .. "/mobs", "cat.lua")
load_file(path .. "/mobs", "chicken.lua")
load_file(path .. "/mobs", "cow.lua")
load_file(path .. "/mobs", "fox.lua")
load_file(path .. "/mobs", "frog.lua")
load_file(path .. "/mobs", "horse.lua")
load_file(path .. "/mobs", "opossum.lua")
load_file(path .. "/mobs", "owl.lua")
load_file(path .. "/mobs", "pig.lua")
load_file(path .. "/mobs", "rat.lua")
load_file(path .. "/mobs", "reindeer.lua")
load_file(path .. "/mobs", "sheep.lua")
load_file(path .. "/mobs", "song_bird.lua")
load_file(path .. "/mobs", "turkey.lua")
load_file(path .. "/mobs", "tropical_fish.lua")
load_file(path .. "/mobs", "wolf.lua")
minetest.settings:set_bool("spawn_mobs",asuna.content.menagerie.animals)
if minetest.settings:get_bool("spawn_mobs", true) then
dofile(path.."/api/spawning.lua")
end
dofile(path.."/api/libri.lua")
minetest.register_on_mods_loaded(function()
for name, def in pairs(minetest.registered_entities) do
if def.logic
or def.brainfunc
or def.bh_tree
or def._cmi_is_mob then
local old_punch = def.on_punch
if not old_punch then
old_punch = function() end
end
local on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
old_punch(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
local pos = self.object:get_pos()
if not pos then return end
local plyr_name = puncher:is_player() and puncher:get_player_name()
local pets = (plyr_name and animalia.pets[plyr_name]) or {}
for _, obj in ipairs(pets) do
local ent = obj and obj:get_luaentity()
if ent
and ent.assist_owner then
ent.owner_target = self
end
end
end
def.on_punch = on_punch
minetest.register_entity(":" .. name, def)
end
end
end)
local convert_mobs_redo = minetest.settings:get_bool("convert_redo_items", false)
if convert_mobs_redo then
minetest.register_alias_force("mobs:leather", "animalia:leather")
minetest.register_alias_force("mobs:meat_raw", "animalia:beef_raw")
minetest.register_alias_force("mobs:meat", "animalia:beef_cooked")
minetest.register_alias_force("mobs:lasso", "animalia:lasso")
minetest.register_alias_force("mobs:net", "animalia:net")
minetest.register_alias_force("mobs:shears", "animalia:shears")
minetest.register_alias_force("mobs:saddles", "animalia:saddles")
minetest.register_alias_force("mobs:nametag", "animalia:nametag")
end
minetest.log("action", "[MOD] Animalia [0.6] loaded")

View file

@ -0,0 +1,7 @@
Bats are a unique flying mammal, found
living in large colonies underground. They
sleep clinging to the ceiling during day, and
leave at night in search of food. While the
colony sleeps, Guano accumulates beneath
them. This Guano can be picked up with a
Bucket and used as fertilizer

View file

@ -0,0 +1,5 @@
Song Birds are small and brightly colored,
often seen moving in flocks swarming
through the air near their nests. Each
species has a unique call that can be used
to identify what Birds are around you.

View file

@ -0,0 +1,9 @@
Cats are often found near Human
settlements. While typically skittish, their
trust can initially be gained by feeding
them, and further increased by playing.
They don't follow commands well but will
do better as their trust grows. However,
some habits never die. If you have Cats in
your home, never leave down glass vessels
unless you want them destroyed.

View file

@ -0,0 +1,9 @@
Chickens are a flightless bird, most often
found in Jungles. They live in tight flocks,
wandering along the floor in search of bugs.
Males, also called Roosters, are visually
distinguised from Females, or hens, by their
large tail feathers. Chickens are a popular
choice among farmers since they can
reliably produce eggs, giving a consistent
source of food.

View file

@ -0,0 +1,5 @@
Cows are large bovines that live in herds.
They're a staple of agriculture and are often
farmed on large plots of land. They produce
Milk and can be slaughtered for Beef, one
of the most filling types of meat.

View file

@ -0,0 +1,7 @@
Foxes wander forests, hunting
for their favorite food: Rats.
They can be quite helpful in
keeping rodent populations
down, but also pose a serious
threat to Chickens and
Turkeys.

View file

@ -0,0 +1,7 @@
Frogs are a small amphibian found in
Swamps. The sound of their croaks often fill
the landscape, in addition to the splashes
heard as they flee to the water from
approaching Humans. Like most
amphibians, Frogs breed in water and live in
it exclusively as tadpoles.

View file

@ -0,0 +1,6 @@
Large, territorial Omnivores. Bears can be
found in colder forests searching for berries.
When approached too closely they will lash out
with their massive claws. Their pelt can be
harvested and crafted into a warm coat that
reduces incoming damage in cold weather.

View file

@ -0,0 +1,13 @@
This is a journal of all the
Animals you've met in this
world. When you find an
Animal you wish to log in this
Journal, simply right-click it.
Doing so will give valuable
information on the Animal,
including the biomes it
inhabits, if and and how it can
be tamed, and a general
overview of it's behavior.
Good luck, Explorer!

View file

@ -0,0 +1,11 @@
Horses are a large mammal found in open
plains. They're commonly used for
transportation, as they can run at high
speed for vast distances without tiring. To
tame one, you have to mount it and keep
your view aligned with the Horse's. After
taming, the Horse can be saddled and
ridden. Not every Horse is created equal,
some can jump tremendous heights, and
others can run faster. Horses will pass
these attributes down to their offspring.

View file

@ -0,0 +1,5 @@
A unique Marsupial and a key member
of it's ecosystem. The Opossum helps
keep pests like rodents and insects
under control, allowing plantlife and
crops to flourish.

View file

@ -0,0 +1,6 @@
Owls are a nocturnal bird of prey, most
common in forests. Owls play an important
role in rodent population control, and are
incredibly efficient at it. Their wings are dead
silent, allowing them to avoid detection by
potential prey.

View file

@ -0,0 +1,6 @@
Pigs are medium-sized, forest-dwelling
mammals. Males are differentiated from
Females by their tusks, which they use to
root up food. They're very destructive to
crops, so it's best to keep them well
contained if you choose to farm them.

View file

@ -0,0 +1,6 @@
Rats are common pest on most
farms. They slowly eat away at
crops and often do so in large
numbers. They can also get
into any unlocked storage and
will eat food from it.

View file

@ -0,0 +1,5 @@
Reindeer are among the most common
animals found in arctic biomes. They move
in large herds, grazing on grass. They're a
great alternative to Cattle for those looking
to farm meat in cold regions.

View file

@ -0,0 +1,4 @@
Sheep are a medium mammal that live in
medium-large flocks on open prairies. While
they can be farmed for mutton, they're more
often farmed for their wool.

View file

@ -0,0 +1,4 @@
Tropical Fish travel in schools among coral
reefs. Their brilliant colors are a staple of
the ocean, but they don't offer anything as
far as food or utility.

View file

@ -0,0 +1,7 @@
Turkeys are a flightless bird found in colder
pine forests. As far as utility, they are
similar to Chickens, but they drop more
meat when killed. Similar to Reindeer,
they're a good alternative to other more
common livestock that don't live in the
same cold regions.

View file

@ -0,0 +1,5 @@
Wolves are a relatively large canine found
in pine forests. They're easy to tame with
mutton, and are a very loyal companion.
They can be given various orders, and will
assist you while fighting.

178
mods/animalia/mobs/bat.lua Normal file
View file

@ -0,0 +1,178 @@
---------
-- Bat --
---------
local vec_dist = vector.distance
local function get_home_pos(self)
local pos = self.object:get_pos()
if not pos then return end
local nodes = minetest.find_nodes_in_area(
vector.subtract(pos, 16),
vector.add(pos, 16),
{"group:leaves", "group:stone"}
)
local home_dist
local new_home
for _, n_pos in ipairs(nodes or {}) do
local dist = vec_dist(pos, n_pos)
if not home_dist
or dist < home_dist then
n_pos.y = n_pos.y - 1
if creatura.get_node_def(n_pos).name == "air" then
home_dist = dist
new_home = n_pos
end
end
end
if new_home then
self.home_position = self:memorize("home_position", new_home)
end
end
creatura.register_mob("animalia:bat", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_bat.b3d",
textures = {
"animalia_bat_1.png",
"animalia_bat_2.png",
"animalia_bat_3.png",
},
makes_footstep_sound = false,
-- Creatura Props
max_health = 2,
armor_groups = {fleshy = 100},
damage = 0,
speed = 4,
tracking_range = 12,
max_boids = 3,
despawn_after = 200,
max_fall = 0,
sounds = {
random = {
name = "animalia_bat",
gain = 0.5,
distance = 16
}
},
hitbox = {
width = 0.15,
height = 0.3
},
animations = {
stand = {range = {x = 1, y = 40}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 51, y = 69}, speed = 30, frame_blend = 0.3, loop = true},
fly = {range = {x = 81, y = 99}, speed = 80, frame_blend = 0.3, loop = true},
latch_ceiling = {range = {x = 110, y = 110}, speed = 1, frame_blend = 0, loop = false}
},
follow = {
"butterflies:butterfly_red",
"butterflies:butterfly_white",
"butterflies:butterfly_violet"
},
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = false,
roost_action = animalia.action_latch,
-- Functions
utility_stack = {
animalia.mob_ai.fly_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.bat_seek_home
},
is_home = function(pos, home_pos)
local dist = vec_dist(pos, home_pos)
if dist < 4 then
local above = {x = pos.x, y = pos.y + 1, z = pos.z}
if creatura.get_node_def(above).walkable
or dist < 1 then
return true
end
end
return false
end,
activate_func = function(self)
animalia.initialize_api(self)
self.home_position = self:recall("home_position") or nil
local home_pos = self.home_position
self.is_landed = self:recall("is_landed") or false
self.trust = self:recall("trust") or {}
if not home_pos
or not creatura.get_node_def(home_pos).walkable then
get_home_pos(self)
end
end,
step_func = function(self)
animalia.step_timers(self)
animalia.do_growth(self, 60)
animalia.rotate_to_pitch(self)
animalia.random_sound(self)
if not self.is_landed
or not self.touching_ground then
self.speed = 4
else
self.speed = 1
end
if self:timer(10)
and math.random(10) < 2 then
local anim = self._anim or ""
if anim == "cling" then
local colony = creatura.get_nearby_objects(self, self.name)
local pos = self.object:get_pos()
if not pos then return end
local center = pos
if #colony > 0 then
local pos_sum = center
local pos_ttl = 1
for _, object in ipairs(colony) do
local obj_pos = object and object:get_pos()
if obj_pos then
pos_sum = vector.add(pos_sum, obj_pos)
pos_ttl = pos_ttl + 1
end
end
center = vector.divide(pos_sum, pos_ttl)
end
center = creatura.get_ground_level(center, 8)
if center.y < pos.y then
local under = {x = center.x, y = center.y - 1, z = center.z}
if creatura.get_node_def(under).walkable
and not minetest.is_protected(center, "") then
minetest.set_node(center, {name = "animalia:guano"})
end
end
end
end
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, false) then
animalia.add_trust(self, clicker, 1)
return
end
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:bat", {
col1 = "392517",
col2 = "321b0b"
})

123
mods/animalia/mobs/bear.lua Normal file
View file

@ -0,0 +1,123 @@
----------
-- Bear --
----------
creatura.register_mob("animalia:grizzly_bear", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_bear.b3d",
textures = {
"animalia_bear_grizzly.png"
},
makes_footstep_sound = true,
-- Creatura Props
max_health = 20,
armor_groups = {fleshy = 100},
damage = 6,
speed = 4,
tracking_range = 10,
despawn_after = 1000,
max_fall = 3,
stepheight = 1.1,
sounds = {
random = {
name = "animalia_bear",
gain = 0.5,
distance = 8
},
hurt = {
name = "animalia_bear_hurt",
gain = 0.5,
distance = 8
},
death = {
name = "animalia_bear_death",
gain = 0.5,
distance = 8
}
},
hitbox = {
width = 0.5,
height = 1
},
animations = {
stand = {range = {x = 1, y = 59}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 61, y = 79}, speed = 10, frame_blend = 0.3, loop = true},
run = {range = {x = 81, y = 99}, speed = 20, frame_blend = 0.3, loop = true},
melee = {range = {x = 101, y = 120}, speed = 30, frame_blend = 0.3, loop = false}
},
follow = animalia.food_bear,
drops = {
{name = "animalia:pelt_bear", min = 1, max = 3, chance = 1},
animalia.bones,
},
fancy_collide = false,
-- Behavior Parameters
attacks_players = true,
-- Animalia Parameters
catch_with_net = true,
catch_with_lasso = true,
head_data = {
offset = {x = 0, y = 0.35, z = 0.0},
pitch_correction = -45,
pivot_h = 0.75,
pivot_v = 1
},
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.basic_seek_food,
animalia.mob_ai.basic_attack,
animalia.mob_ai.basic_breed
},
on_eat_drop = function(self)
local feed_no = (self.feed_no or 0) + 1
if feed_no >= 5 then
feed_no = 0
if self.breeding then return false end
if self.breeding_cooldown <= 0 then
self.breeding = true
self.breeding_cooldown = 60
animalia.particle_spawner(self.stand_pos, "heart.png", "float")
end
self._despawn = self:memorize("_despawn", false)
self.despawn_after = self:memorize("despawn_after", false)
end
self.feed_no = feed_no
end,
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self, 0.75, 0.75)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:grizzly_bear", {
col1 = "64361d",
col2 = "2c0d03"
})

227
mods/animalia/mobs/cat.lua Normal file
View file

@ -0,0 +1,227 @@
---------
-- Cat --
---------
local follow = {
"animalia:poultry_raw"
}
if minetest.registered_items["ethereal:fish_raw"] then
follow = {
"ethereal:fish_raw",
"animalia:poultry_raw"
}
end
creatura.register_mob("animalia:cat", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_cat.b3d",
textures = {
"animalia_cat_1.png",
"animalia_cat_2.png",
"animalia_cat_3.png",
"animalia_cat_4.png",
"animalia_cat_5.png",
"animalia_cat_6.png",
"animalia_cat_7.png",
"animalia_cat_8.png",
"animalia_cat_9.png",
"animalia_cat_ash.png",
"animalia_cat_birch.png",
},
use_texture_alpha = false,
makes_footstep_sound = false,
backface_culling = true,
glow = 0,
-- Creatura Props
max_health = 10,
damage = 1,
speed = 3,
tracking_range = 16,
max_boids = 0,
despawn_after = 500,
max_fall = 0,
stepheight = 1.1,
sounds = {
random = {
name = "animalia_cat",
gain = 0.25,
distance = 8
},
purr = {
name = "animalia_cat_purr",
gain = 0.6,
distance = 8
},
hurt = {
name = "animalia_cat_hurt",
gain = 0.25,
distance = 8
},
death = {
name = "animalia_cat_hurt",
gain = 0.25,
distance = 8
}
},
hitbox = {
width = 0.2,
height = 0.4
},
animations = {
stand = {range = {x = 1, y = 60}, speed = 20, frame_blend = 0.3, loop = true},
walk = {range = {x = 70, y = 89}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 100, y = 119}, speed = 40, frame_blend = 0.3, loop = true},
sit = {range = {x = 130, y = 139}, speed = 10, frame_blend = 0.3, loop = true},
},
follow = follow,
drops = {},
-- Behavior Parameters
is_skittish_mob = true,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
head_data = {
offset = {x = 0, y = 0.14, z = 0},
pitch_correction = -25,
pivot_h = 0.4,
pivot_v = 0.4
},
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.cat_seek_vessel,
animalia.mob_ai.cat_stay,
animalia.mob_ai.cat_play_with_owner,
animalia.mob_ai.cat_follow_owner,
animalia.mob_ai.basic_attack,
animalia.mob_ai.basic_breed
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
self.interact_sound_cooldown = 0
self.trust_cooldown = self:recall("trust_cooldown") or 0
self.order = self:recall("order") or "wander"
self.owner = self:recall("owner") or nil
self.trust = self:recall("trust") or {}
if self.owner
and minetest.get_player_by_name(self.owner) then
if not animalia.pets[self.owner][self.object] then
table.insert(animalia.pets[self.owner], self.object)
end
end
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self, 0.75, 0.75)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
if self:timer(1) then
if self.interact_sound_cooldown > 0 then
self.interact_sound_cooldown = self.interact_sound_cooldown - 1
end
end
end,
death_func = animalia.death_func,
deactivate_func = function(self)
if self.owner then
for i, object in ipairs(animalia.pets[self.owner] or {}) do
if object == self.object then
animalia.pets[self.owner][i] = nil
end
end
end
if self.enemies
and self.enemies[1]
and self.memorize then
self.enemies[1] = nil
self.enemies = self:memorize("enemies", self.enemies)
end
end,
on_rightclick = function(self, clicker)
local item_name = clicker:get_wielded_item():get_name()
if item_name == "animalia:net" then return end
local trust = self.trust[clicker:get_player_name()] or 0
local pos = self.object:get_pos()
if not pos then return end
if animalia.feed(self, clicker, true, true) then
animalia.add_trust(self, clicker, 1)
animalia.particle_spawner(pos, "creatura_particle_green.png", "float")
return
end
if animalia.set_nametag(self, clicker) then
return
end
-- Purr to indicate trust level (louder = more trust)
if clicker:get_player_control().sneak then
if self.interact_sound_cooldown <= 0 then
self.sounds["purr"].gain = 0.15 * trust
self.interact_sound_cooldown = 3
self:play_sound("purr")
end
end
if not self.owner
or clicker:get_player_name() ~= self.owner then
return
end
if trust <= 5 then
if self.interact_sound_cooldown <= 0 then
self.interact_sound_cooldown = 3
self:play_sound("random")
end
return
end
if clicker:get_player_control().sneak then
if self.interact_sound_cooldown <= 0 then
self.sounds["purr"].gain = 0.15 * self.trust[self.owner]
self.interact_sound_cooldown = 3
self:play_sound("purr")
end
local order = self.order
if order == "wander" then
minetest.chat_send_player(clicker:get_player_name(), "Cat is following")
self.order = "follow"
self:initiate_utility("animalia:follow_player", self, clicker, true)
self:set_utility_score(0.7)
elseif order == "follow" then
minetest.chat_send_player(clicker:get_player_name(), "Cat is sitting")
self.order = "sit"
self:initiate_utility("animalia:stay", self)
self:set_utility_score(0.5)
else
minetest.chat_send_player(clicker:get_player_name(), "Cat is wandering")
self.order = "wander"
self:set_utility_score(0)
end
self:memorize("order", self.order)
end
end,
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage)
creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage)
self:initiate_utility("animalia:flee_from_player", self, puncher)
self:set_utility_score(1)
animalia.add_trust(self, puncher, -1)
local pos = self.object:get_pos()
animalia.particle_spawner(pos, "creatura_particle_red.png", "float")
end
})
creatura.register_spawn_item("animalia:cat", {
col1 = "db9764",
col2 = "cf8d5a"
})

View file

@ -0,0 +1,143 @@
-------------
-- Chicken --
-------------
creatura.register_mob("animalia:chicken", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_chicken.b3d",
female_textures = {
"animalia_chicken_1.png",
"animalia_chicken_2.png",
"animalia_chicken_3.png"
},
male_textures = {
"animalia_rooster_1.png",
"animalia_rooster_2.png",
"animalia_rooster_3.png"
},
child_textures = {"animalia_chicken_child.png"},
makes_footstep_sound = true,
-- Creatura Props
max_health = 5,
armor_groups = {fleshy = 100},
damage = 0,
speed = 2,
tracking_range = 8,
max_boids = 3,
despawn_after = 500,
max_fall = 0,
stepheight = 1.1,
sounds = {
random = {
name = "animalia_chicken",
gain = 0.5,
distance = 8
},
hurt = {
name = "animalia_chicken_hurt",
gain = 0.5,
distance = 8
},
death = {
name = "animalia_chicken_death",
gain = 0.5,
distance = 8
}
},
hitbox = {
width = 0.25,
height = 0.5
},
animations = {
stand = {range = {x = 1, y = 39}, speed = 20, frame_blend = 0.3, loop = true},
walk = {range = {x = 41, y = 59}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 41, y = 59}, speed = 45, frame_blend = 0.3, loop = true},
eat = {range = {x = 61, y = 89}, speed = 45, frame_blend = 0.3, loop = true},
fall = {range = {x = 91, y = 99}, speed = 70, frame_blend = 0.3, loop = true}
},
follow = animalia.food_seeds,
drops = {
{name = "animalia:poultry_raw", min = 1, max = 3, chance = 1},
{name = "animalia:feather", min = 1, max = 3, chance = 2}
},
-- Behavior Parameters
is_herding_mob = true,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
head_data = {
offset = {x = 0, y = 0.45, z = 0},
pitch_correction = 40,
pivot_h = 0.25,
pivot_v = 0.55
},
move_chance = 2,
idle_time = 1,
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed,
animalia.mob_ai.basic_flee
},
add_child = function(self)
local pos = self.object:get_pos()
if not pos then return end
animalia.particle_spawner(pos, "animalia_egg_fragment.png", "splash", pos, pos)
local object = minetest.add_entity(pos, self.name)
local ent = object:get_luaentity()
ent.growth_scale = 0.7
animalia.initialize_api(ent)
animalia.protect_from_despawn(ent)
end,
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self, 0.75, 0.75)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
if self.fall_start then
self:set_gravity(-4.9)
self:animate("fall")
end
if (self.growth_scale or 1) > 0.8
and self.gender == "female"
and self:timer(60) then
animalia.random_drop_item(self, "animalia:chicken_egg", 10)
end
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, true) then
return
end
animalia.set_nametag(self, clicker)
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:chicken", {
col1 = "c6c6c6",
col2 = "d22222"
})

169
mods/animalia/mobs/cow.lua Normal file
View file

@ -0,0 +1,169 @@
---------
-- Cow --
---------
creatura.register_mob("animalia:cow", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_cow.b3d",
female_textures = {
"animalia_cow_1.png^animalia_cow_udder.png",
"animalia_cow_2.png^animalia_cow_udder.png",
"animalia_cow_3.png^animalia_cow_udder.png",
"animalia_cow_4.png^animalia_cow_udder.png",
"animalia_cow_5.png^animalia_cow_udder.png"
},
male_textures = {
"animalia_cow_1.png",
"animalia_cow_2.png",
"animalia_cow_3.png",
"animalia_cow_4.png",
"animalia_cow_5.png"
},
child_textures = {
"animalia_cow_1.png",
"animalia_cow_2.png",
"animalia_cow_3.png",
"animalia_cow_4.png",
"animalia_cow_5.png"
},
makes_footstep_sound = true,
-- Creatura Props
max_health = 20,
armor_groups = {fleshy = 100},
damage = 0,
speed = 2,
tracking_range = 10,
max_boids = 0,
despawn_after = 500,
max_fall = 3,
stepheight = 1.1,
sounds = {
random = {
name = "animalia_cow",
gain = 0.5,
distance = 8
},
hurt = {
name = "animalia_cow_hurt",
gain = 0.5,
distance = 8
},
death = {
name = "animalia_cow_death",
gain = 0.5,
distance = 8
}
},
hitbox = {
width = 0.5,
height = 1
},
animations = {
stand = {range = {x = 1, y = 59}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 71, y = 89}, speed = 15, frame_blend = 0.3, loop = true},
run = {range = {x = 71, y = 89}, speed = 30, frame_blend = 0.3, loop = true},
},
follow = animalia.food_wheat,
drops = {
{name = "animalia:beef_raw", min = 1, max = 3, chance = 1},
{name = "animalia:leather", min = 1, max = 3, chance = 2},
animalia.bones,
},
fancy_collide = false,
-- Behavior Parameters
is_grazing_mob = true,
is_herding_mob = true,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
consumable_nodes = {
["default:dirt_with_grass"] = "default:dirt",
["default:dry_dirt_with_dry_grass"] = "default:dry_dirt"
},
head_data = {
offset = {x = 0, y = 0.5, z = 0.0},
pitch_correction = -40,
pivot_h = 0.75,
pivot_v = 1
},
wander_action = animalia.action_boid_move,
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed,
animalia.mob_ai.basic_flee
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
self.collected = self:recall("collected") or false
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self, 0.75, 0.75)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, true)
or animalia.set_nametag(self, clicker) then
return
end
local tool = clicker:get_wielded_item()
local name = clicker:get_player_name()
if tool:get_name() == "bucket:bucket_empty" then
if self.growth_scale < 1 then
return
end
if self.collected then
minetest.chat_send_player(name, "This Cow has already been milked.")
return
end
local inv = clicker:get_inventory()
tool:take_item()
clicker:set_wielded_item(tool)
if inv:room_for_item("main", {name = "animalia:bucket_milk"}) then
clicker:get_inventory():add_item("main", "animalia:bucket_milk")
else
local pos = self.object:get_pos()
pos.y = pos.y + 0.5
minetest.add_item(pos, {name = "animalia:bucket_milk"})
end
self.collected = self:memorize("collected", true)
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:cow", {
col1 = "cac3a1",
col2 = "464438"
})

106
mods/animalia/mobs/fox.lua Normal file
View file

@ -0,0 +1,106 @@
---------
-- Fox --
---------
creatura.register_mob("animalia:fox", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_fox.b3d",
textures = {
"animalia_fox_1.png"
},
makes_footstep_sound = false,
-- Creatura Props
max_health = 10,
armor_groups = {fleshy = 100},
damage = 2,
speed = 4,
tracking_range = 16,
max_boids = 0,
despawn_after = 500,
stepheight = 1.1,
sound = {},
hitbox = {
width = 0.35,
height = 0.5
},
animations = {
stand = {range = {x = 1, y = 39}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 41, y = 59}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 41, y = 59}, speed = 45, frame_blend = 0.3, loop = true},
},
follow = {
"animalia:rat_raw",
"animalia:mutton_raw",
"animalia:beef_raw",
"animalia:porkchop_raw",
"animalia:poultry_raw"
},
drops = {
animalia.bones,
},
-- Behavior Parameters
is_skittish_mob = true,
attack_list = {
"animalia:chicken",
"animalia:rat"
},
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
head_data = {
offset = {x = 0, y = 0.18, z = 0},
pitch_correction = -67,
pivot_h = 0.65,
pivot_v = 0.65
},
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.basic_attack,
animalia.mob_ai.fox_flee,
animalia.mob_ai.basic_seek_food,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed
},
on_eat_drop = function(self)
animalia.protect_from_despawn(self)
end,
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self, 0.5, 0.75)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
end,
death_func = animalia.death_func,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, true, true) then
return
end
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:fox", {
col1 = "d0602d",
col2 = "c9c9c9"
})

257
mods/animalia/mobs/frog.lua Normal file
View file

@ -0,0 +1,257 @@
----------
-- Frog --
----------
local random = math.random
local function poison_effect(object)
object:punch(object, 1.0, {
full_punch_interval = 1.0,
damage_groups = {fleshy = 1},
})
end
local hitboxes = {
{-0.25, 0, -0.25, 0.2, 0.4, 0.25},
{-0.4, 0, -0.4, 0.4, 0.5, 0.4},
{-0.15, 0, -0.15, 0.15, 0.3, 0.15}
}
local animations = {
{
stand = {range = {x = 1, y = 40}, speed = 10, frame_blend = 0.3, loop = true},
float = {range = {x = 90, y = 90}, speed = 1, frame_blend = 0.3, loop = true},
swim = {range = {x = 90, y = 110}, speed = 50, frame_blend = 0.3, loop = true},
walk = {range = {x = 50, y = 80}, speed = 50, frame_blend = 0.3, loop = true},
run = {range = {x = 50, y = 80}, speed = 60, frame_blend = 0.3, loop = true}
},
{
stand = {range = {x = 1, y = 40}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 50, y = 79}, speed = 20, frame_blend = 0.3, loop = true},
run = {range = {x = 50, y = 79}, speed = 30, frame_blend = 0.3, loop = true},
warn = {range = {x = 90, y = 129}, speed = 30, frame_blend = 0.3, loop = true},
punch = {range = {x = 140, y = 160}, speed = 30, frame_blend = 0.1, loop = false},
float = {range = {x = 170, y = 209}, speed = 10, frame_blend = 0.3, loop = true},
swim = {range = {x = 220, y = 239}, speed = 20, frame_blend = 0.3, loop = true}
},
{
stand = {range = {x = 1, y = 40}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 50, y = 69}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 50, y = 69}, speed = 40, frame_blend = 0.3, loop = true},
float = {range = {x = 80, y = 119}, speed = 10, frame_blend = 0.3, loop = true},
swim = {range = {x = 130, y = 149}, speed = 20, frame_blend = 0.3, loop = true}
}
}
local utility_stacks = {
{ -- Tree Frog
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_wander,
animalia.mob_ai.frog_seek_bug,
animalia.mob_ai.frog_breed,
animalia.mob_ai.basic_flee,
animalia.mob_ai.frog_flop,
animalia.mob_ai.frog_seek_water
},
{ -- Bull Frog
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_wander,
animalia.mob_ai.basic_seek_food,
animalia.mob_ai.basic_attack,
animalia.mob_ai.frog_breed,
animalia.mob_ai.frog_flop,
animalia.mob_ai.frog_seek_water
},
{ -- Poison Dart Frog
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_wander,
animalia.mob_ai.frog_breed,
animalia.mob_ai.basic_flee,
animalia.mob_ai.frog_flop,
animalia.mob_ai.frog_seek_water
}
}
local head_data = {
{
offset = {x = 0, y = 0.43, z = 0},
pitch_correction = -15,
pivot_h = 0.3,
pivot_v = 0.3
},
{
offset = {x = 0, y = 0.50, z = 0},
pitch_correction = -20,
pivot_h = 0.3,
pivot_v = 0.3
},
{
offset = {x = 0, y = 0.25, z = 0},
pitch_correction = -20,
pivot_h = 0.3,
pivot_v = 0.3
}
}
local follow = {
{
"butterflies:butterfly_white",
"butterflies:butterfly_violet",
"butterflies:butterfly_red"
},
{
"animalia:rat_raw"
},
{}
}
creatura.register_mob("animalia:frog", {
-- Engine Props
visual_size = {x = 10, y = 10},
meshes = {
"animalia_frog.b3d",
"animalia_bull_frog.b3d",
"animalia_dart_frog.b3d"
},
child_mesh = "animalia_tadpole.b3d",
mesh_textures = {
{
"animalia_tree_frog.png"
},
{
"animalia_bull_frog.png"
},
{
"animalia_dart_frog_1.png",
"animalia_dart_frog_2.png",
"animalia_dart_frog_3.png"
}
},
child_textures = {
"animalia_tadpole.png"
},
makes_footstep_sound = true,
-- Creatura Props
max_health = 5,
armor_groups = {fleshy = 100},
damage = 2,
max_breath = 0,
speed = 2,
tracking_range = 8,
max_boids = 0,
despawn_after = 300,
max_fall = 0,
stepheight = 1.1,
sound = {},
hitbox = {
width = 0.15,
height = 0.3
},
animations = {},
follow = {},
drops = {},
fancy_collide = false,
bouyancy_multiplier = 0,
hydrodynamics_multiplier = 0.3,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = false,
head_data = {},
-- Functions
utility_stack = {},
on_grown = function(self)
local mesh_no = self.mesh_no
self.animations = animations[mesh_no]
self.utility_stack = utility_stacks[mesh_no]
self.head_data = head_data[mesh_no]
self.object:set_properties({
collisionbox = hitboxes[mesh_no]
})
end,
activate_func = function(self)
animalia.initialize_api(self)
self.trust = self:recall("trust") or {}
local mesh_no = self.mesh_no
-- Set Species Properties
if self.growth_scale >= 0.8 then
self.animations = animations[mesh_no]
self.utility_stack = utility_stacks[mesh_no]
self.object:set_properties({
collisionbox = hitboxes[mesh_no]
})
else
self.animations = {
swim = {range = {x = 1, y = 19}, speed = 20, frame_blend = 0.1, loop = true}
}
self.utility_stack = utility_stacks[1]
end
self.head_data = head_data[mesh_no]
if mesh_no == 1 then
for i = 1, 15 do
local frame = 120 + i
local anim = {range = {x = frame, y = frame}, speed = 1, frame_blend = 0.3, loop = false}
self.animations["tongue_" .. i] = anim
end
elseif mesh_no == 2 then
self.object:set_armor_groups({fleshy = 50})
self.warn_before_attack = true
end
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self, 0.2, 0.2)
animalia.do_growth(self, 60)
if self:timer(random(5, 15)) then
self:play_sound("random")
end
if not self.mesh_vars_set then
self.follow = follow[self.mesh_no]
end
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_rightclick = function(self, clicker)
if self.mesh_no ~= 2 then return end
if animalia.feed(self, clicker, false, true) then
animalia.add_trust(self, clicker, 1)
return
end
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage)
creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage)
local name = puncher:is_player() and puncher:get_player_name()
if name then
self.trust[name] = 0
self:memorize("trust", self.trust)
if self.mesh_no == 3 then
animalia.set_player_effect(name, poison_effect, 3)
end
end
end
})
creatura.register_spawn_item("animalia:frog", {
col1 = "67942e",
col2 = "294811"
})

View file

@ -0,0 +1,426 @@
-----------
-- Horse --
-----------
local random = math.random
-- Horse Inventory
local form_obj = {}
local function create_horse_inventory(self)
if not self.owner then return end
local inv_name = "animalia:horse_" .. self.owner
local inv = minetest.create_detached_inventory(inv_name, {
allow_move = function(_, _, _, _, _, count)
return count
end,
allow_put = function(_, _, _, stack)
return stack:get_count()
end,
allow_take = function(_, _, _, stack)
return stack:get_count()
end
})
inv:set_size("main", 12)
inv:set_width("main", 4)
return inv
end
local function serialize_horse_inventory(self)
if not self.owner then return end
local inv_name = "animalia:horse_" .. self.owner
local inv = minetest.get_inventory({type = "detached", name = inv_name})
if not inv then return end
local list = inv:get_list("main")
local stored = {}
for k, item in ipairs(list) do
local itemstr = item:to_string()
if itemstr ~= "" then
stored[k] = itemstr
end
end
self._inventory = self:memorize("_inventory", minetest.serialize(stored))
end
local function get_form(self, player_name)
local inv = create_horse_inventory(self)
if inv
and self._inventory then
inv:set_list("main", minetest.deserialize(self._inventory))
end
local frame_range = self.animations["stand"].range
local frame_loop = frame_range.x .. "," .. frame_range.y
local texture = self:get_props().textures[1]
local form = {
"formspec_version[3]",
"size[10.5,10]",
"image[0,0;10.5,5.25;animalia_form_horse_bg.png]",
"model[0,0.5;5,3.5;mob_mesh;animalia_horse.b3d;" .. texture .. ";-10,-130;false;false;" .. frame_loop .. ";15]",
"list[detached:animalia:horse_" .. player_name .. ";main;5.4,0.5;4,3;]",
"list[current_player;main;0.4,4.9;8,4;]",
"listring[current_player;main]"
}
return table.concat(form, "")
end
local function close_form(player)
local name = player:get_player_name()
if form_obj[name] then
form_obj[name] = nil
minetest.remove_detached_inventory("animalia:horse_" .. name)
end
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name()
if not form_obj[name] or not form_obj[name]:get_yaw() then
return
end
local obj = form_obj[name]
if formname == "animalia:horse_forms" then
local ent = obj and obj:get_luaentity()
if not ent then return end
if fields.quit or fields.key_enter then
form_obj[name] = nil
serialize_horse_inventory(ent)
minetest.remove_detached_inventory("animlaia:horse_" .. name)
end
end
if formname == "animalia:horse_inv" then
local ent = obj and obj:get_luaentity()
if not ent then return end
if fields.quit or fields.key_enter then
form_obj[name] = nil
serialize_horse_inventory(ent)
minetest.remove_detached_inventory("animalia:horse_" .. name)
end
end
end)
minetest.register_on_leaveplayer(close_form)
-- Pattern
local patterns = {
"animalia_horse_pattern_1.png",
"animalia_horse_pattern_2.png",
"animalia_horse_pattern_3.png"
}
local avlbl_colors = {
[1] = {
"animalia_horse_2.png",
"animalia_horse_3.png",
"animalia_horse_5.png"
},
[2] = {
"animalia_horse_1.png",
"animalia_horse_5.png"
},
[3] = {
"animalia_horse_2.png",
"animalia_horse_1.png"
},
[4] = {
"animalia_horse_2.png",
"animalia_horse_1.png"
},
[5] = {
"animalia_horse_2.png",
"animalia_horse_1.png"
}
}
local function set_pattern(self)
local pattern_no = self:recall("pattern_no")
if pattern_no and pattern_no < 1 then return end
if not pattern_no then
if random(3) < 2 then
pattern_no = self:memorize("pattern_no", random(#patterns))
else
self:memorize("pattern_no", 0)
return
end
end
local colors = avlbl_colors[self.texture_no]
local color_no = self:recall("color_no") or self:memorize("color_no", random(#colors))
if not colors[color_no] then return end
local pattern = "(" .. patterns[pattern_no] .. "^[mask:" .. colors[color_no] .. ")"
local texture = self.textures[self.texture_no]
self.object:set_properties({
textures = {texture .. "^" .. pattern}
})
end
-- Definition
creatura.register_mob("animalia:horse", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_horse.b3d",
textures = {
"animalia_horse_1.png",
"animalia_horse_2.png",
"animalia_horse_3.png",
"animalia_horse_4.png",
"animalia_horse_5.png"
},
makes_footstep_sound = true,
-- Creatura Props
max_health = 40,
armor_groups = {fleshy = 100},
damage = 8,
speed = 10,
tracking_range = 16,
max_boids = 7,
despawn_after = 1000,
max_fall = 4,
stepheight = 1.2,
sounds = {
alter_child_pitch = true,
random = {
name = "animalia_horse_idle",
gain = 1.0,
distance = 8
},
hurt = {
name = "animalia_horse_hurt",
gain = 1.0,
distance = 8
},
death = {
name = "animalia_horse_death",
gain = 1.0,
distance = 8
}
},
hitbox = {
width = 0.65,
height = 1.95
},
animations = {
stand = {range = {x = 1, y = 59}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 70, y = 89}, speed = 20, frame_blend = 0.3, loop = true},
run = {range = {x = 101, y = 119}, speed = 40, frame_blend = 0.3, loop = true},
punch_aoe = {range = {x = 170, y = 205}, speed = 30, frame_blend = 0.2, loop = false},
rear = {range = {x = 130, y = 160}, speed = 20, frame_blend = 0.1, loop = false},
eat = {range = {x = 210, y = 240}, speed = 30, frame_blend = 0.3, loop = false}
},
follow = animalia.food_wheat,
drops = {
{name = "animalia:leather", min = 1, max = 4, chance = 2},
animalia.bones,
},
fancy_collide = false,
-- Behavior Parameters
is_grazing_mob = true,
is_herding_mob = true,
-- Animalia Props
catch_with_net = true,
catch_with_lasso = true,
consumable_nodes = {
["default:dirt_with_grass"] = "default:dirt",
["default:dry_dirt_with_dry_grass"] = "default:dry_dirt"
},
head_data = {
bone = "Neck.CTRL",
offset = {x = 0, y = 1.4, z = 0.0},
pitch_correction = 15,
pivot_h = 1,
pivot_v = 1.75
},
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed,
animalia.mob_ai.basic_flee,
{
utility = "animalia:horse_tame",
get_score = function(self)
local rider = not self.owner and self.rider
if rider
and rider:get_pos() then
return 0.7, {self}
end
return 0
end
},
{
utility = "animalia:horse_ride",
get_score = function(self)
if not self.owner then return 0 end
local owner = self.owner and minetest.get_player_by_name(self.owner)
local rider = owner == self.rider and self.rider
if rider
and rider:get_pos() then
return 0.8, {self, rider}
end
return 0
end
}
},
-- Functions
set_saddle = function(self, saddle)
if saddle then
self.saddled = self:memorize("saddled", true)
local texture = self.object:get_properties().textures[1]
self.object:set_properties({
textures = {texture .. "^animalia_horse_saddle.png"}
})
self.drops = {
{name = "animalia:leather", chance = 2, min = 1, max = 4},
{name = "animalia:saddle", chance = 1, min = 1, max = 1}
}
else
local pos = self.object:get_pos()
if not pos then return end
self.saddled = self:memorize("saddled", false)
set_pattern(self)
self.drops = {
{name = "animalia:leather", chance = 2, min = 1, max = 4}
}
minetest.add_item(pos, "animalia:saddle")
end
end,
add_child = function(self, mate)
local pos = self.object:get_pos()
if not pos then return end
local obj = minetest.add_entity(pos, self.name)
local ent = obj and obj:get_luaentity()
if not ent then return end
ent.growth_scale = 0.7
local tex_no = self.texture_no
local mate_ent = mate and mate:get_luaentity()
if mate_ent
or not mate_ent.speed
or not mate_ent.jump_power
or not mate_ent.max_health then
return
end
if random(2) < 2 then
tex_no = mate_ent.texture_no
end
ent:memorize("texture_no", tex_no)
ent:memorize("speed", random(mate_ent.speed, self.speed))
ent:memorize("jump_power", random(mate_ent.jump_power, self.jump_power))
ent:memorize("max_health", random(mate_ent.max_health, self.max_health))
ent.speed = ent:recall("speed")
ent.jump_power = ent:recall("jump_power")
ent.max_health = ent:recall("max_health")
animalia.initialize_api(ent)
animalia.protect_from_despawn(ent)
end,
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
set_pattern(self)
self.owner = self:recall("owner") or nil
if self.owner then
self._inventory = self:recall("_inventory")
end
self.rider = nil
self.saddled = self:recall("saddled") or false
self.max_health = self:recall("max_health") or random(30, 45)
self.speed = self:recall("speed") or random(5, 10)
self.jump_power = self:recall("jump_power") or random(2, 5)
self:memorize("max_health", self.max_health)
self:memorize("speed", self.speed)
self:memorize("jump_power", self.jump_power)
if self.saddled then
self:set_saddle(true)
end
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
end,
death_func = function(self)
if self.rider then
animalia.mount(self, self.rider)
end
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, true) then
return
end
local owner = self.owner
local name = clicker and clicker:get_player_name()
if owner and name ~= owner then return end
if animalia.set_nametag(self, clicker) then
return
end
local wielded_name = clicker:get_wielded_item():get_name()
if wielded_name == "animalia:saddle" then
self:set_saddle(true)
return
end
if clicker:get_player_control().sneak
and owner then
minetest.show_formspec(name, "animalia:horse_forms", get_form(self, name))
form_obj[name] = self.object
elseif wielded_name == "" then
animalia.mount(self, clicker, {rot = {x = -65, y = 180, z = 0}, pos = {x = 0, y = 0.75, z = 0.6}})
if self.saddled then
self:initiate_utility("animalia:mount", self, clicker)
end
end
end,
on_punch = function(self, puncher, ...)
if self.rider and puncher == self.rider then return end
local name = puncher:is_player() and puncher:get_player_name()
if name
and name == self.owner
and puncher:get_player_control().sneak then
self:set_saddle(false)
return
end
animalia.punch(self, puncher, ...)
end,
on_detach_child = function(self, child)
if child
and self.rider == child then
self.rider = nil
child:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
child:set_properties({visual_size = {x = 1, y = 1}})
animalia.animate_player(child, "stand", 30)
end
end
})
creatura.register_spawn_item("animalia:horse", {
col1 = "ebdfd8",
col2 = "653818"
})

View file

@ -0,0 +1,105 @@
-------------
-- Opossum --
-------------
creatura.register_mob("animalia:opossum", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_opossum.b3d",
textures = {
"animalia_opossum.png"
},
makes_footstep_sound = false,
-- Creatura Props
max_health = 5,
armor_groups = {fleshy = 100},
damage = 2,
speed = 4,
tracking_range = 16,
max_boids = 0,
despawn_after = 500,
stepheight = 1.1,
max_fall = 8,
sound = {},
hitbox = {
width = 0.25,
height = 0.4
},
animations = {
stand = {range = {x = 1, y = 59}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 70, y = 89}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 100, y = 119}, speed = 45, frame_blend = 0.3, loop = true},
feint = {range = {x = 130, y = 130}, speed = 45, frame_blend = 0.3, loop = false},
clean_crop = {range = {x = 171, y = 200}, speed = 15, frame_blend = 0.2, loop = false}
},
follow = {
"animalia:song_bird_egg",
"animalia:rat_raw",
"animalia:mutton_raw",
"animalia:beef_raw",
"animalia:porkchop_raw",
"animalia:poultry_raw"
},
-- Behavior Parameters
is_skittish_mob = true,
attack_list = {"animalia:rat"},
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
head_data = {
offset = {x = 0, y = 0.18, z = 0},
pitch_correction = -67,
pivot_h = 0.65,
pivot_v = 0.65
},
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.basic_attack,
animalia.mob_ai.opossum_feint,
animalia.mob_ai.opossum_seek_crop,
animalia.mob_ai.basic_seek_food,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed
},
on_eat_drop = function(self)
animalia.protect_from_despawn(self)
end,
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self, 0.5, 0.75)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
end,
death_func = animalia.death_func,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, true, true) then
return
end
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:opossum", {
col1 = "75665f",
col2 = "ccbfb8"
})

169
mods/animalia/mobs/owl.lua Normal file
View file

@ -0,0 +1,169 @@
---------
-- Owl --
---------
local abs = math.abs
local vec_dist = vector.distance
local function get_home_pos(self)
local pos = self.object:get_pos()
if not pos then return end
local leaves = minetest.find_nodes_in_area_under_air(
vector.subtract(pos, 16),
vector.add(pos, 16),
"group:leaves"
)
local home_dist
local new_home
for _, leaf_pos in ipairs(leaves or {}) do
local dist = vec_dist(pos, leaf_pos)
if not home_dist
or dist < home_dist then
home_dist = dist
new_home = leaf_pos
end
end
if new_home then
new_home.y = new_home.y + 1
self.home_position = self:memorize("home_position", new_home)
end
end
creatura.register_mob("animalia:owl", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_owl.b3d",
textures = {
"animalia_owl.png"
},
makes_footstep_sound = false,
-- Creatura Props
max_health = 10,
damage = 2,
speed = 4,
tracking_range = 16,
max_boids = 0,
despawn_after = 500,
max_fall = 0,
sound = {}, -- TODO
hitbox = {
width = 0.15,
height = 0.3
},
animations = {
stand = {range = {x = 1, y = 60}, speed = 20, frame_blend = 0.3, loop = true},
fly = {range = {x = 71, y = 89}, speed = 30, frame_blend = 0.3, loop = true},
glide = {range = {x = 101, y = 119}, speed = 20, frame_blend = 0.2, loop = true},
fly_punch = {range = {x = 131, y = 149}, speed = 20, frame_blend = 0.1, loop = false},
eat = {range = {x = 161, y = 179}, speed = 20, frame_blend = 0.1, loop = false}
},
follow = {"animalia:rat_raw"},
drops = {
{name = "animalia:feather", min = 1, max = 2, chance = 1}
},
-- Animalia Props
flee_puncher = true, -- TODO
catch_with_net = true,
catch_with_lasso = false,
roost_action = animalia.action_roost,
-- Functions
on_eat_drop = function(self)
animalia.protect_from_despawn(self)
get_home_pos(self)
end,
is_home = function(pos, home_pos)
if abs(pos.x - home_pos.x) < 0.5
and abs(pos.z - home_pos.z) < 0.5 then
if abs(pos.y - home_pos.y) < 0.75 then
return true
else
local under = {x = home_pos.x, y = home_pos.y, z = home_pos.z}
local name = minetest.get_node(under).name
if minetest.get_node_group(name, "leaves") > 0 then
return true
end
end
end
return false
end,
wander_action = creatura.action_move,
utility_stack = {
animalia.mob_ai.fly_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.bat_seek_home,
animalia.mob_ai.fly_seek_food,
animalia.mob_ai.eagle_attack
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
self._tp2home = self:recall("_tp2home") or nil
self.home_position = self:recall("home_position") or nil
local home_pos = self.home_position
if self._tp2home
and home_pos then
self.object:set_pos(home_pos)
end
self.is_landed = self:recall("is_landed") or false
if not home_pos
or creatura.get_node_def(home_pos).walkable then
get_home_pos(self)
end
end,
step_func = function(self)
animalia.step_timers(self)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.rotate_to_pitch(self)
if not self.is_landed
or not self.touching_ground then
self.speed = 5
else
self.speed = 1
end
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
deactivate_func = function(self)
if self:get_utility()
and self:get_utility() == "animalia:fly_to_roost" then
local pos = self.home_position
local node = minetest.get_node_or_nil(pos)
if node
and not creatura.get_node_def(node.name).walkable
and minetest.get_natural_light(pos) > 0 then
self:memorize("_tp2home", true)
end
end
end,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, false) then
return
end
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:owl", {
col1 = "412918",
col2 = "735b46"
})

120
mods/animalia/mobs/pig.lua Normal file
View file

@ -0,0 +1,120 @@
---------
-- Pig --
---------
creatura.register_mob("animalia:pig", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_pig.b3d",
female_textures = {
"animalia_pig_1.png",
"animalia_pig_2.png",
"animalia_pig_3.png"
},
male_textures = {
"animalia_pig_1.png^animalia_pig_tusks.png",
"animalia_pig_2.png^animalia_pig_tusks.png",
"animalia_pig_3.png^animalia_pig_tusks.png"
},
child_textures = {
"animalia_pig_1.png",
"animalia_pig_2.png",
"animalia_pig_3.png"
},
makes_footstep_sound = true,
-- Creatura Props
max_health = 20,
damage = 2,
speed = 3,
tracking_range = 12,
despawn_after = 500,
stepheight = 1.1,
sounds = {
random = {
name = "animalia_pig",
gain = 1.0,
distance = 8
},
hurt = {
name = "animalia_pig_hurt",
gain = 1.0,
distance = 8
},
death = {
name = "animalia_pig_death",
gain = 1.0,
distance = 8
}
},
hitbox = {
width = 0.35,
height = 0.7
},
animations = {
stand = {range = {x = 1, y = 60}, speed = 20, frame_blend = 0.3, loop = true},
walk = {range = {x = 70, y = 89}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 100, y = 119}, speed = 40, frame_blend = 0.3, loop = true},
},
follow = animalia.food_crops,
drops = {
{name = "animalia:porkchop_raw", min = 1, max = 3, chance = 1},
animalia.bones,
},
-- Behavior Parameters
is_herding_mob = true,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
birth_count = 2,
head_data = {
offset = {x = 0, y = 0.7, z = 0},
pitch_correction = 0,
pivot_h = 0.5,
pivot_v = 0.3
},
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.basic_seek_crop,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed,
animalia.mob_ai.basic_flee
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.do_growth(self, 60)
animalia.head_tracking(self)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
end,
death_func = animalia.death_func,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, true) then
return
end
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:pig", {
col1 = "e0b1a7",
col2 = "cc9485"
})

View file

@ -0,0 +1,82 @@
----------
-- Mice --
----------
creatura.register_mob("animalia:rat", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_rat.b3d",
textures = {
"animalia_rat_1.png",
"animalia_rat_2.png",
"animalia_rat_3.png"
},
-- Creatura Props
max_health = 5,
damage = 0,
speed = 1,
tracking_range = 8,
despawn_after = 200,
stepheight = 1.1,
--sound = {},
hitbox = {
width = 0.15,
height = 0.3
},
animations = {
stand = {range = {x = 1, y = 39}, speed = 20, frame_blend = 0.3, loop = true},
walk = {range = {x = 51, y = 69}, speed = 20, frame_blend = 0.3, loop = true},
run = {range = {x = 81, y = 99}, speed = 45, frame_blend = 0.3, loop = true},
eat = {range = {x = 111, y = 119}, speed = 20, frame_blend = 0.1, loop = false}
},
drops = {
{name = "animalia:rat_raw", min = 1, max = 1, chance = 1}
},
-- Behavior Parameters
is_skittish_mob = true,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = false,
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.basic_seek_crop,
animalia.mob_ai.rat_seek_chest,
animalia.mob_ai.basic_flee
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.do_growth(self, 60)
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_rightclick = function(self, clicker)
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:rat", {
col1 = "605a55",
col2 = "ff936f"
})

View file

@ -0,0 +1,119 @@
--------------
-- Reindeer --
--------------
creatura.register_mob("animalia:reindeer", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_reindeer.b3d",
textures = {"animalia_reindeer.png"},
child_textures = {"animalia_reindeer_calf.png"},
makes_footstep_sound = true,
-- Creatura Props
max_health = 15,
damage = 0,
speed = 3,
tracking_range = 12,
max_boids = 4,
despawn_after = 500,
stepheight = 1.1,
sounds = {
random = {
name = "animalia_reindeer",
gain = 0.5,
distance = 8
},
hurt = {
name = "animalia_reindeer_hurt",
gain = 0.5,
distance = 8
},
death = {
name = "animalia_reindeer_death",
gain = 0.5,
distance = 8
}
},
hitbox = {
width = 0.45,
height = 0.9
},
animations = {
stand = {range = {x = 1, y = 59}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 70, y = 89}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 100, y = 119}, speed = 40, frame_blend = 0.3, loop = true},
eat = {range = {x = 130, y = 150}, speed = 20, frame_blend = 0.3, loop = false}
},
follow = animalia.food_wheat,
drops = {
{name = "animalia:venison_raw", min = 1, max = 3, chance = 1},
{name = "animalia:leather", min = 1, max = 3, chance = 2},
animalia.bones,
},
-- Behavior Parameters
is_grazing_mob = true,
is_herding_mob = true,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
consumable_nodes = {
{
name = "default:dirt_with_grass",
replacement = "default:dirt"
},
{
name = "default:dry_dirt_with_dry_grass",
replacement = "default:dry_dirt"
}
},
head_data = {
offset = {x = 0, y = 0.55, z = 0},
pitch_correction = -45,
pivot_h = 1,
pivot_v = 1
},
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed,
animalia.mob_ai.basic_flee
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
end,
death_func = animalia.death_func,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, true) then
return
end
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:reindeer", {
col1 = "413022",
col2 = "d5c0a3"
})

View file

@ -0,0 +1,199 @@
-----------
-- Sheep --
-----------
local random = math.random
local palette = {
black = {"Black", "#000000b0"},
blue = {"Blue", "#015dbb70"},
brown = {"Brown", "#663300a0"},
cyan = {"Cyan", "#01ffd870"},
dark_green = {"Dark Green", "#005b0770"},
dark_grey = {"Dark Grey", "#303030b0"},
green = {"Green", "#61ff0170"},
grey = {"Grey", "#5b5b5bb0"},
magenta = {"Magenta", "#ff05bb70"},
orange = {"Orange", "#ff840170"},
pink = {"Pink", "#ff65b570"},
red = {"Red", "#ff0000a0"},
violet = {"Violet", "#2000c970"},
white = {"White", "#ababab00"},
yellow = {"Yellow", "#e3ff0070"},
}
creatura.register_mob("animalia:sheep", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_sheep.b3d",
textures = {
"animalia_sheep.png^animalia_sheep_wool.png"
},
child_textures = {
"animalia_sheep.png"
},
makes_footstep_sound = true,
-- Creatura Props
max_health = 15,
armor_groups = {fleshy = 100},
damage = 0,
speed = 3,
tracking_range = 12,
max_boids = 4,
despawn_after = 500,
stepheight = 1.1,
sounds = {
random = {
name = "animalia_sheep",
gain = 1.0,
distance = 8
},
hurt = {
name = "animalia_sheep_hurt",
gain = 1.0,
distance = 8
},
death = {
name = "animalia_sheep_death",
gain = 1.0,
distance = 8
}
},
hitbox = {
width = 0.4,
height = 0.8
},
animations = {
stand = {range = {x = 1, y = 59}, speed = 10, frame_blend = 0.3, loop = true},
walk = {range = {x = 70, y = 89}, speed = 20, frame_blend = 0.3, loop = true},
run = {range = {x = 100, y = 119}, speed = 30, frame_blend = 0.3, loop = true},
eat = {range = {x = 130, y = 150}, speed = 20, frame_blend = 0.3, loop = false}
},
follow = animalia.food_wheat,
drops = {
{name = "animalia:mutton_raw", min = 1, max = 3, chance = 1},
minetest.get_modpath("wool") and {name = "wool:white", min = 1, max = 3, chance = 2} or nil,
animalia.bones,
},
-- Behavior Parameters
is_grazing_mob = true,
is_herding_mob = true,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
consumable_nodes = {
["default:dirt_with_grass"] = "default:dirt",
["default:dry_dirt_with_dry_grass"] = "default:dry_dirt"
},
head_data = {
offset = {x = 0, y = 0.41, z = 0},
pitch_correction = -45,
pivot_h = 0.75,
pivot_v = 0.85
},
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed,
animalia.mob_ai.basic_flee
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
self.collected = self:recall("collected") or false
self.dye_color = self:recall("dye_color") or "white"
if self.collected then
self.object:set_properties({
textures = {"animalia_sheep.png"},
})
elseif self.dye_color ~= "white" then
self.object:set_properties({
textures = {"animalia_sheep.png^(animalia_sheep_wool.png^[multiply:" .. palette[self.dye_color][2] .. ")"},
})
end
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
end,
death_func = animalia.death_func,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, true) then
return
end
if animalia.set_nametag(self, clicker) then
return
end
if self.collected
or self.growth_scale < 1 then
return
end
local tool = clicker:get_wielded_item()
local tool_name = tool:get_name()
local creative = minetest.is_creative_enabled(clicker:get_player_name())
if tool_name == "animalia:shears" then
if not minetest.get_modpath("wool") then
return
end
minetest.add_item(
self.object:get_pos(),
ItemStack("wool:" .. self.dye_color .. " " .. random(1, 3))
)
self.collected = self:memorize("collected", true)
self.dye_color = self:memorize("dye_color", "white")
self.object:set_properties({
textures = {"animalia_sheep.png"},
})
if not creative then
tool:add_wear(650)
clicker:set_wielded_item(tool)
end
end
if tool_name:match("^dye:") then
local dye_color = tool_name:split(":")[2]
if palette[dye_color] then
self.dye_color = self:memorize("dye_color", dye_color)
self.drops = {
{name = "animalia:mutton_raw", chance = 1, min = 1, max = 4},
{name = "wool:" .. dye_color, chance = 2, min = 1, max = 2},
}
self.object:set_properties({
textures = {"animalia_sheep.png^(animalia_sheep_wool.png^[multiply:" .. palette[dye_color][2] .. ")"},
})
if not creative then
tool:take_item()
clicker:set_wielded_item(tool)
end
end
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:sheep", {
col1 = "f4e6cf",
col2 = "e1ca9b"
})

View file

@ -0,0 +1,138 @@
---------------
-- Song Bird --
---------------
local random = math.random
creatura.register_mob("animalia:song_bird", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_bird.b3d",
textures = {
"animalia_cardinal.png",
"animalia_bluebird.png",
"animalia_goldfinch.png"
},
-- Creatura Props
max_health = 2,
speed = 4,
tracking_range = 8,
max_boids = 6,
boid_seperation = 0.3,
despawn_after = 200,
max_fall = 0,
stepheight = 1.1,
sounds = {
cardinal = {
name = "animalia_cardinal",
gain = 0.5,
distance = 63
},
eastern_blue = {
name = "animalia_bluebird",
gain = 0.5,
distance = 63
},
goldfinch = {
name = "animalia_goldfinch",
gain = 0.5,
distance = 63
},
},
hitbox = {
width = 0.2,
height = 0.4
},
animations = {
stand = {range = {x = 1, y = 100}, speed = 30, frame_blend = 0.3, loop = true},
walk = {range = {x = 110, y = 130}, speed = 40, frame_blend = 0.3, loop = true},
fly = {range = {x = 140, y = 160}, speed = 40, frame_blend = 0.3, loop = true}
},
--follow = {},
drops = {
{name = "animalia:feather", min = 1, max = 1, chance = 2}
},
-- Behavior Parameters
uses_boids = true,
-- Animalia Props
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = false,
wander_action = animalia.action_boid_move,
--roost_action = animalia.action_roost,
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.fly_landing_wander,
animalia.mob_ai.fly_seek_land
},
activate_func = function(self)
if animalia.despawn_inactive_mob(self) then return end
animalia.initialize_api(self)
animalia.initialize_lasso(self)
self.is_landed = (random(2) < 2 and true) or false
end,
step_func = function(self)
animalia.step_timers(self)
animalia.do_growth(self, 60)
--animalia.update_lasso_effects(self)
animalia.rotate_to_pitch(self)
if self:timer(random(6, 12)) then
if animalia.is_day then
if self.texture_no == 1 then
self:play_sound("cardinal")
elseif self.texture_no == 2 then
self:play_sound("eastern_blue")
else
self:play_sound("goldfinch")
end
end
end
if not self.is_landed
or not self.touching_ground then
self.speed = 4
else
self.speed = 3
end
end,
death_func = animalia.death_func,
on_rightclick = function(self, clicker)
--[[if animalia.feed(self, clicker, false, false) then
return
end]]
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:song_bird", {
col1 = "ae2f2f",
col2 = "f3ac1c"
})
minetest.register_entity("animalia:bird", {
static_save = false,
on_activate = function(self)
self.object:remove()
end
})
minetest.register_abm({
label = "animalia:nest_cleanup",
nodenames = "animalia:nest_song_bird",
interval = 900,
action = function(pos)
minetest.remove_node(pos)
end
})

View file

@ -0,0 +1,91 @@
----------
-- Fish --
----------
creatura.register_mob("animalia:tropical_fish", {
-- Engine Props
visual_size = {x = 10, y = 10},
meshes = {
"animalia_clownfish.b3d",
"animalia_angelfish.b3d"
},
mesh_textures = {
{
"animalia_clownfish.png",
"animalia_blue_tang.png"
},
{
"animalia_angelfish.png"
}
},
-- Creatura Props
max_health = 5,
armor_groups = {fleshy = 150},
damage = 0,
max_breath = 0,
speed = 2,
tracking_range = 6,
max_boids = 6,
boid_seperation = 0.3,
despawn_after = 200,
max_fall = 0,
stepheight = 1.1,
hitbox = {
width = 0.15,
height = 0.3
},
animations = {
swim = {range = {x = 1, y = 20}, speed = 20, frame_blend = 0.3, loop = true},
flop = {range = {x = 30, y = 40}, speed = 20, frame_blend = 0.3, loop = true},
},
liquid_submergence = 1,
liquid_drag = 0,
-- Animalia Behaviors
is_aquatic_mob = true,
-- Animalia Props
flee_puncher = false,
catch_with_net = true,
catch_with_lasso = false,
-- Functions
utility_stack = {
animalia.mob_ai.swim_wander
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_rightclick = function(self, clicker)
if animalia.set_nametag(self, clicker) then
return
end
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:tropical_fish", {
col1 = "e28821",
col2 = "f6e5d2"
})
animalia.alias_mob("animalia:clownfish", "animalia:tropical_fish")
animalia.alias_mob("animalia:blue_tang", "animalia:tropical_fish")
animalia.alias_mob("animalia:angelfish", "animalia:tropical_fish")

View file

@ -0,0 +1,133 @@
------------
-- Turkey --
------------
creatura.register_mob("animalia:turkey", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_turkey.b3d",
female_textures = {"animalia_turkey_hen.png"},
male_textures = {"animalia_turkey_tom.png"},
child_textures = {"animalia_turkey_chick.png"},
makes_footstep_sound = true,
-- Creatura Props
max_health = 8,
armor_groups = {fleshy = 100},
damage = 0,
speed = 2,
tracking_range = 8,
max_boids = 3,
despawn_after = 500,
max_fall = 0,
stepheight = 1.1,
sounds = {
random = {
name = "animalia_turkey",
gain = 0.5,
distance = 8
},
hurt = {
name = "animalia_turkey_hurt",
gain = 0.5,
distance = 8
},
death = {
name = "animalia_turkey_death",
gain = 0.5,
distance = 8
}
},
hitbox = {
width = 0.3,
height = 0.6
},
animations = {
stand = {range = {x = 0, y = 0}, speed = 1, frame_blend = 0.3, loop = true},
walk = {range = {x = 10, y = 30}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 40, y = 60}, speed = 45, frame_blend = 0.3, loop = true},
fall = {range = {x = 70, y = 90}, speed = 30, frame_blend = 0.3, loop = true},
},
follow = animalia.food_seeds,
drops = {
{name = "animalia:poultry_raw", min = 1, max = 4, chance = 1},
{name = "animalia:feather", min = 1, max = 3, chance = 2},
animalia.bones,
},
-- Animalia Props
group_wander = true,
flee_puncher = true,
catch_with_net = true,
catch_with_lasso = true,
head_data = {
offset = {x = 0, y = 0.15, z = 0},
pitch_correction = 45,
pivot_h = 0.45,
pivot_v = 0.65
},
move_chance = 2,
idle_time = 1,
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_breed,
animalia.mob_ai.basic_flee
},
add_child = function(self)
local pos = self.object:get_pos()
if not pos then return end
animalia.particle_spawner(pos, "animalia_egg_fragment.png", "splash", pos, pos)
local object = minetest.add_entity(pos, self.name)
local ent = object:get_luaentity()
ent.growth_scale = 0.7
animalia.initialize_api(ent)
animalia.protect_from_despawn(ent)
end,
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self, 0.75, 0.75)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
animalia.random_sound(self)
if self.fall_start then
self:set_gravity(-4.9)
self:animate("fall")
end
if (self.growth_scale or 1) > 0.8
and self.gender == "female"
and self:timer(60) then
animalia.random_drop_item(self, "animalia:turkey_egg", 10)
end
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
on_rightclick = function(self, clicker)
if animalia.feed(self, clicker, false, true) then
return
end
animalia.set_nametag(self, clicker)
end,
on_punch = animalia.punch
})
creatura.register_spawn_item("animalia:turkey", {
col1 = "352b22",
col2 = "2f2721"
})

198
mods/animalia/mobs/wolf.lua Normal file
View file

@ -0,0 +1,198 @@
----------
-- Wolf --
----------
local follow = {
"animalia:mutton_raw",
"animalia:beef_raw",
"animalia:porkchop_raw",
"animalia:poultry_raw"
}
if minetest.registered_items["bonemeal:bone"] then
follow = {
"bonemeal:bone",
"animalia:beef_raw",
"animalia:porkchop_raw",
"animalia:mutton_raw",
"animalia:poultry_raw"
}
end
local function is_value_in_table(tbl, val)
for _, v in pairs(tbl) do
if v == val then
return true
end
end
return false
end
creatura.register_mob("animalia:wolf", {
-- Engine Props
visual_size = {x = 10, y = 10},
mesh = "animalia_wolf.b3d",
textures = {
"animalia_wolf_1.png",
"animalia_wolf_2.png",
"animalia_wolf_3.png",
"animalia_wolf_4.png"
},
makes_footstep_sound = true,
-- Creatura Props
max_health = 20,
damage = 4,
speed = 4,
tracking_range = 24,
despawn_after = 500,
stepheight = 1.1,
sound = {},
hitbox = {
width = 0.35,
height = 0.7
},
animations = {
stand = {range = {x = 1, y = 60}, speed = 20, frame_blend = 0.3, loop = true},
walk = {range = {x = 70, y = 89}, speed = 30, frame_blend = 0.3, loop = true},
run = {range = {x = 100, y = 119}, speed = 40, frame_blend = 0.3, loop = true},
sit = {range = {x = 130, y = 139}, speed = 10, frame_blend = 0.3, loop = true},
},
follow = follow,
drops = {
animalia.bones,
},
-- Behavior Parameters
is_skittish_mob = true,
is_herding_mob = true,
-- Animalia Props
assist_owner = true,
flee_puncher = false,
catch_with_net = true,
catch_with_lasso = true,
consumable_nodes = {},
head_data = {
offset = {x = 0, y = 0.22, z = 0},
pitch_correction = -35,
pivot_h = 0.45,
pivot_v = 0.45
},
-- Functions
utility_stack = {
animalia.mob_ai.basic_wander,
animalia.mob_ai.swim_seek_land,
animalia.mob_ai.tamed_stay,
animalia.mob_ai.tamed_follow_owner,
animalia.mob_ai.basic_attack,
animalia.mob_ai.basic_breed
},
activate_func = function(self)
animalia.initialize_api(self)
animalia.initialize_lasso(self)
self.order = self:recall("order") or "wander"
self.owner = self:recall("owner") or nil
self.enemies = self:recall("enemies") or {}
if self.owner
and minetest.get_player_by_name(self.owner) then
if not is_value_in_table(animalia.pets[self.owner], self.object) then
table.insert(animalia.pets[self.owner], self.object)
end
end
end,
step_func = function(self)
animalia.step_timers(self)
animalia.head_tracking(self)
animalia.do_growth(self, 60)
animalia.update_lasso_effects(self)
end,
death_func = function(self)
if self:get_utility() ~= "animalia:die" then
self:initiate_utility("animalia:die", self)
end
end,
deactivate_func = function(self)
if self.owner then
for i, object in ipairs(animalia.pets[self.owner] or {}) do
if object == self.object then
animalia.pets[self.owner][i] = nil
end
end
end
if self.enemies
and self.enemies[1]
and self.memorize then
self.enemies[1] = nil
self.enemies = self:memorize("enemies", self.enemies)
end
end,
on_rightclick = function(self, clicker)
if not clicker:is_player() then return end
local name = clicker:get_player_name()
local passive = true
if is_value_in_table(self.enemies, name) then passive = false end
if animalia.feed(self, clicker, passive, passive) then
return
end
if animalia.set_nametag(self, clicker) then
return
end
if self.owner
and name == self.owner
and clicker:get_player_control().sneak then
local order = self.order
if order == "wander" then
minetest.chat_send_player(name, "Wolf is following")
self.order = "follow"
self:initiate_utility("animalia:follow_player", self, clicker, true)
self:set_utility_score(0.7)
elseif order == "follow" then
minetest.chat_send_player(name, "Wolf is sitting")
self.order = "sit"
self:initiate_utility("animalia:stay", self)
self:set_utility_score(0.5)
else
minetest.chat_send_player(name, "Wolf is wandering")
self.order = "wander"
self:set_utility_score(0)
end
self:memorize("order", self.order)
end
end,
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, direction, damage)
creatura.basic_punch_func(self, puncher, time_from_last_punch, tool_capabilities, direction, damage)
local name = puncher:is_player() and puncher:get_player_name()
if name then
if self.owner
and name == self.owner then
return
elseif not is_value_in_table(self.enemies, name) then
table.insert(self.enemies, name)
if #self.enemies > 15 then
table.remove(self.enemies, 1)
end
self.enemies = self:memorize("enemies", self.enemies)
else
table.remove(self.enemies, 1)
table.insert(self.enemies, name)
self.enemies = self:memorize("enemies", self.enemies)
end
end
self._target = puncher
end,
})
creatura.register_spawn_item("animalia:wolf", {
col1 = "a19678",
col2 = "231b13"
})

6
mods/animalia/mod.conf Normal file
View file

@ -0,0 +1,6 @@
name = animalia
depends = creatura
optional_depends = default, mcl_player, farming, 3d_armor, bonemeal
description = Adds unique and consistantly designed Animals
title = Animalia
author = ElCeejo

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,49 @@
# Made in Blockbench 4.1.5
mtllib animalia_nest.mtl
o cube
v 0.3125 -0.3125 0.3125
v 0.3125 -0.3125 -0.3125
v 0.3125 -0.5 0.3125
v 0.3125 -0.5 -0.3125
v -0.3125 -0.3125 -0.3125
v -0.3125 -0.3125 0.3125
v -0.3125 -0.5 -0.3125
v -0.3125 -0.5 0.3125
vt 0.16666666666666666 0.5833333333333333
vt 0.5833333333333334 0.5833333333333333
vt 0.5833333333333334 0.45833333333333337
vt 0.16666666666666666 0.45833333333333337
vt 0.16666666666666666 0.5833333333333333
vt 0.5833333333333334 0.5833333333333333
vt 0.5833333333333334 0.45833333333333337
vt 0.16666666666666666 0.45833333333333337
vt 0.16666666666666666 0.5833333333333333
vt 0.5833333333333334 0.5833333333333333
vt 0.5833333333333334 0.45833333333333337
vt 0.16666666666666666 0.45833333333333337
vt 0.16666666666666666 0.5833333333333333
vt 0.5833333333333334 0.5833333333333333
vt 0.5833333333333334 0.45833333333333337
vt 0.16666666666666666 0.45833333333333337
vt 1 0.5833333333333333
vt 0.5833333333333334 0.5833333333333333
vt 0.5833333333333334 1
vt 1 1
vt 1 0.5833333333333333
vt 0.5833333333333334 0.5833333333333333
vt 0.5833333333333334 0.16666666666666663
vt 1 0.16666666666666663
vn 0 0 -1
vn 1 0 0
vn 0 0 1
vn -1 0 0
vn 0 1 0
vn 0 -1 0
usemtl m_1
f 4/4/1 7/3/1 5/2/1 2/1/1
f 3/8/2 4/7/2 2/6/2 1/5/2
f 8/12/3 3/11/3 1/10/3 6/9/3
f 7/16/4 8/15/4 6/14/4 5/13/4
f 6/20/5 1/19/5 2/18/5 5/17/5
f 7/24/6 4/23/6 3/22/6 8/21/6

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,29 @@
# How many chunks can generate before adding another to spawn queue
chunk_spawn_add_int (Chunk Spawning Addition Interval) float 6
# How often (in seconds) the spawn queue is executed and cleared
chunk_spawn_queue_int (Chunk Spawning Queue Execution Interval) float 16
# If true, mobs will spawn in the wild
spawn_mobs (Spawn Mobs) bool true
# If true, items from mobs_redo and mobs_animal will be converted to Animalia items
convert_redo_items(Convert Mobs Redo Items) bool false
# If true, Guano will accumulate under resting bats
guano_accumulation (Guano Accumulation) bool true
# If true, Guano can be used as fertilizer
guano_fertilization (Guano Fertilization) bool true
# Chance for Common Mobs to spawn (1 in x nodes will spawn a mob every minute)
animalia_common_chance (Common Mob Spawn Chance) int 40000 20000 60000
# Chance for Ambient Mobs to spawn (1 in x nodes will spawn a mob every minute)
animalia_ambient_chance (Ambient Mob Spawn Chance) int 15000 5000 25000
# Chance for Pest Mobs to spawn (1 in x nodes will spawn a mob every minute)
animalia_pest_chance (Pest Mob Spawn Chance) int 5000 100 15000
# Chance for Common Mobs to spawn (1 in x nodes will spawn a mob every minute)
animalia_predator_chance (Predator Mob Spawn Chance) int 40000 20000 50000

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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