write something there
This commit is contained in:
commit
b4b6c08f4f
8546 changed files with 309825 additions and 0 deletions
882
mods/animalia/api/api.lua
Normal file
882
mods/animalia/api/api.lua
Normal 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
251
mods/animalia/api/lasso.lua
Normal 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
|
||||
})
|
82
mods/animalia/api/legacy_convert.lua
Normal file
82
mods/animalia/api/legacy_convert.lua
Normal 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
528
mods/animalia/api/libri.lua
Normal 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
2065
mods/animalia/api/mob_ai.lua
Normal file
File diff suppressed because it is too large
Load diff
409
mods/animalia/api/spawning.lua
Normal file
409
mods/animalia/api/spawning.lua
Normal 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
|
||||
})
|
22
mods/animalia/api/storage.lua
Normal file
22
mods/animalia/api/storage.lua
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue