271 lines
6.2 KiB
Lua
271 lines
6.2 KiB
Lua
local PATH_FIND_ALGORITHM
|
|
PATH_FIND_ALGORITHM = "Dijkstra"
|
|
|
|
PyuTest.ENTITY_BLOOD_AMOUNT = 6
|
|
PyuTest.HUMAN_LIKE_CBOX = { -0.25, -1, -0.25, 0.25, 1, 0.25 }
|
|
PyuTest.SMALL_ANIMAL_CBOX = {-0.25, -0.5, -0.25, 0.25, 0.15, 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 core.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
|
|
|
|
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_entity(target)
|
|
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 = target
|
|
|
|
if state.target.object == nil then
|
|
return
|
|
end
|
|
|
|
state.target.position = state.target.object:get_pos()
|
|
state.target.path = core.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
|
|
|
|
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()
|
|
|
|
self:path_find_entity(PyuTest.get_nearest_entity(obj, pos, cfg.view_range, follow_only_player, false))
|
|
end
|
|
|
|
function class:on_punch()
|
|
local pos = self.object:get_pos()
|
|
core.sound_play({
|
|
name = self.options.sounds.hurt,
|
|
pos = pos
|
|
})
|
|
|
|
core.add_particlespawner({
|
|
amount = 8,
|
|
time = 0.4,
|
|
minexptime = 0.4,
|
|
maxexptime = 0.8,
|
|
minsize = 1.5,
|
|
maxsize = 1.62,
|
|
vertical = false,
|
|
glow = core.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
|
|
|
|
function class:on_death()
|
|
local pos = self.object:get_pos()
|
|
|
|
for _, v in pairs(self.options.drops) do
|
|
PyuTest.drop_item(pos, v)
|
|
end
|
|
end
|
|
|
|
function class:on_step(dtime, moveresult)
|
|
self.dtime = dtime
|
|
self.moveresult = moveresult
|
|
|
|
local obj = self.object
|
|
local cfg = self.options
|
|
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)
|
|
|
|
self:do_physics()
|
|
|
|
if not self.state.action_timer then
|
|
core.after(1, function ()
|
|
self.state.action_timer = true
|
|
|
|
core.after(1, function ()
|
|
self.action_timer = false
|
|
end)
|
|
end)
|
|
end
|
|
|
|
|
|
-- follow_player
|
|
if self.options.follow_player then
|
|
self:path_find_nearest_entity(false)
|
|
end
|
|
|
|
-- follow_items
|
|
for o in core.objects_inside_radius(obj:get_pos(), cfg.view_range) do
|
|
if o ~= obj then
|
|
for _, v in pairs(self.options.follow_items) do
|
|
local i = o:get_wielded_item()
|
|
|
|
if i:get_name() == v then
|
|
self:path_find_entity(o)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- attack_entities
|
|
if self.state.action_timer then
|
|
for o in core.objects_inside_radius(obj:get_pos(), cfg.hit_range) do
|
|
if o ~= obj then
|
|
for _, v in pairs(cfg.attack_entities) do
|
|
local lua_entity = o:get_luaentity()
|
|
|
|
if (v == "player" and o:is_player()) or (lua_entity and v == lua_entity.name) then
|
|
PyuTest.deal_damage(o, cfg.hit_damage * dtime, {
|
|
type = "punch",
|
|
object = obj
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
PyuTest.make_mob = function(name, properties, options)
|
|
local default_options = {
|
|
max_jump = 1,
|
|
max_drop = 8,
|
|
speed = 3,
|
|
hit_range = 3,
|
|
hit_damage = 3,
|
|
view_range = 10,
|
|
gravity = true,
|
|
gravity_multiplier = 1,
|
|
health_regen = true,
|
|
follow_player = false,
|
|
follow_items = {},
|
|
attack_entities = {},
|
|
|
|
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
|
|
|
|
core.register_entity(name, setmetatable({
|
|
initial_properties = PyuTest.util.tableconcat(properties, {
|
|
hp_max = properties.hp_max or 20,
|
|
physical = true,
|
|
collide_with_objects = false,
|
|
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 = {
|
|
action_timer = false,
|
|
target = {
|
|
object = nil,
|
|
position = nil,
|
|
path = nil,
|
|
pathindex = nil
|
|
}
|
|
},
|
|
}, { __index = class }))
|
|
end
|