mirror of
https://github.com/ElCeejo/animalia.git
synced 2025-03-15 12:21:24 +00:00
583 lines
19 KiB
Lua
583 lines
19 KiB
Lua
-------------
|
|
---- API ----
|
|
-------------
|
|
-- Ver 0.1 --
|
|
|
|
local random = math.random
|
|
local pi = math.pi
|
|
local abs = math.abs
|
|
local ceil = math.ceil
|
|
|
|
local vec_dist = vector.distance
|
|
|
|
local abr = minetest.get_mapgen_setting('active_block_range')
|
|
|
|
local function hitbox(self) return self.object:get_properties().collisionbox end
|
|
|
|
local walkable_nodes = {}
|
|
|
|
minetest.register_on_mods_loaded(function()
|
|
for name in pairs(minetest.registered_nodes) do
|
|
if name ~= "air" and name ~= "ignore" then
|
|
if minetest.registered_nodes[name].walkable then
|
|
table.insert(walkable_nodes,name)
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
function better_fauna.particle_spawner(pos, texture, type, min_pos, max_pos)
|
|
type = type or "float"
|
|
min_pos = min_pos or {
|
|
x = pos.x - 2,
|
|
y = pos.y - 2,
|
|
z = pos.z - 2,
|
|
}
|
|
max_pos = max_pos or {
|
|
x = pos.x + 2,
|
|
y = pos.y + 2,
|
|
z = pos.z + 2,
|
|
}
|
|
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 = vector.new(-1, 2, -1),
|
|
maxvel = vector.new(1, 5, 1),
|
|
minacc = vector.new(0, -9.81, 0),
|
|
maxacc = vector.new(0, -9.81, 0),
|
|
minsize = 2,
|
|
maxsize = 4,
|
|
collisiondetection = true,
|
|
texture = texture,
|
|
})
|
|
end
|
|
end
|
|
|
|
---------------------------
|
|
-- Mob Control Functions --
|
|
---------------------------
|
|
|
|
local function can_fit(pos, width)
|
|
local pos1 = vector.new(pos.x - width, pos.y, pos.z - width)
|
|
local pos2 = vector.new(pos.x + width, pos.y, pos.z + width)
|
|
for x = pos1.x, pos2.x do
|
|
for y = pos1.y, pos2.y do
|
|
for z = pos1.z, pos2.z do
|
|
local p2 = vector.new(x, y, z)
|
|
local node = minetest.get_node(p2)
|
|
if minetest.registered_nodes[node.name].walkable then
|
|
local p3 = vector.new(p2.x, p2.y + 1, p2.z)
|
|
local node2 = minetest.get_node(p3)
|
|
if minetest.registered_nodes[node2.name].walkable then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function move_from_wall(pos, width)
|
|
local pos1 = vector.new(pos.x - width, pos.y, pos.z - width)
|
|
local pos2 = vector.new(pos.x + width, pos.y, pos.z + width)
|
|
for x = pos1.x, pos2.x do
|
|
for y = pos1.y, pos2.y do
|
|
for z = pos1.z, pos2.z do
|
|
local p2 = vector.new(x, y, z)
|
|
if can_fit(p2, width) then
|
|
return p2
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return pos
|
|
end
|
|
|
|
function better_fauna.find_path(pos, tpos, width)
|
|
|
|
local endpos = tpos
|
|
|
|
if not minetest.registered_nodes[minetest.get_node(
|
|
vector.new(endpos.x, endpos.y - 1, endpos.z))
|
|
.name].walkable then
|
|
local min = vector.subtract(endpos, 1)
|
|
local max = vector.add(endpos, 1)
|
|
|
|
local index_table = minetest.find_nodes_in_area_under_air( min, max, better_fauna.walkable_nodes)
|
|
for _, i_pos in pairs(index_table) do
|
|
if minetest.registered_nodes[minetest.get_node(i_pos)
|
|
.name].walkable then
|
|
endpos = vector.new(i_pos.x, i_pos.y + 1, i_pos.z)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
local path = minetest.find_path(pos, endpos, 32, 1, 1, "A*_noprefetch")
|
|
|
|
if not path
|
|
or #path < 2 then return end
|
|
|
|
table.remove(path, 1)
|
|
table.remove(path, #path)
|
|
|
|
for i = #path, 1, -1 do
|
|
if not path then return end
|
|
if not can_fit(path[i], width + 1) then
|
|
local clear = move_from_wall(path[i], width + 1)
|
|
if clear and can_fit(clear, width) then
|
|
path[i] = clear
|
|
end
|
|
end
|
|
if #path > 3 then
|
|
local pos1 = path[i - 2]
|
|
local pos2 = path[i]
|
|
-- Handle Diagonals
|
|
if pos1
|
|
and pos2
|
|
and pos1.x ~= pos2.x
|
|
and pos1.z ~= pos2.z then
|
|
if minetest.line_of_sight(pos1, pos2) then
|
|
local pos3 = vector.divide(vector.add(pos1, pos2), 2)
|
|
if can_fit(pos, width) then
|
|
table.remove(path, i - 1)
|
|
end
|
|
end
|
|
end
|
|
-- Reduce Straight Lines
|
|
if pos1
|
|
and pos2
|
|
and pos1.x == pos2.x
|
|
and pos1.z ~= pos2.z
|
|
and pos1.y == pos2.y then
|
|
if minetest.line_of_sight(pos1, pos2) then
|
|
local pos3 = vector.divide(vector.add(pos1, pos2), 2)
|
|
if can_fit(pos, width) then
|
|
table.remove(path, i - 1)
|
|
end
|
|
end
|
|
elseif pos1
|
|
and pos2
|
|
and pos1.x ~= pos2.x
|
|
and pos1.z == pos2.z
|
|
and pos1.y == pos2.y then
|
|
if minetest.line_of_sight(pos1, pos2) then
|
|
local pos3 = vector.divide(vector.add(pos1, pos2), 2)
|
|
if can_fit(pos, width) then
|
|
table.remove(path, i - 1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if #path > 2 then
|
|
if vector.distance(pos, path[2]) <= width + 1 then
|
|
for i = 3, #path do
|
|
path[i - 1] = path[i]
|
|
end
|
|
end
|
|
end
|
|
|
|
return path
|
|
end
|
|
|
|
function better_fauna.path_to_next_waypoint(self, tpos, speed_factor)
|
|
speed_factor = speed_factor or 1
|
|
local pos = self.object:get_pos()
|
|
local path_data = better_fauna.find_path(pos, tpos, 1)
|
|
if not path_data
|
|
or #path_data < 2 then
|
|
return true
|
|
end
|
|
local pos2 = path_data[2]
|
|
if pos2 then
|
|
local yaw = self.object:get_yaw()
|
|
local tyaw = minetest.dir_to_yaw(vector.direction(pos, pos2))
|
|
if abs(tyaw - yaw) > 0.1 then
|
|
mobkit.lq_turn2pos(self, pos2)
|
|
end
|
|
mobkit.lq_dumbwalk(self, pos2, speed_factor)
|
|
return true
|
|
end
|
|
end
|
|
|
|
function better_fauna.feed_tame(self, clicker, feed_count, tame, breed)
|
|
local item = clicker:get_wielded_item()
|
|
local pos = self.object:get_pos()
|
|
local mob_name = mob_core.get_name_proper(self.name)
|
|
if mob_core.follow_holding(self, clicker) then
|
|
if creative == false then
|
|
item:take_item()
|
|
clicker:set_wielded_item(item)
|
|
end
|
|
mobkit.heal(self, self.max_hp/feed_count)
|
|
if self.hp >= self.max_hp then
|
|
self.hp = self.max_hp
|
|
end
|
|
self.food = mobkit.remember(self, "food", self.food + 1)
|
|
|
|
local minppos = vector.add(pos, hitbox(self)[4])
|
|
local maxppos = vector.subtract(pos, hitbox(self)[4])
|
|
local def = minetest.registered_items[item:get_name()]
|
|
local texture = def.inventory_image
|
|
if not texture or texture == "" then
|
|
texture = def.wield_image
|
|
if def.tiles then
|
|
texture = def.tiles[1]
|
|
end
|
|
end
|
|
texture = texture .. "^[resize:8x8" -- Crops image
|
|
minetest.add_particlespawner({
|
|
amount = 12*self.height,
|
|
time = 0.1,
|
|
minpos = minppos,
|
|
maxpos = maxppos,
|
|
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,
|
|
minsize = 2*self.height,
|
|
maxsize = 3*self.height,
|
|
collisiondetection = true,
|
|
vertical = false,
|
|
texture = texture,
|
|
})
|
|
if self.food >= feed_count then
|
|
self.food = mobkit.remember(self, "food", 0)
|
|
if tame and not self.tamed then
|
|
mob_core.set_owner(self, clicker:get_player_name())
|
|
minetest.chat_send_player(clicker:get_player_name(), mob_name.." has been tamed!")
|
|
mobkit.clear_queue_high(self)
|
|
paleotest.particle_spawner(pos, "mob_core_green_particle.png", "float", min, max)
|
|
end
|
|
if breed then
|
|
if self.child then return false end
|
|
if self.breeding then return false end
|
|
if self.breeding_cooldown <= 0 then
|
|
self.breeding = true
|
|
local min = vector.subtract(pos, 0.5)
|
|
local max = vector.add(pos, 0.5)
|
|
better_fauna.particle_spawner(pos, "heart.png", "float", min, max)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
------------------
|
|
-- HQ Functions --
|
|
------------------
|
|
|
|
function better_fauna.hq_sporadic_flee(self, prty, player)
|
|
local tyaw = 0
|
|
local init = true
|
|
local timer = 1
|
|
if not player then return true end
|
|
local func = function(self)
|
|
if not mobkit.is_alive(player) then return true end
|
|
if not mobkit.is_alive(self) then return true end
|
|
local pos = mobkit.get_stand_pos(self)
|
|
local yaw = self.object:get_yaw()
|
|
local tpos = vector.add(pos, vector.multiply(minetest.yaw_to_dir(yaw), 4))
|
|
|
|
if init then
|
|
tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos))
|
|
end
|
|
if self._anim ~= "run" then
|
|
mobkit.animate(self, "run")
|
|
end
|
|
|
|
if mobkit.is_queue_empty_low(self) then
|
|
timer = timer - self.dtime
|
|
if timer < 0 then
|
|
tyaw = yaw - random(1.6, 3.2)
|
|
timer = 1
|
|
end
|
|
if abs(tyaw - yaw) > 0.1 then
|
|
mobkit.turn2yaw(self, tyaw)
|
|
end
|
|
mobkit.go_forward_horizontal(self, self.max_speed)
|
|
if timer <= 0 then return true end
|
|
end
|
|
end
|
|
mobkit.queue_high(self, func, prty)
|
|
end
|
|
|
|
function better_fauna.hq_follow_player(self, prty, player) -- Follow Player
|
|
if not player then return end
|
|
if not mob_core.follow_holding(self, player) then return end
|
|
local func = function(self)
|
|
if not mobkit.is_alive(player) then
|
|
mobkit.clear_queue_high(self)
|
|
return true
|
|
end
|
|
if mobkit.is_queue_empty_low(self) then
|
|
local pos = mobkit.get_stand_pos(self)
|
|
local tpos = player:get_pos()
|
|
if mob_core.follow_holding(self, player) then
|
|
self.status = mobkit.remember(self, "status", "following")
|
|
local dist = vec_dist(pos, tpos)
|
|
local yaw = self.object:get_yaw()
|
|
local tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos))
|
|
if dist > self.view_range then
|
|
self.status = mobkit.remember(self, "status", "")
|
|
return true
|
|
end
|
|
better_fauna.path_to_next_waypoint(self, tpos, 0.85)
|
|
if vec_dist(pos, tpos) < hitbox(self)[4] + 2 then
|
|
mobkit.lq_idle(self, 0.1, "stand")
|
|
end
|
|
else
|
|
self.status = mobkit.remember(self, "status", "")
|
|
mobkit.lq_idle(self, 0.1, "stand")
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
mobkit.queue_high(self, func, prty)
|
|
end
|
|
|
|
function better_fauna.get_nearby_mate(self, name)
|
|
for _,obj in ipairs(self.nearby_objects) do
|
|
if mobkit.is_alive(obj)
|
|
and not obj:is_player()
|
|
and obj:get_luaentity().name == name
|
|
and obj:get_luaentity().gender ~= self.gender
|
|
and obj:get_luaentity().breeding then
|
|
return obj
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
function better_fauna.hq_breed(self, prty)
|
|
local mate = better_fauna.get_nearby_mate(self, self.name)
|
|
if not mate then return end
|
|
local speed_factor = 0.5
|
|
local func = function(self)
|
|
if mobkit.is_queue_empty_low(self) then
|
|
local pos = mobkit.get_stand_pos(self)
|
|
local tpos = mate:get_pos()
|
|
local dist = vec_dist(pos, tpos) - math.abs(hitbox(self)[4])
|
|
if dist > 1.5 then
|
|
speed_factor = 0.5
|
|
else
|
|
speed_factor = 0.1
|
|
end
|
|
mob_core.goto_next_waypoint(self, tpos, speed_factor)
|
|
if dist < 1.75 then
|
|
self.breeding_time = self.breeding_time + 1
|
|
end
|
|
if self.breeding_time >= 2
|
|
or mate:get_luaentity().breeding_time >= 2 then
|
|
if self.gender == "female" then
|
|
mob_core.spawn_child(pos, self.name)
|
|
end
|
|
self.breeding = false
|
|
self.breeding_time = 0
|
|
self.breeding_cooldown = 300
|
|
mobkit.remember(self, "breeding", self.breeding)
|
|
mobkit.remember(self, "breeding_time", self.breeding_time)
|
|
mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown)
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
mobkit.queue_high(self, func, prty)
|
|
end
|
|
|
|
function better_fauna.hq_fowl_breed(self, prty)
|
|
local mate = better_fauna.get_nearby_mate(self, self.name)
|
|
if not mate then return end
|
|
local speed_factor = 0.5
|
|
local func = function(self)
|
|
if mobkit.is_queue_empty_low(self) then
|
|
local pos = mobkit.get_stand_pos(self)
|
|
local tpos = mate:get_pos()
|
|
local dist = vec_dist(pos, tpos) - math.abs(hitbox(self)[4])
|
|
if dist > 1.5 then
|
|
speed_factor = 0.5
|
|
else
|
|
speed_factor = 0.1
|
|
end
|
|
mob_core.goto_next_waypoint(self, tpos, speed_factor)
|
|
if dist < 1.75 then
|
|
self.breeding_time = self.breeding_time + 1
|
|
end
|
|
if self.breeding_time >= 2
|
|
or mate:get_luaentity().breeding_time >= 2 then
|
|
if self.gender == "female" then
|
|
minetest.add_particlespawner({
|
|
amount = 6,
|
|
time = 0.25,
|
|
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 = vector.new(-1, 2, -1),
|
|
maxvel = vector.new(1, 5, 1),
|
|
minacc = vector.new(0, -9.81, 0),
|
|
maxacc = vector.new(0, -9.81, 0),
|
|
collisiondetection = true,
|
|
texture = "better_fauna_egg_fragment.png",
|
|
})
|
|
mob_core.spawn_child(pos, self.name)
|
|
end
|
|
self.breeding = false
|
|
self.breeding_time = 0
|
|
self.breeding_cooldown = 300
|
|
mobkit.remember(self, "breeding", self.breeding)
|
|
mobkit.remember(self, "breeding_time", self.breeding_time)
|
|
mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown)
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
mobkit.queue_high(self, func, prty)
|
|
end
|
|
|
|
function better_fauna.hq_eat(self, prty)
|
|
local func = function(self)
|
|
local pos = mobkit.get_stand_pos(self)
|
|
local under = vector.new(pos.x, pos.y - 1, pos.z)
|
|
for _, node in ipairs(self.consumable_nodes) do
|
|
if node.name == minetest.get_node(under).name then
|
|
minetest.set_node(under, {name = node.replacement})
|
|
local def = minetest.registered_nodes[node.name]
|
|
local texture = def.tiles[1]
|
|
texture = texture .. "^[resize:8x8"
|
|
minetest.add_particlespawner({
|
|
amount = 6,
|
|
time = 0.1,
|
|
minpos = vector.new(
|
|
pos.x - 0.5,
|
|
pos.y + 0.1,
|
|
pos.z - 0.5
|
|
),
|
|
maxpos = vector.new(
|
|
pos.x + 0.5,
|
|
pos.y + 0.1,
|
|
pos.z + 0.5
|
|
),
|
|
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,
|
|
minsize = 1,
|
|
maxsize = 2,
|
|
collisiondetection = true,
|
|
vertical = false,
|
|
texture = texture,
|
|
})
|
|
self.gotten = false
|
|
mobkit.remember(self, "gotten", self.gotten)
|
|
return true
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
mobkit.queue_high(self, func, prty)
|
|
end
|
|
|
|
---------------------------------
|
|
-- Entity Definition Functions --
|
|
---------------------------------
|
|
|
|
function better_fauna.on_step(self, dtime, moveresult)
|
|
mob_core.on_step(self, dtime, moveresult)
|
|
if mobkit.timer(self, 1) then
|
|
if self.breeding_cooldown > 0 then
|
|
self.breeding_cooldown = self.breeding_cooldown - 1
|
|
end
|
|
mobkit.remember(self, "breeding_cooldown", self.breeding_cooldown)
|
|
end
|
|
end
|
|
|
|
function better_fauna.on_activate(self, staticdata, dtime_s)
|
|
mob_core.on_activate(self, staticdata, dtime_s)
|
|
self.gotten = mobkit.recall(self, "gotten") or false
|
|
self.attention_span = mobkit.recall(self, "attention_span") or 0
|
|
self.breeding = mobkit.recall(self, "breeding") or false
|
|
self.breeding_time = mobkit.recall(self, "breeding_time") or 0
|
|
self.breeding_cooldown = mobkit.recall(self, "breeding_cooldown") or 0
|
|
end
|
|
|
|
-------------
|
|
-- Physics --
|
|
-------------
|
|
|
|
function better_fauna.lightweight_physics(self)
|
|
local vel = self.object:get_velocity()
|
|
if self.isonground and not self.isinliquid then
|
|
self.object:set_velocity({x= vel.x> 0.2 and vel.x*mobkit.friction or 0,
|
|
y=vel.y,
|
|
z=vel.z > 0.2 and vel.z*mobkit.friction or 0})
|
|
end
|
|
if self.springiness and self.springiness > 0 then
|
|
local vnew = vector.new(vel)
|
|
|
|
if not self.collided then
|
|
for _,k in ipairs({'y','z','x'}) do
|
|
if vel[k]==0 and abs(self.lastvelocity[k])> 0.1 then
|
|
vnew[k]=-self.lastvelocity[k]*self.springiness
|
|
end
|
|
end
|
|
end
|
|
if not vector.equals(vel,vnew) then
|
|
self.collided = true
|
|
else
|
|
if self.collided then
|
|
vnew = vector.new(self.lastvelocity)
|
|
end
|
|
self.collided = false
|
|
end
|
|
|
|
self.object:set_velocity(vnew)
|
|
end
|
|
local surface = nil
|
|
local surfnodename = nil
|
|
local spos = mobkit.get_stand_pos(self)
|
|
spos.y = spos.y+0.01
|
|
local snodepos = mobkit.get_node_pos(spos)
|
|
local surfnode = mobkit.nodeatpos(spos)
|
|
while surfnode and surfnode.drawtype == 'liquid' do
|
|
surfnodename = surfnode.name
|
|
surface = snodepos.y+0.5
|
|
if surface > spos.y+self.height then break end
|
|
snodepos.y = snodepos.y+1
|
|
surfnode = mobkit.nodeatpos(snodepos)
|
|
end
|
|
self.isinliquid = surfnodename
|
|
if surface then
|
|
local submergence = min(surface-spos.y,self.height)/self.height
|
|
local buoyacc = mobkit.gravity*(self.buoyancy-submergence)
|
|
mobkit.set_acceleration(self.object,
|
|
{x=-vel.x*self.water_drag,y=buoyacc-vel.y*abs(vel.y)*0.4,z=-vel.z*self.water_drag})
|
|
else
|
|
self.object:set_acceleration({x=0,y=-2.8,z=0})
|
|
end
|
|
end
|