1689 lines
48 KiB
Lua
1689 lines
48 KiB
Lua
------------------
|
|
-- Mob Core API --
|
|
------------------
|
|
----- Ver 0.1 ----
|
|
|
|
---------------------
|
|
-- Local Variables --
|
|
---------------------
|
|
|
|
local abs = math.abs
|
|
local floor = math.floor
|
|
local random = math.random
|
|
local min = math.min
|
|
|
|
local creative = minetest.settings:get_bool("creative_mode")
|
|
|
|
mob_core.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(mob_core.walkable_nodes, name)
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
----------------------
|
|
-- Helper Functions --
|
|
----------------------
|
|
|
|
local function all_first_to_upper(str)
|
|
str = string.gsub(" "..str, "%W%l", string.upper):sub(2)
|
|
return str
|
|
end
|
|
|
|
local function underscore_to_space(str)
|
|
return (str:gsub("_", " "))
|
|
end
|
|
|
|
function mob_core.get_name_proper(str)
|
|
if str then
|
|
if str:match(":") then
|
|
str = str:split(":")[2]
|
|
end
|
|
str = all_first_to_upper(str)
|
|
str = underscore_to_space(str)
|
|
return str
|
|
end
|
|
end
|
|
|
|
function mob_core.is_mobkit_mob(object)
|
|
if type(object) == 'userdata' then
|
|
object = object:get_luaentity()
|
|
end
|
|
if type(object) == 'table' then
|
|
if (object.logic or object.brainfunc) then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function mob_core.shared_owner(self, object)
|
|
if object:is_player() then return false end
|
|
if type(object) == 'userdata' then
|
|
object = object:get_luaentity()
|
|
end
|
|
if not self.tamed then return false end
|
|
if self.owner and object.owner then
|
|
if self.owner == object.owner then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function mob_core.find_val(tbl, val)
|
|
for _, v in ipairs(tbl) do
|
|
if v == val then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Follow Holding? --
|
|
|
|
function mob_core.follow_holding(self, player)
|
|
local item = player:get_wielded_item()
|
|
local t = type(self.follow)
|
|
if t == "string"
|
|
and item:get_name() == self.follow then
|
|
return true
|
|
elseif t == "table" then
|
|
for no = 1, #self.follow do
|
|
if self.follow[no] == item:get_name() then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-----------------------
|
|
-- Mob Item Handling --
|
|
-----------------------
|
|
|
|
function mob_core.register_spawn_egg(mob, col1, col2, inventory_image)
|
|
if col1 and col2 then
|
|
local len1 = string.len(col1)
|
|
local len2 = string.len(col2)
|
|
if len1 == 6 then
|
|
col1 = col1 .. "d9"
|
|
end
|
|
if len2 == 6 then
|
|
col2 = col2 .. "d9"
|
|
end
|
|
local base = "mob_core_spawn_egg_base.png^(mob_core_spawn_egg_base.png^[colorize:#"..col1..")"
|
|
local spots = "mob_core_spawn_egg_overlay.png^(mob_core_spawn_egg_overlay.png^[colorize:#"..col2..")"
|
|
inventory_image = base .. "^" .. spots
|
|
end
|
|
minetest.register_craftitem(mob:split(":")[1]..":spawn_"..mob:split(":")[2], {
|
|
description = "Spawn "..mob_core.get_name_proper(mob),
|
|
inventory_image = inventory_image,
|
|
stack_max = 99,
|
|
on_place = function(itemstack, _, pointed_thing)
|
|
local mobdef = minetest.registered_entities[mob]
|
|
local spawn_offset = math.abs(mobdef.collisionbox[2])
|
|
local pos = minetest.get_pointed_thing_position(pointed_thing, true)
|
|
pos.y = pos.y+spawn_offset
|
|
minetest.add_entity(pos, mob)
|
|
if not creative then
|
|
itemstack:take_item()
|
|
return itemstack
|
|
end
|
|
end,
|
|
})
|
|
end
|
|
|
|
function mob_core.register_set(mob, background, mask)
|
|
local invimg = background
|
|
if mask then
|
|
invimg = "mob_core_spawn_egg_base.png^(" .. invimg ..
|
|
"^[mask:mob_core_spawn_egg_overlay.png)"
|
|
end
|
|
if not minetest.registered_entities[mob] then
|
|
return
|
|
end
|
|
-- register new spawn egg containing mob information
|
|
minetest.register_craftitem(mob .. "_set", {
|
|
description = mob_core.get_name_proper(mob).." (Captured)",
|
|
inventory_image = invimg,
|
|
groups = {not_in_creative_inventory = 1},
|
|
stack_max = 1,
|
|
on_place = function(itemstack, placer, pointed_thing)
|
|
local pos = pointed_thing.above
|
|
-- am I clicking on something with existing on_rightclick function?
|
|
local under = minetest.get_node(pointed_thing.under)
|
|
local node = minetest.registered_nodes[under.name]
|
|
if node and node.on_rightclick then
|
|
return node.on_rightclick(pointed_thing.under, under, placer, itemstack)
|
|
end
|
|
if pos
|
|
and not minetest.is_protected(pos, placer:get_player_name()) then
|
|
pos.y = pos.y + 1
|
|
local staticdata = itemstack:get_meta():get_string("staticdata")
|
|
minetest.add_entity(pos, mob, staticdata)
|
|
itemstack:take_item()
|
|
end
|
|
return itemstack
|
|
end,
|
|
})
|
|
end
|
|
|
|
-----------------------
|
|
-- Utility Functions --
|
|
-----------------------
|
|
|
|
------------
|
|
-- Sounds --
|
|
------------
|
|
|
|
function mob_core.make_sound(self, sound)
|
|
local spec = self.sounds and self.sounds[sound]
|
|
local parameters = {object = self.object}
|
|
|
|
if type(spec) == 'table' then
|
|
if #spec > 0 then spec = spec[random(#spec)] end
|
|
|
|
local function in_range(value)
|
|
return type(value) == 'table' and value[1]+random()*(value[2]-value[1]) or value
|
|
end
|
|
|
|
local pitch = 1.0
|
|
|
|
pitch = pitch + random(-10, 10) * 0.005
|
|
|
|
if self.child
|
|
and self.sounds.alter_child_pitch then
|
|
parameters.pitch = 2.0
|
|
end
|
|
|
|
minetest.sound_play(spec, parameters)
|
|
|
|
if not spec.gain then spec.gain = 1.0 end
|
|
if not spec.distance then spec.distance = 16 end
|
|
|
|
--pick random values within a range if they're a table
|
|
parameters.gain = in_range(spec.gain)
|
|
parameters.max_hear_distance = in_range(spec.distance)
|
|
parameters.fade = in_range(spec.fade)
|
|
parameters.pitch = pitch
|
|
return minetest.sound_play(spec.name, parameters)
|
|
end
|
|
return minetest.sound_play(spec, parameters)
|
|
end
|
|
|
|
function mob_core.random_sound(self, chance) -- Random Sound
|
|
if not chance then chance = 150 end
|
|
if math.random(1, chance) == 1 then
|
|
mob_core.make_sound(self, "random")
|
|
end
|
|
end
|
|
|
|
----------------
|
|
-- Drop Items --
|
|
----------------
|
|
|
|
function mob_core.item_drop(self) -- Drop Items
|
|
if not self.drops or #self.drops == 0 then
|
|
return
|
|
end
|
|
local obj, item, num
|
|
local pos = mobkit.get_stand_pos(self)
|
|
for n = 1, #self.drops do
|
|
if math.random(1, self.drops[n].chance) == 1 then
|
|
num = math.random(self.drops[n].min or 0, self.drops[n].max or 1)
|
|
item = self.drops[n].name
|
|
if self.drops[n].min ~= 0 then
|
|
obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
|
|
if obj then
|
|
local v = math.random(-1, 1)
|
|
obj:add_velocity({x = v, y = 1, z = v})
|
|
end
|
|
elseif obj then
|
|
obj:remove()
|
|
end
|
|
end
|
|
end
|
|
self.drops = {}
|
|
end
|
|
|
|
------------------------
|
|
-- Damage and Vitals --
|
|
------------------------
|
|
|
|
-- Damage Indication --
|
|
|
|
local function flash_red(self)
|
|
minetest.after(0.0, function()
|
|
self.object:settexturemod("^[colorize:#FF000040")
|
|
core.after(0.2, function()
|
|
if mobkit.is_alive(self) then
|
|
self.object:settexturemod("")
|
|
end
|
|
end)
|
|
end)
|
|
end
|
|
|
|
-- Death --
|
|
|
|
local pi = math.pi
|
|
|
|
function mob_core.lq_fallover(self)
|
|
local zrot = 0
|
|
local init = true
|
|
local func=function(self)
|
|
if init then
|
|
local vel = self.object:get_velocity()
|
|
self.object:set_velocity(mobkit.pos_shift(vel,{y=1}))
|
|
mobkit.animate(self,'stand')
|
|
init = false
|
|
end
|
|
zrot=zrot+pi*0.05
|
|
local rot = self.object:get_rotation()
|
|
if rot then self.object:set_rotation({x=rot.x,y=rot.y,z=zrot}) end
|
|
if zrot >= pi*0.5 then return true end
|
|
end
|
|
mobkit.queue_low(self,func)
|
|
end
|
|
|
|
function mob_core.on_die(self)
|
|
mobkit.clear_queue_high(self)
|
|
mobkit.clear_queue_low(self)
|
|
local pos = mobkit.get_stand_pos(self)
|
|
if self.driver then
|
|
mob_core.force_detach(self.driver)
|
|
end
|
|
if self.owner then
|
|
self.owner = nil
|
|
end
|
|
if self.sounds and self.sounds["death"] then
|
|
mob_core.make_sound(self, "death")
|
|
end
|
|
self.object:set_velocity({x=0,y=0,z=0})
|
|
self.object:settexturemod("^[colorize:#FF000040")
|
|
local timer = 1
|
|
local start = true
|
|
local func = function()
|
|
if not mobkit.exists(self) then return true end
|
|
if start then
|
|
if self.animation
|
|
and self.animation["death"] then
|
|
mobkit.animate(self,"death")
|
|
else
|
|
mob_core.lq_fallover(self)
|
|
end
|
|
self.logic = function() end -- brain dead as well
|
|
start = false
|
|
end
|
|
timer = timer-self.dtime
|
|
if timer <= 0 then
|
|
if self.driver then
|
|
mob_core.force_detach(self.driver)
|
|
end
|
|
mob_core.item_drop(self)
|
|
minetest.add_particlespawner({
|
|
amount = 12,
|
|
time = 0.1,
|
|
minpos = {
|
|
x = pos.x - self.collisionbox[4]*0.75,
|
|
y = pos.y,
|
|
z = pos.z - self.collisionbox[4]*0.75,
|
|
},
|
|
maxpos = {
|
|
x = pos.x + self.collisionbox[4]*0.75,
|
|
y = pos.y + self.collisionbox[4]*0.75,
|
|
z = pos.z + self.collisionbox[4]*0.75,
|
|
},
|
|
minvel = {x=-0.2, y=-0.1, z=-0.2},
|
|
maxvel = {x=0.2, y=-0.1, z=0.2},
|
|
minacc = {x=0, y=0.25, z=0},
|
|
maxacc = {x=0, y=0.45, z=0},
|
|
minexptime = 1.5,
|
|
maxexptime = 2,
|
|
minsize = 2,
|
|
maxsize = 3,
|
|
collisiondetection = true,
|
|
vertical = false,
|
|
texture = "mob_core_red_particle.png"
|
|
})
|
|
self.object:remove()
|
|
end
|
|
minetest.after(2, function() -- fail safe
|
|
if not mobkit.exists(self) then return true end
|
|
if self.driver then
|
|
mob_core.force_detach(self.driver)
|
|
end
|
|
mob_core.item_drop(self)
|
|
minetest.add_particlespawner({
|
|
amount = self.collisionbox[4]*4,
|
|
time = 0.25,
|
|
minpos = {
|
|
x = pos.x - self.collisionbox[4]*0.5,
|
|
y = pos.y,
|
|
z = pos.z - self.collisionbox[4]*0.5,
|
|
},
|
|
maxpos = {
|
|
x = pos.x + self.collisionbox[4]*0.5,
|
|
y = pos.y + self.collisionbox[4]*0.5,
|
|
z = pos.z + self.collisionbox[4]*0.5,
|
|
},
|
|
minacc = {x = -0.25, y = 0.5, z = -0.25},
|
|
maxacc = {x = 0.25, y = 0.25, z = 0.25},
|
|
minexptime = 0.75,
|
|
maxexptime = 1,
|
|
minsize = 4,
|
|
maxsize = 4,
|
|
texture = "mob_core_red_particle.png",
|
|
glow = 16,
|
|
})
|
|
self.object:remove()
|
|
end)
|
|
end
|
|
mobkit.queue_high(self, func, 100)
|
|
end
|
|
|
|
-- Vitals --
|
|
|
|
function mob_core.vitals(self)
|
|
if not mobkit.is_alive(self) then return end
|
|
-- Fall Damage
|
|
if self.fall_damage == nil then
|
|
self.fall_damage = true
|
|
end
|
|
if self.fall_damage then
|
|
if not self.isonground
|
|
and not self.isinliquid
|
|
and not self.fall_start then
|
|
self.fall_start = mobkit.get_stand_pos(self).y
|
|
end
|
|
if self.fall_start then
|
|
local fall_distance = self.fall_start - mobkit.get_stand_pos(self).y
|
|
if not self.max_fall then
|
|
self.max_fall = 3
|
|
end
|
|
if self.isonground
|
|
and fall_distance > self.max_fall then
|
|
flash_red(self)
|
|
mob_core.make_sound(self, "hurt")
|
|
mobkit.hurt(self, fall_distance)
|
|
self.fall_start = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Lava/Fire Damage
|
|
if self.igniter_damage == nil then
|
|
self.igniter_damage = true
|
|
end
|
|
|
|
if self.igniter_damage then
|
|
local pos = mobkit.get_stand_pos(self)
|
|
local node = minetest.get_node(pos)
|
|
if node and minetest.registered_nodes[node.name].groups.igniter then
|
|
if mobkit.timer(self,1) then
|
|
flash_red(self)
|
|
mob_core.make_sound(self, "hurt")
|
|
mobkit.hurt(self, self.max_hp/16)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Drowning
|
|
if self.lung_capacity then
|
|
local colbox = self.object:get_properties().collisionbox
|
|
local headnode = mobkit.nodeatpos(mobkit.pos_shift(self.object:get_pos(),{y=colbox[5]})) -- node at hitbox top
|
|
if headnode and headnode.drawtype == 'liquid' then
|
|
self.oxygen = self.oxygen - self.dtime
|
|
else
|
|
self.oxygen = self.lung_capacity
|
|
end
|
|
|
|
if self.oxygen <= 0 then
|
|
if mobkit.timer(self,2) then
|
|
flash_red(self)
|
|
mobkit.hurt(self,self.max_hp/self.lung_capacity)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Basic Damage -- Flash red, Knockback, Make sound.
|
|
|
|
function mob_core.on_punch_basic(self, puncher, tool_capabilities, dir)
|
|
local item = puncher:get_wielded_item()
|
|
if mobkit.is_alive(self) then
|
|
if self.immune_to then
|
|
for i = 1, #self.immune_to do
|
|
if item:get_name() == self.immune_to[i] then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
if self.protected == true and puncher:get_player_name() ~= self.owner then
|
|
return
|
|
else
|
|
flash_red(self)
|
|
if self.isonground then
|
|
local hvel = vector.multiply(vector.normalize({x=dir.x,y=0,z=dir.z}),4)
|
|
self.object:add_velocity({x=hvel.x,y=2,z=hvel.z})
|
|
end
|
|
mobkit.hurt(self,tool_capabilities.damage_groups.fleshy or 1)
|
|
mob_core.make_sound(self, "hurt")
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Retaliate --
|
|
|
|
function mob_core.on_punch_retaliate(self, puncher, water, group)
|
|
if mobkit.is_alive(self) then
|
|
local pos = self.object:get_pos()
|
|
if (not water) or (water and self.semiaquatic) then
|
|
mob_core.hq_hunt(self, 10, puncher)
|
|
if group then
|
|
local objs = minetest.get_objects_inside_radius(pos, self.view_range)
|
|
for n = 1, #objs do
|
|
local luaent = objs[n]:get_luaentity()
|
|
if luaent and luaent.name == self.name and luaent.owner == self.owner and mobkit.is_alive(luaent) then
|
|
mob_core.hq_hunt(luaent, 10, puncher)
|
|
end
|
|
end
|
|
end
|
|
elseif water and self.isinliquid then
|
|
mob_core.hq_aqua_attack(self, 10, puncher, 1)
|
|
if group then
|
|
local objs = minetest.get_objects_inside_radius(pos, self.view_range)
|
|
for n = 1, #objs do
|
|
local luaent = objs[n]:get_luaentity()
|
|
if luaent and luaent.name == self.name and luaent.owner == self.owner and mobkit.is_alive(luaent) then
|
|
mob_core.hq_aqua_attack(luaent, 10, puncher, 1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Runaway --
|
|
|
|
function mob_core.on_punch_runaway(self, puncher, water, group)
|
|
if mobkit.is_alive(self) then
|
|
local pos = self.object:get_pos()
|
|
if (not water) or (water and not self.isinliquid) then
|
|
mobkit.hq_runfrom(self, 10, puncher)
|
|
if group then
|
|
local objs = minetest.get_objects_inside_radius(pos, self.view_range)
|
|
for n = 1, #objs do
|
|
local luaent = objs[n]:get_luaentity()
|
|
if luaent and luaent.name == self.name and luaent.owner == self.owner and mobkit.is_alive(luaent) then
|
|
mobkit.hq_runfrom(self, 10, puncher)
|
|
end
|
|
end
|
|
end
|
|
elseif water and self.isinliquid then
|
|
mob_core.hq_swimfrom(self, 10, puncher, 1)
|
|
if group then
|
|
local objs = minetest.get_objects_inside_radius(pos, self.view_range)
|
|
for n = 1, #objs do
|
|
local luaent = objs[n]:get_luaentity()
|
|
if luaent and luaent.name == self.name and luaent.owner == self.owner and mobkit.is_alive(luaent) then
|
|
mob_core.hq_swimfrom(luaent, 10, puncher, 1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Default On Punch -- Likely won't be used, more of a demo
|
|
|
|
function mob_core.on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
|
|
mob_core.on_punch_basic(self, puncher, time_from_last_punch, tool_capabilities, dir)
|
|
end
|
|
|
|
-----------------
|
|
-- On Activate --
|
|
-----------------
|
|
|
|
local function set_gender(self)
|
|
self.gender = mobkit.recall(self, "gender") or nil
|
|
if not self.gender then
|
|
if math.random(1, 2) == 1 then
|
|
self.gender = mobkit.remember(self, "gender", "female")
|
|
else
|
|
self.gender = mobkit.remember(self, "gender", "male")
|
|
end
|
|
end
|
|
end
|
|
|
|
function mob_core.activate_nametag(self)
|
|
if not self.nametag then
|
|
return
|
|
end
|
|
self.object:set_properties({
|
|
nametag = self.nametag,
|
|
nametag_color = "#FFFFFF"
|
|
})
|
|
end
|
|
|
|
function mob_core.set_textures(self)
|
|
if not self.texture_no then
|
|
if self.gender == "male" and self.male_textures then
|
|
if #self.male_textures > 1 then
|
|
self.texture_no = random(#self.male_textures)
|
|
else
|
|
self.texture_no = 1
|
|
end
|
|
end
|
|
if self.gender == "female" and self.female_textures then
|
|
if #self.female_textures > 1 then
|
|
self.texture_no = random(#self.female_textures)
|
|
else
|
|
self.texture_no = 1
|
|
end
|
|
end
|
|
end
|
|
if self.textures and self.texture_no then
|
|
local texture_no = self.texture_no
|
|
local props = {}
|
|
if self.gender == "female" then
|
|
if self.female_textures then
|
|
props.textures = {self.female_textures[texture_no]}
|
|
else
|
|
props.textures = {self.textures[texture_no]}
|
|
end
|
|
elseif self.gender == "male" then
|
|
if self.male_textures then
|
|
props.textures = {self.male_textures[texture_no]}
|
|
else
|
|
props.textures = {self.textures[texture_no]}
|
|
end
|
|
end
|
|
if self.child and self.child_textures then
|
|
if self.gender == "female" then
|
|
if self.child_female_textures then
|
|
if texture_no > #self.child_female_textures then
|
|
texture_no = 1
|
|
end
|
|
props.textures = {self.child_female_textures[texture_no]}
|
|
else
|
|
if texture_no > #self.child_textures then
|
|
texture_no = 1
|
|
end
|
|
props.textures = {self.child_textures[texture_no]}
|
|
end
|
|
elseif self.gender == "male" then
|
|
if self.child_male_textures then
|
|
if texture_no > #self.child_male_textures then
|
|
texture_no = 1
|
|
end
|
|
props.textures = {self.child_male_textures[texture_no]}
|
|
else
|
|
if texture_no > #self.child_textures then
|
|
texture_no = 1
|
|
end
|
|
props.textures = {self.child_textures[texture_no]}
|
|
end
|
|
end
|
|
end
|
|
self.object:set_properties(props)
|
|
end
|
|
end
|
|
|
|
function mob_core.on_activate(self, staticdata, dtime_s) -- On Activate
|
|
local init_props = {}
|
|
if not self.textures then
|
|
if self.female_textures then
|
|
init_props.textures = {self.female_textures[1]}
|
|
elseif self.male_textures then
|
|
init_props.textures = {self.male_textures[1]}
|
|
end
|
|
self.object:set_properties(init_props)
|
|
end
|
|
if not self.textures then self.textures = {} end
|
|
mobkit.actfunc(self, staticdata, dtime_s)
|
|
self.tamed = mobkit.recall(self, "tamed") or false
|
|
self.owner = mobkit.recall(self, "owner") or nil
|
|
self.protected = mobkit.recall(self, "protected") or false
|
|
self.food = mobkit.recall(self, "food") or 0
|
|
self.breed_mode = mobkit.recall(self, "breed_mode") or false
|
|
self.breed_timer = mobkit.recall(self, "breed_timer") or 0
|
|
self.growth_stage = mobkit.recall(self, "growth_stage") or 4
|
|
self.growth_timer = mobkit.recall(self, "growth_timer") or 1801
|
|
self.child = mobkit.recall(self, "child") or false
|
|
self.status = mobkit.recall(self, "status") or ""
|
|
self.nametag = mobkit.recall(self, "nametag") or ""
|
|
set_gender(self)
|
|
mob_core.activate_nametag(self)
|
|
mob_core.set_textures(self)
|
|
if self.protected then
|
|
self.timeout = nil
|
|
end
|
|
if self.growth_stage == 1
|
|
and self.scale_stage1 then
|
|
mob_core.set_scale(self, self.scale_stage1)
|
|
elseif self.growth_stage == 2
|
|
and self.scale_stage2 then
|
|
mob_core.set_scale(self, self.scale_stage2)
|
|
elseif self.growth_stage == 3
|
|
and self.scale_stage3 then
|
|
mob_core.set_scale(self, self.scale_stage3)
|
|
elseif self.growth_stage == 4 then
|
|
mob_core.set_scale(self, 1)
|
|
end
|
|
end
|
|
|
|
-----------------------
|
|
-- Utility Functions --
|
|
-----------------------
|
|
|
|
-- Set Scale --
|
|
|
|
function mob_core.set_scale(self, scale)
|
|
self.base_size = self.visual_size
|
|
self.base_colbox = self.collisionbox
|
|
self.base_selbox = self.selectionbox
|
|
|
|
self.object:set_properties({
|
|
visual_size = {
|
|
x = self.base_size.x*scale,
|
|
y = self.base_size.y*scale
|
|
},
|
|
collisionbox = {
|
|
self.base_colbox[1]*scale,
|
|
self.base_colbox[2]*scale,
|
|
self.base_colbox[3]*scale,
|
|
self.base_colbox[4]*scale,
|
|
self.base_colbox[5]*scale,
|
|
self.base_colbox[6]*scale
|
|
},
|
|
})
|
|
end
|
|
|
|
-- Set Owner --
|
|
|
|
function mob_core.set_owner(self, name)
|
|
self.tamed = mobkit.remember(self, "tamed", true)
|
|
self.owner = mobkit.remember(self, "owner", name)
|
|
end
|
|
|
|
-- Spawn Child Mob --
|
|
|
|
function mob_core.spawn_child(pos, mob)
|
|
local obj = minetest.add_entity(pos, mob)
|
|
local luaent = obj:get_luaentity()
|
|
luaent.child = mobkit.remember(luaent,"child",true)
|
|
luaent.growth_timer = mobkit.remember(luaent,"growth_timer",1)
|
|
luaent.growth_stage = mobkit.remember(luaent,"growth_stage",1)
|
|
mob_core.set_scale(luaent, luaent.scale_stage1 or 0.25)
|
|
mob_core.set_textures(luaent)
|
|
return
|
|
end
|
|
|
|
-- Force Tame Command --
|
|
|
|
minetest.register_chatcommand("force_tame", {
|
|
params = "",
|
|
description = "tame pointed mobkit mob",
|
|
privs = {server = true, creative = true},
|
|
func = function(name)
|
|
local player = minetest.get_player_by_name(name)
|
|
if not player then return false end
|
|
local dir = player:get_look_dir()
|
|
local pos = player:get_pos()
|
|
pos.y = pos.y + player:get_properties().eye_height or 1.625
|
|
local dest = vector.add(pos, vector.multiply(dir, 40))
|
|
local ray = minetest.raycast(pos, dest, true, false)
|
|
for pointed_thing in ray do
|
|
if pointed_thing.type == "object" then
|
|
local pointedobject = pointed_thing.ref
|
|
if pointedobject:get_luaentity() then
|
|
pointedobject = pointedobject:get_luaentity()
|
|
local mob_name = mob_core.get_name_proper(pointedobject.name)
|
|
if not pointedobject.tamed then
|
|
if not pointedobject.logic or pointedobject.brainfunc then
|
|
minetest.chat_send_player(name, "This command only works on mobkit mobs")
|
|
return
|
|
end
|
|
mob_core.set_owner(pointedobject, name)
|
|
minetest.chat_send_player(name, mob_name.." has been tamed!")
|
|
mobkit.clear_queue_high(pointedobject)
|
|
pos = pointedobject.object:get_pos()
|
|
minetest.add_particlespawner({
|
|
amount = 16,
|
|
time = 0.25,
|
|
minpos = {
|
|
x = pos.x - pointedobject.collisionbox[4],
|
|
y = pos.y - pointedobject.collisionbox[4],
|
|
z = pos.z - pointedobject.collisionbox[4],
|
|
},
|
|
maxpos = {
|
|
x = pos.x + pointedobject.collisionbox[4],
|
|
y = pos.y + pointedobject.collisionbox[4],
|
|
z = pos.z + pointedobject.collisionbox[4],
|
|
},
|
|
minacc = {x = 0, y = 0.25, z = 0},
|
|
maxacc = {x = 0, y = -0.25, z = 0},
|
|
minexptime = 0.75,
|
|
maxexptime = 1,
|
|
minsize = 4,
|
|
maxsize = 4,
|
|
texture = "mob_core_green_particle.png",
|
|
glow = 16,
|
|
})
|
|
return
|
|
else
|
|
minetest.chat_send_player(name, mob_name.." is already tamed.")
|
|
return
|
|
end
|
|
end
|
|
else
|
|
minetest.chat_send_player(name, "You must be pointing at a mob.")
|
|
return
|
|
end
|
|
end
|
|
end
|
|
})
|
|
|
|
]]--
|
|
|
|
-- Spawning --
|
|
|
|
function mob_core.get_biome_name(pos)
|
|
if not pos then return end
|
|
return minetest.get_biome_name(minetest.get_biome_data(pos).biome)
|
|
end
|
|
|
|
local find_node_height = 32
|
|
|
|
local block_protected_spawn = minetest.settings:get_bool("block_protected_spawn") or true
|
|
local mob_limit = tonumber(minetest.settings:get("mob_limit") or 6)
|
|
|
|
mob_core.spawn_enabled = true
|
|
|
|
function mob_core.spawn(name, nodes, min_light, max_light, min_height,
|
|
max_height, min_rad, max_rad, group, optional)
|
|
|
|
if not mob_core.spawn_enabled then return end
|
|
|
|
group = group or 1
|
|
|
|
--are we in the nether??
|
|
|
|
local netherian = {}
|
|
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
if player and player:is_player() and player:get_pos().y > 25000 then
|
|
table.insert(netherian,player)
|
|
end
|
|
end
|
|
|
|
if minetest.registered_entities[name] then
|
|
for _, player in ipairs(netherian) do
|
|
local mobs_amount = 0
|
|
--use this instead of 'return'
|
|
local allow_spawn_mob = true
|
|
|
|
|
|
for _, entity in pairs(minetest.luaentities) do
|
|
if entity.name == name then
|
|
local ent_pos = entity.object:get_pos()
|
|
if ent_pos and vector.distance(player:get_pos(), ent_pos) <=
|
|
max_rad then
|
|
mobs_amount = mobs_amount + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
local mob_spawned = false
|
|
|
|
local spawned_pos = nil
|
|
|
|
if mobs_amount >= mob_limit then allow_spawn_mob = false end
|
|
|
|
local reliability = 3
|
|
|
|
if optional and optional.reliability then
|
|
reliability = optional.reliability
|
|
end
|
|
|
|
for _ = 1, reliability do -- 3 attempts
|
|
local int = {-1, 1}
|
|
local pos = vector.floor(vector.add(player:get_pos(), 0.5))
|
|
|
|
local x, z
|
|
|
|
-- this is used to determine the axis buffer from the player
|
|
local axis = math.random(0, 1)
|
|
|
|
-- cast towards the direction
|
|
if axis == 0 then -- x
|
|
x = pos.x + math.random(min_rad, max_rad) *
|
|
int[random(1, 2)]
|
|
z = pos.z + math.random(-max_rad, max_rad)
|
|
else -- z
|
|
z = pos.z + math.random(min_rad, max_rad) *
|
|
int[random(1, 2)]
|
|
x = pos.x + math.random(-max_rad, max_rad)
|
|
end
|
|
|
|
local spawner = minetest.find_nodes_in_area_under_air(
|
|
vector.new(x - 1, pos.y - find_node_height,
|
|
z - 1), vector.new(x + 1,
|
|
pos.y +
|
|
find_node_height,
|
|
z + 1), nodes)
|
|
|
|
if table.getn(spawner) > 0 then
|
|
local mob_pos = spawner[1]
|
|
|
|
if block_protected_spawn and
|
|
minetest.is_protected(mob_pos, "") then
|
|
allow_spawn_mob = false
|
|
end
|
|
|
|
if optional then
|
|
if optional.biomes then
|
|
if not mob_core.find_val(optional.biomes,
|
|
mob_core.get_biome_name(pos)) then
|
|
allow_spawn_mob = false
|
|
end
|
|
end
|
|
end
|
|
|
|
if mob_pos.y > max_height or mob_pos.y < min_height then
|
|
allow_spawn_mob = false
|
|
end
|
|
|
|
local light = minetest.get_node_light(mob_pos)
|
|
if not light or light > max_light or light < min_light then
|
|
allow_spawn_mob = false
|
|
end
|
|
|
|
|
|
if allow_spawn_mob == true then
|
|
mob_spawned = true
|
|
|
|
spawned_pos = mob_pos
|
|
|
|
minetest.add_entity(mob_pos, name)
|
|
|
|
if group then
|
|
|
|
local spawned = 0
|
|
|
|
local attempts = 0
|
|
|
|
while spawned < group and attempts < group * 2 do
|
|
local mobdef = minetest.registered_entities[name]
|
|
local side = mobdef.collisionbox[4]
|
|
local group_pos =
|
|
vector.new(
|
|
mob_pos.x + (random(-group, group) * side),
|
|
mob_pos.y,
|
|
mob_pos.z + (random(-group, group) * side))
|
|
local spawn_pos =
|
|
minetest.find_nodes_in_area_under_air(
|
|
vector.new(group_pos.x, group_pos.y - 8,
|
|
group_pos.z), vector.new(
|
|
group_pos.x, group_pos.y + 8,
|
|
group_pos.z), nodes)
|
|
if spawn_pos[1] then
|
|
minetest.add_entity(
|
|
vector.new(spawn_pos[1].x, spawn_pos[1].y +
|
|
math.abs(
|
|
mobdef.collisionbox[2]),
|
|
spawn_pos[1].z), name)
|
|
spawned = spawned + 1
|
|
end
|
|
attempts = attempts + 1
|
|
end
|
|
end
|
|
end
|
|
break
|
|
end
|
|
end
|
|
--return mob_spawned, spawned_pos
|
|
--moved the execution of registered_on_spawns here
|
|
if mob_spawned == true and mob_core.registered_on_spawns[name] then
|
|
mob_core.registered_spawns[name].last_pos = spawned_pos
|
|
local on_spawn = mob_core.registered_on_spawns[name]
|
|
on_spawn.func(unpack(on_spawn.args))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
mob_core.registered_on_spawns = {}
|
|
|
|
mob_core.registered_spawns = {}
|
|
|
|
function mob_core.register_spawn(def, interval, chance)
|
|
local spawn_timer = 0
|
|
mob_core.registered_spawns[def.name] = {func = nil, last_pos = {}}
|
|
mob_core.registered_spawns[def.name].func =
|
|
minetest.register_globalstep(function(dtime)
|
|
spawn_timer = spawn_timer + dtime
|
|
if spawn_timer > interval then
|
|
if random(1, chance) == 1 then
|
|
-- local spawned, last_pos =
|
|
mob_core.spawn(
|
|
def.name,
|
|
def.nodes or {"group:soil", "group:stone"},
|
|
def.min_light or 0, def.max_light or 15,
|
|
def.min_height or -31000,
|
|
def.max_height or 31000,
|
|
def.min_rad or 24, def.max_rad or 256,
|
|
def.group or 1,
|
|
def.optional or nil
|
|
)
|
|
-- moved into mob_core.spawn()
|
|
-- if spawned and mob_core.registered_on_spawns[def.name] then
|
|
-- mob_core.registered_spawns[def.name].last_pos = last_pos
|
|
-- local on_spawn = mob_core.registered_on_spawns[def.name]
|
|
-- on_spawn.func(unpack(on_spawn.args))
|
|
-- end
|
|
end
|
|
spawn_timer = 0
|
|
end
|
|
end)
|
|
end
|
|
|
|
function mob_core.register_on_spawn(name, func, ...)
|
|
mob_core.registered_on_spawns[name] = {args = {...}, func = func}
|
|
end
|
|
|
|
--[[
|
|
|
|
-------------
|
|
-- On Step --
|
|
-------------
|
|
|
|
-- Push on entity collision --
|
|
|
|
function mob_core.collision_detection(self)
|
|
if not mobkit.is_alive(self) then return end
|
|
local pos = self.object:get_pos()
|
|
local hitbox = self.object:get_properties().collisionbox
|
|
local width = -hitbox[1] + hitbox[4] + 0.5
|
|
for _, object in ipairs(minetest.get_objects_inside_radius(pos, width)) do
|
|
if (object and object ~= self.object)
|
|
and (object:is_player() or (object:get_luaentity() and object:get_luaentity().logic))
|
|
and (not object:get_attach() or (object:get_attach() and object:get_attach() ~= self.object))
|
|
and (not self.object:get_attach() or (self.object:get_attach() and self.object:get_attach() ~= object)) then
|
|
local pos2 = object:get_pos()
|
|
local dir = vector.direction(pos,pos2)
|
|
dir.y = 0
|
|
if dir.x == 0 and dir.z == 0 then
|
|
dir = vector.new(math.random(-1,1)*math.random(),0,math.random(-1,1)*math.random())
|
|
end
|
|
local velocity = vector.multiply(dir,1.1)
|
|
local vel1 = vector.multiply(velocity, -1)
|
|
local vel2 = velocity
|
|
self.object:add_velocity(vel1)
|
|
if object:is_player() then
|
|
object:add_player_velocity(vel2)
|
|
else
|
|
object:add_velocity(vel2)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- 4 Stage Growth --
|
|
|
|
function mob_core.growth(self,interval)
|
|
if not mobkit.is_alive(self) then return end
|
|
if self.growth_stage == 4 then return end
|
|
if not self.base_hp then
|
|
self.base_hp = mobkit.remember(self,"base_hp",self.max_hp)
|
|
end
|
|
local pos = self.object:get_pos()
|
|
local reach = minetest.registered_entities[self.name].reach
|
|
local speed = minetest.registered_entities[self.name].max_speed
|
|
interval = interval or 400
|
|
if not self.scale_stage1 then
|
|
self.scale_stage1 = 0.25
|
|
self.scale_stage2 = 0.5
|
|
self.scale_stage3 = 0.75
|
|
end
|
|
if self.growth_stage < 4 then
|
|
self.growth_timer = self.growth_timer + 1
|
|
end
|
|
if self.growth_timer > interval then
|
|
self.growth_timer = 1
|
|
self.max_speed = speed/4
|
|
if reach then
|
|
self.reach = reach/4
|
|
end
|
|
if self.growth_stage == 1 then
|
|
self.growth_stage = mobkit.remember(self,"growth_stage",2)
|
|
self.object:set_pos({x=pos.x,y=pos.y+math.abs(self.collisionbox[2]),z=pos.z})
|
|
mob_core.set_scale(self, self.scale_stage2)
|
|
self.max_speed = speed/2
|
|
if reach then
|
|
self.reach = reach/2
|
|
end
|
|
elseif self.growth_stage == 2 then
|
|
self.growth_stage = mobkit.remember(self,"growth_stage",3)
|
|
self.object:set_pos({x=pos.x,y=pos.y+math.abs(self.collisionbox[2]),z=pos.z})
|
|
mob_core.set_scale(self, self.scale_stage3)
|
|
self.child = mobkit.remember(self,"child",false)
|
|
mob_core.set_textures(self)
|
|
self.max_speed = speed/1.5
|
|
if reach then
|
|
self.reach = reach/1.5
|
|
end
|
|
elseif self.growth_stage == 3 then
|
|
self.growth_stage = mobkit.remember(self,"growth_stage",4)
|
|
self.object:set_pos({x=pos.x,y=pos.y+math.abs(self.collisionbox[2]),z=pos.z})
|
|
mob_core.set_scale(self, 1)
|
|
self.max_speed = speed
|
|
if reach then
|
|
self.reach = reach
|
|
end
|
|
end
|
|
end
|
|
if self.growth_stage == 1
|
|
and self.hp > self.max_hp/4 then
|
|
self.hp = self.max_hp/4
|
|
end
|
|
if self.growth_stage == 2
|
|
and self.hp > self.max_hp/2 then
|
|
self.hp = self.max_hp/2
|
|
end
|
|
if self.growth_stage == 3
|
|
and self.hp > self.max_hp/1.5 then
|
|
self.hp = self.max_hp/1.5
|
|
end
|
|
self.growth_timer = mobkit.remember(self,"growth_timer",self.growth_timer)
|
|
end
|
|
|
|
-- Breeding --
|
|
|
|
function mob_core.breed(self)
|
|
if not mobkit.is_alive(self) then return end
|
|
if self.breed_timer > 0 then
|
|
self.breed_timer = self.breed_timer - self.dtime
|
|
else
|
|
self.breed_timer = 0
|
|
end
|
|
if self.gender == "female" then
|
|
local pos = self.object:get_pos()
|
|
local objs = minetest.get_objects_inside_radius(pos, self.collisionbox[4]*4)
|
|
for i = 1, #objs do
|
|
local luaent = objs[i]:get_luaentity()
|
|
if luaent and luaent.name == self.name then
|
|
if luaent.breed_mode == true and luaent.gender == "male" then
|
|
self.breed_mode = false
|
|
self.breed_timer = 300
|
|
luaent.breed_mode = false
|
|
luaent.breed_timer = 300
|
|
minetest.after(2.5,function()
|
|
mob_core.spawn_child(pos,self.name)
|
|
minetest.add_particlespawner({
|
|
amount = 16,
|
|
time = 0.25,
|
|
minpos = {
|
|
x = pos.x - self.collisionbox[4],
|
|
y = pos.y - self.collisionbox[4],
|
|
z = pos.z - self.collisionbox[4],
|
|
},
|
|
maxpos = {
|
|
x = pos.x + self.collisionbox[4],
|
|
y = pos.y + self.collisionbox[4],
|
|
z = pos.z + self.collisionbox[4],
|
|
},
|
|
minacc = {x = 0, y = 0.25, z = 0},
|
|
maxacc = {x = 0, y = -0.25, z = 0},
|
|
minexptime = 0.75,
|
|
maxexptime = 1,
|
|
minsize = 4,
|
|
maxsize = 4,
|
|
texture = "heart.png",
|
|
glow = 16,
|
|
})
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Step Function --
|
|
|
|
function mob_core.on_step(self, dtime, moveresult)
|
|
mobkit.stepfunc(self, dtime)
|
|
self.moveresult = moveresult
|
|
if self.owner_target
|
|
and not mobkit.exists(self.owner_target) then
|
|
self.owner_target = nil
|
|
end
|
|
if self.custom_punch_target
|
|
and not mobkit.exists(self.custom_punch_target) then
|
|
self.custom_punch_target = nil
|
|
end
|
|
|
|
if self.core_growth then
|
|
mob_core.growth(self)
|
|
end
|
|
if self.core_breeding then
|
|
mob_core.breed(self)
|
|
end
|
|
if self.push_on_collide then
|
|
mob_core.collision_detection(self)
|
|
end
|
|
end
|
|
|
|
|
|
--------------------------
|
|
-- Rightclick Functions --
|
|
--------------------------
|
|
|
|
-- Mount --
|
|
|
|
function mob_core.mount(self, clicker)
|
|
if not self.driver and self.child == false then
|
|
mobkit.clear_queue_high(self)
|
|
self.status = mobkit.remember(self,"status","ridden")
|
|
mob_core.attach(self, clicker)
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- Capture Mob --
|
|
|
|
function mob_core.capture_mob(self, clicker, capture_tool, capture_chance, wear, force_take)
|
|
if not clicker:is_player()
|
|
or not clicker:get_inventory() then
|
|
return false
|
|
end
|
|
local mobname = self.name
|
|
local catcher = clicker:get_player_name()
|
|
local tool = clicker:get_wielded_item()
|
|
if tool:get_name() ~= capture_tool then
|
|
return false
|
|
end
|
|
if self.tamed == false then
|
|
minetest.chat_send_player(catcher, "Mob is not tamed.")
|
|
return false
|
|
end
|
|
if self.owner ~= catcher
|
|
and force_take == false then
|
|
minetest.chat_send_player(catcher, "Mob is owned by @1"..self.owner)
|
|
return false
|
|
end
|
|
if clicker:get_inventory():room_for_item("main", mobname) then
|
|
local chance = 0
|
|
if tool:get_name() == capture_tool then
|
|
if capture_tool == "" then
|
|
chance = capture_chance
|
|
else
|
|
chance = capture_chance
|
|
tool:add_wear(wear)
|
|
clicker:set_wielded_item(tool)
|
|
end
|
|
end
|
|
if chance and chance > 0 and random(1, 100) <= chance then
|
|
local new_stack = ItemStack(mobname .. "_set")
|
|
new_stack:get_meta():set_string("staticdata", self:get_staticdata())
|
|
local inv = clicker:get_inventory()
|
|
if inv:room_for_item("main", new_stack) then
|
|
inv:add_item("main", new_stack)
|
|
else
|
|
minetest.add_item(clicker:get_pos(), new_stack)
|
|
end
|
|
self.object:remove()
|
|
return new_stack
|
|
elseif chance and chance ~= 0 then
|
|
return false
|
|
elseif not chance then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- Feed/Tame/Breed --
|
|
|
|
function mob_core.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)
|
|
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)
|
|
minetest.add_particlespawner({
|
|
amount = 16,
|
|
time = 0.25,
|
|
minpos = {
|
|
x = pos.x - self.collisionbox[4],
|
|
y = pos.y - self.collisionbox[4],
|
|
z = pos.z - self.collisionbox[4],
|
|
},
|
|
maxpos = {
|
|
x = pos.x + self.collisionbox[4],
|
|
y = pos.y + self.collisionbox[4],
|
|
z = pos.z + self.collisionbox[4],
|
|
},
|
|
minacc = {x = 0, y = 0.25, z = 0},
|
|
maxacc = {x = 0, y = -0.25, z = 0},
|
|
minexptime = 0.75,
|
|
maxexptime = 1,
|
|
minsize = 4,
|
|
maxsize = 4,
|
|
texture = "mob_core_green_particle.png",
|
|
glow = 16,
|
|
})
|
|
end
|
|
if breed then
|
|
if self.child then return false end
|
|
if self.breed_mode then return false end
|
|
if self.breed_timer == 0 and self.breed_mode == false then
|
|
self.breed_mode = true
|
|
minetest.add_particlespawner({
|
|
amount = 16,
|
|
time = 0.25,
|
|
minpos = {
|
|
x = pos.x - self.collisionbox[4],
|
|
y = pos.y - self.collisionbox[4],
|
|
z = pos.z - self.collisionbox[4],
|
|
},
|
|
maxpos = {
|
|
x = pos.x + self.collisionbox[4],
|
|
y = pos.y + self.collisionbox[4],
|
|
z = pos.z + self.collisionbox[4],
|
|
},
|
|
minacc = {x = 0, y = 0.25, z = 0},
|
|
maxacc = {x = 0, y = -0.25, z = 0},
|
|
minexptime = 0.75,
|
|
maxexptime = 1,
|
|
minsize = 4,
|
|
maxsize = 4,
|
|
texture = "heart.png",
|
|
glow = 16,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Protection --
|
|
|
|
function mob_core.protect(self, clicker, force_protect)
|
|
local name = clicker:get_player_name()
|
|
local item = clicker:get_wielded_item()
|
|
local mob_name = mob_core.get_name_proper(self.name)
|
|
if item:get_name() ~= "mob_core:protection_gem" then
|
|
return false
|
|
end
|
|
if self.tamed == false and not force_protect then
|
|
minetest.chat_send_player(name, mob_name.." is not tamed")
|
|
return true
|
|
end
|
|
if self.protected == true then
|
|
minetest.chat_send_player(name, mob_name.." is already protected")
|
|
return true
|
|
end
|
|
if not creative then
|
|
item:take_item()
|
|
clicker:set_wielded_item(item)
|
|
end
|
|
self.protected = true
|
|
mobkit.remember(self, "protected", self.protected)
|
|
self.timeout = nil
|
|
local pos = self.object:get_pos()
|
|
pos.y = pos.y + self.collisionbox[2] + 1/1
|
|
minetest.add_particlespawner({
|
|
amount = 16,
|
|
time = 0.25,
|
|
minpos = {
|
|
x = pos.x - self.collisionbox[4],
|
|
y = pos.y - self.collisionbox[4],
|
|
z = pos.z - self.collisionbox[4],
|
|
},
|
|
maxpos = {
|
|
x = pos.x + self.collisionbox[4],
|
|
y = pos.y + self.collisionbox[4],
|
|
z = pos.z + self.collisionbox[4],
|
|
},
|
|
minacc = {x = 0, y = 0.25, z = 0},
|
|
maxacc = {x = 0, y = -0.25, z = 0},
|
|
minexptime = 0.75,
|
|
maxexptime = 1,
|
|
minsize = 4,
|
|
maxsize = 4,
|
|
texture = "mob_core_green_particle.png",
|
|
glow = 16,
|
|
})
|
|
return true
|
|
end
|
|
|
|
-- Set Nametag --
|
|
|
|
local nametag_obj = {}
|
|
local nametag_item = {}
|
|
|
|
function mob_core.nametag(self, clicker, force_name)
|
|
if not force_name
|
|
and clicker:get_player_name() ~= self.owner then
|
|
return
|
|
end
|
|
local item = clicker:get_wielded_item()
|
|
if item:get_name() == "mob_core:nametag" then
|
|
|
|
local name = clicker:get_player_name()
|
|
|
|
nametag_obj[name] = self
|
|
nametag_item[name] = item
|
|
|
|
local tag = self.nametag or ""
|
|
|
|
minetest.show_formspec(name, "mob_core_nametag", "size[8,4]"
|
|
.. "field[0.5,1;7.5,0;name;"
|
|
.. minetest.formspec_escape("Enter name:") .. ";" .. tag .. "]"
|
|
.. "button_exit[2.5,3.5;3,1;mob_rename;"
|
|
.. minetest.formspec_escape("Rename") .. "]")
|
|
end
|
|
end
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
|
|
if formname == "mob_core_nametag"
|
|
and fields.name then
|
|
|
|
local name = player:get_player_name()
|
|
|
|
if not nametag_obj[name]
|
|
or not nametag_obj[name].object then
|
|
return
|
|
end
|
|
|
|
local item = player:get_wielded_item()
|
|
|
|
if item:get_name() ~= "mob_core:nametag" then
|
|
return
|
|
end
|
|
|
|
if string.len(fields.name) > 64 then
|
|
fields.name = string.sub(fields.name, 1, 64)
|
|
end
|
|
|
|
nametag_obj[name].nametag = mobkit.remember(nametag_obj[name], "nametag", fields.name)
|
|
|
|
mob_core.activate_nametag(nametag_obj[name])
|
|
|
|
if fields.name ~= ""
|
|
and not creative then
|
|
nametag_item[name]:take_item()
|
|
player:set_wielded_item(nametag_item[name])
|
|
end
|
|
|
|
nametag_obj[name] = nil
|
|
nametag_item[name] = nil
|
|
end
|
|
end)
|
|
|
|
-----------------
|
|
-- Pathfinding --
|
|
-----------------
|
|
|
|
-----------------
|
|
-- Pathfinding --
|
|
-----------------
|
|
|
|
local function can_fit(pos, width, single_plane)
|
|
height = height or 0
|
|
local pos1 = vector.new(pos.x - width, pos.y, pos.z - width)
|
|
local pos2 = vector.new(pos.x + width, pos.y + height, 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
|
|
if single_plane 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)
|
|
and vector.distance(pos, p2) < width then
|
|
return p2
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return pos
|
|
end
|
|
|
|
function mob_core.find_path_lite(pos, tpos, width)
|
|
|
|
local raw
|
|
|
|
if not minetest.registered_nodes[minetest.get_node(
|
|
vector.new(pos.x, pos.y - 1, pos.z))
|
|
.name].walkable then
|
|
local min = vector.subtract(pos, width+1)
|
|
local max = vector.add(pos, width+1)
|
|
|
|
local index_table = minetest.find_nodes_in_area_under_air( min, max, mob_core.walkable_nodes)
|
|
for _, i_pos in pairs(index_table) do
|
|
if minetest.registered_nodes[minetest.get_node(i_pos)
|
|
.name].walkable then
|
|
pos = vector.new(i_pos.x, i_pos.y + 1, i_pos.z)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not minetest.registered_nodes[minetest.get_node(
|
|
vector.new(tpos.x, tpos.y - 1, tpos.z))
|
|
.name].walkable then
|
|
local min = vector.subtract(tpos, width)
|
|
local max = vector.add(tpos, width)
|
|
|
|
local index_table = minetest.find_nodes_in_area_under_air( min, max, mob_core.walkable_nodes)
|
|
for _, i_pos in pairs(index_table) do
|
|
if minetest.registered_nodes[minetest.get_node(i_pos)
|
|
.name].walkable then
|
|
tpos = vector.new(i_pos.x, i_pos.y + 1, i_pos.z)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
local path = minetest.find_path(pos, tpos, 32, 2, 2, "A*_noprefetch")
|
|
|
|
if not path then return end
|
|
|
|
table.remove(path, 1)
|
|
|
|
for i = #path, 1, -1 do
|
|
if not path then return end
|
|
if vector.distance(pos, path[i]) <= width + 1 then
|
|
for i = 3, #path do
|
|
path[i - 1] = path[i]
|
|
end
|
|
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 minetest.get_node(path[i]).name == "default:snow" then
|
|
path[i] = vector.new(path[i].x, path[i].y + 1, path[i].z)
|
|
end
|
|
|
|
|
|
raw = path
|
|
if #path > 3 then
|
|
|
|
if vector.distance(pos, path[i]) < width then
|
|
table.remove(path, i)
|
|
end
|
|
|
|
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
|
|
|
|
return path, raw
|
|
end
|
|
|
|
function mob_core.find_path(self, tpos)
|
|
|
|
if not mobkit.is_alive(self) then return end
|
|
|
|
local pos = mobkit.get_stand_pos(self)
|
|
|
|
local pos_above = function(v) return vector.new(v.x, v.y + 1, v.z) end
|
|
|
|
local pos_under = function(v) return vector.new(v.x, v.y - 1, v.z) end
|
|
|
|
local width = self.object:get_properties().collisionbox[4] + 1
|
|
|
|
if not minetest.registered_nodes[minetest.get_node(pos_under(pos)).name].walkable then
|
|
|
|
local min = vector.subtract(pos, width + 1)
|
|
local max = vector.add(pos, width + 1)
|
|
|
|
local index_table = minetest.find_nodes_in_area_under_air(min, max, mob_core.walkable_nodes)
|
|
for _, i_pos in pairs(index_table) do
|
|
if can_fit(i_pos, width) then
|
|
pos = vector.new(i_pos.x, i_pos.y + 0.6, i_pos.z)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not minetest.registered_nodes[minetest.get_node(pos_under(tpos)).name].walkable then
|
|
local min = vector.subtract(tpos, width)
|
|
local max = vector.add(tpos, width)
|
|
|
|
local index_table = minetest.find_nodes_in_area_under_air(min, max, mob_core.walkable_nodes)
|
|
for _, i_pos in pairs(index_table) do
|
|
if minetest.registered_nodes[minetest.get_node(i_pos).name].walkable then
|
|
tpos = vector.new(i_pos.x, i_pos.y + 1, i_pos.z)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
local path = pathfinder.find_path(self, pos, tpos, 64, self.dtime)
|
|
|
|
if not path then return end
|
|
|
|
for i = #path, 1, -1 do
|
|
|
|
local under = minetest.get_node(pos_under(path[i]))
|
|
|
|
if minetest.registered_nodes[under.name]
|
|
and not minetest.registered_nodes[under.name].walkable then
|
|
table.remove(path, i)
|
|
end
|
|
|
|
local above = pos_above(path[i])
|
|
|
|
if not can_fit(path[i], width, true) then
|
|
local clear = move_from_wall(path[i], width + 1)
|
|
if clear and can_fit(clear, width)
|
|
and (path[i + 1]
|
|
and path[i].y <= path[i + 1].y) then
|
|
path[i] = clear
|
|
end
|
|
end
|
|
|
|
if vector.distance(pos, path[i]) <= width then
|
|
table.remove(path, i)
|
|
end
|
|
end
|
|
|
|
return path
|
|
end
|
|
|
|
]]-- |