267 lines
5.6 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 = {
_name = "Entity",
_last_hp = 0,
_heal_timer = 0,
_attack_timer = 0,
_velocity = vector.new(0, 0, 0),
_state = {
target = {
object = nil,
position = nil,
path = nil,
pathindex = nil
}
},
}
function class:_do_physics()
-- TODO
end
function class:_path_find_entity(target)
-- TODO
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:_visualize_damage()
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:_attack()
local obj = self.object
local cfg = self._options
local function attack(o)
if cfg.attack_type == "melee" then
PyuTest.deal_damage(o, cfg.hit_damage, {
type = "punch",
object = obj
})
elseif cfg.attack_type == "ranged" then
PyuTest.shoot_projectile_from_object(
cfg.attack_projectile,
obj,
cfg.attack_projectile_velocity
)
else
error(string.format("Expected attack_type to be melee or ranged"))
end
end
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()
local b =
(v == "player" and o:is_player()) or (lua_entity and v == lua_entity.name)
if b then
attack(o)
end
end
end
end
end
function class:on_death()
self:_visualize_damage()
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
self._attack_timer = self._attack_timer + (2 * dtime)
self._heal_timer = self._heal_timer + (2 * dtime)
local obj = self.object
local cfg = self._options
local p = obj:get_properties()
local hp_for_healing = obj:get_hp()
if self._options.health_regen and hp_for_healing < p.hp_max and self._heal_timer >= 5 then
self._heal_timer = 0
obj:set_hp(hp_for_healing + 1)
end
if obj:get_hp() > p.hp_max then
obj:set_hp(p.hp_max)
end
if obj:get_hp() < self._last_hp then
self:_visualize_damage()
end
p.infotext = string.format("Mob Health: %d/%d", obj:get_hp(), p.hp_max)
obj:set_properties(p)
self:_do_physics()
-- 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._attack_timer >= 3 then
self._attack_timer = 0
self:_attack()
end
self.object:set_velocity(self._velocity)
self._last_hp = self.object:get_hp()
end
PyuTest.make_mob = function(id, 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_type = "melee", -- Can be melee or ranged
attack_projectile = "pyutest_projectiles:arrow",
attack_projectile_velocity = 20,
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(id, 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,
infotext = "",
}),
_options = cfg,
_name = name,
}, { __index = class }))
end