2024-11-16 10:05:03 -06:00

266 lines
6.2 KiB
Lua

local PATH_FIND_ALGORITHM
PATH_FIND_ALGORITHM = "Dijkstra"
-- PATH_FIND_ALGORITHM = "A*_noprefetch"
-- PATH_FIND_ALGORITHM = "A*"
PyuTest.ENTITY_BLOOD_AMOUNT = 6
PyuTest.HUMAN_LIKE_CBOX = { -0.25, -1, -0.25, 0.25, 1, 0.25 }
PyuTest.get_nearest_entity = function(entity, pos, range, only_player, dont_ignore_allies)
local closet_distance = math.huge
local nearest
local function set_nearest_and_distance(n, d)
nearest = n
closet_distance = d
end
for obj in minetest.objects_inside_radius(pos, range) do
local dist = vector.distance(pos, obj:get_pos())
if dist < closet_distance and obj ~= entity then
if only_player then
if obj:is_player() then
set_nearest_and_distance(obj, dist)
end
else
local e = obj:get_luaentity()
-- Ignore items
if e then
if e.name ~= "__builtin:item" then
if not dont_ignore_allies then
local self = entity:get_luaentity()
if self then
if self.name ~= e.name then
set_nearest_and_distance(obj, dist)
end
end
end
end
else
set_nearest_and_distance(obj, dist)
end
end
end
end
return nearest
end
PyuTest.register_entity_spawn = function(name, entity, def)
if def == nil then
error("Table expected for options!")
end
minetest.register_node(name, {
description = "Entity Spawner",
groups = {
not_in_creative_inventory = 1,
},
drawtype = "airlike",
walkable = false,
pointable = false
})
minetest.register_decoration({
sidelen = 80,
decoration = name,
deco_type = "simple",
place_on = def.place_on,
spawn_by = def.spawn_by,
num_spawn_by = def.num_spawn_by,
fill_ratio = def.fill_ratio or 0.0008,
y_max = def.y_max or 31000,
y_min = def.y_min or -31000,
biomes = {}
})
minetest.register_lbm({
name = name .. "_spawn",
run_at_every_load = true,
nodenames = { name },
action = function(pos)
minetest.remove_node(pos)
local min = def.min or 1
local max = def.max or 1
if max == 1 then
minetest.add_entity(pos, entity)
else
for _ = min, math.random(min, max) do
minetest.add_entity(pos, entity)
end
end
end
})
end
local class = {}
function class:do_physics()
local obj = self.object
local state = self.state
local cfg = self.options
local moveresult = self.moveresult
local pos = obj:get_pos()
if cfg.gravity then
if not moveresult.touching_ground then
end
end
end
function class:path_find_nearest_entity(follow_only_player)
local obj = self.object
local state = self.state
local cfg = self.options
local pos = obj:get_pos()
if state.target.path == nil then
state.target.object = PyuTest.get_nearest_entity(obj, pos, cfg.sight_range, follow_only_player)
if state.target.object == nil then
return
end
state.target.position = state.target.object:get_pos()
state.target.path = minetest.find_path(pos, state.target.position, cfg.view_range, cfg.max_jump, cfg.max_drop,
PATH_FIND_ALGORITHM)
state.target.pathindex = 1
else
if state.target.pathindex == #state.target.path then
state.target.path = nil
state.target.object = nil
state.target.position = nil
state.target.pathindex = nil
else
local p = state.target.path[state.target.pathindex]
-- obj:set_velocity(p * dtime)
obj:move_to(p, true)
state.target.pathindex = state.target.pathindex + 1
end
end
end
PyuTest.make_mob = function(name, properties, options)
local default_options = {
ai = "dummy",
max_jump = 1,
max_drop = 8,
speed = 3,
view_range = 3,
sight_range = 10,
gravity = true,
gravity_multiplier = 1,
health_regen = true,
sounds = {
hurt = "pyutest-entity-hurt"
},
drops = {}
}
local cfg = {}
for k, v in pairs(options) do
cfg[k] = v
end
for k, v in pairs(default_options) do
if cfg[k] == nil then
cfg[k] = v
end
end
local collisionbox = properties.collisionbox or PyuTest.HUMAN_LIKE_CBOX
minetest.register_entity(name, setmetatable({
initial_properties = PyuTest.util.tableconcat(properties, {
hp_max = properties.hp_max or 20,
physical = true,
collide_with_objects = true,
stepheight = properties.stepheight or 1.1,
collisionbox = collisionbox,
selectionbox = properties.selectionbox or collisionbox,
show_on_minimap = properties.show_on_minimap or true,
infotext = "",
}),
options = cfg,
state = {
target = {
object = nil,
position = nil,
path = nil,
pathindex = nil
}
},
on_step = function(self, dtime, moveresult)
self.dtime = dtime
self.moveresult = moveresult
local ai = self.options.ai
local obj = self.object
local p = obj:get_properties()
local hp = obj:get_hp()
if self.options.health_regen and not (hp >= p.hp_max) then
obj:set_hp(hp + 0.5)
end
if obj:get_hp() > p.hp_max then
obj:set_hp(p.hp_max)
end
p.infotext = string.format("Mob Health: %d/%d", obj:get_hp(), p.hp_max)
obj:set_properties(p)
if ai == nil or ai == "dummy" then
return
end
self:do_physics()
if ai == "follownearest" then
self:path_find_nearest_entity(false)
end
end,
on_punch = function(self)
local pos = self.object:get_pos()
minetest.sound_play(self.options.sounds.hurt, { pos = pos })
minetest.add_particlespawner({
amount = 8,
time = 0.4,
minexptime = 0.4,
maxexptime = 0.8,
minsize = 1.5,
maxsize = 1.62,
vertical = false,
glow = minetest.LIGHT_MAX,
collisiondetection = false,
texture = "pyutest-blood.png",
minpos = pos,
maxpos = pos,
minvel = vector.new(-1, -1, 1),
maxvel = vector.new(1, 1, 1),
})
end,
on_death = function(self)
local pos = self.object:get_pos()
for _, v in pairs(self.options.drops) do
PyuTest.drop_item(pos, v)
end
end
}, { __index = class }))
end