1
0
mirror of https://codeberg.org/tenplus1/mobs_redo.git synced 2025-07-19 16:50:25 +02:00

Compare commits

...

16 Commits

9 changed files with 272 additions and 160 deletions

181
api.lua
View File

@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi")
mobs = { mobs = {
mod = "redo", mod = "redo",
version = "20201029", version = "20210114",
intllib = S, intllib = S,
invis = minetest.global_exists("invisibility") and invisibility or {} invis = minetest.global_exists("invisibility") and invisibility or {}
} }
@ -58,12 +58,14 @@ local mobs_drop_items = settings:get_bool("mobs_drop_items") ~= false
local mobs_griefing = settings:get_bool("mobs_griefing") ~= false local mobs_griefing = settings:get_bool("mobs_griefing") ~= false
local spawn_protected = settings:get_bool("mobs_spawn_protected") ~= false local spawn_protected = settings:get_bool("mobs_spawn_protected") ~= false
local remove_far = settings:get_bool("remove_far_mobs") ~= false local remove_far = settings:get_bool("remove_far_mobs") ~= false
local mob_area_spawn = settings:get_bool("mob_area_spawn")
local difficulty = tonumber(settings:get("mob_difficulty")) or 1.0 local difficulty = tonumber(settings:get("mob_difficulty")) or 1.0
local show_health = settings:get_bool("mob_show_health") ~= false local show_health = settings:get_bool("mob_show_health") ~= false
local max_per_block = tonumber(settings:get("max_objects_per_block") or 99) local max_per_block = tonumber(settings:get("max_objects_per_block") or 99)
local mob_nospawn_range = tonumber(settings:get("mob_nospawn_range") or 12) local mob_nospawn_range = tonumber(settings:get("mob_nospawn_range") or 12)
local active_limit = tonumber(settings:get("mob_active_limit") or 0) local active_limit = tonumber(settings:get("mob_active_limit") or 0)
local mob_chance_multiplier = tonumber(settings:get("mob_chance_multiplier") or 1) local mob_chance_multiplier = tonumber(settings:get("mob_chance_multiplier") or 1)
local peaceful_player_enabled = settings:get_bool("enable_peaceful_player")
local active_mobs = 0 local active_mobs = 0
@ -111,6 +113,7 @@ local mob_class = {
light_damage_max = 15, light_damage_max = 15,
water_damage = 0, water_damage = 0,
lava_damage = 0, lava_damage = 0,
air_damage = 0,
suffocation = 2, suffocation = 2,
fall_damage = 1, fall_damage = 1,
fall_speed = -10, -- must be lower than -2 (default: -10) fall_speed = -10, -- must be lower than -2 (default: -10)
@ -276,10 +279,10 @@ function mob_class:set_velocity(v)
c_x, c_y = unpack(self:collision()) c_x, c_y = unpack(self:collision())
end end
local yaw = (self.object:get_yaw() or 0) + self.rotate local yaw = (self.object:get_yaw() or 0) + (self.rotate or 0)
-- nil check for velocity -- nil check for velocity
v = v or 0 v = v or 0.01
-- check if standing in liquid with max viscosity of 7 -- check if standing in liquid with max viscosity of 7
local visc = min(minetest.registered_nodes[self.standing_in].liquid_viscosity, 7) local visc = min(minetest.registered_nodes[self.standing_in].liquid_viscosity, 7)
@ -291,8 +294,8 @@ function mob_class:set_velocity(v)
v = v / (visc + 1) v = v / (visc + 1)
end end
-- set velocity with hard limit of 10 -- set velocity
local vel = self.object:get_velocity() local vel = self.object:get_velocity() or 0
local new_vel = { local new_vel = {
x = (sin(yaw) * -v) + c_x, x = (sin(yaw) * -v) + c_x,
@ -560,7 +563,7 @@ function mob_class:attempt_flight_correction(override)
-- We are not flying in what we are supposed to. -- We are not flying in what we are supposed to.
-- See if we can find intended flight medium and return to it -- See if we can find intended flight medium and return to it
local pos = self.object:get_pos() local pos = self.object:get_pos() ; if not pos then return true end
local searchnodes = self.fly_in local searchnodes = self.fly_in
if type(searchnodes) == "string" then if type(searchnodes) == "string" then
@ -569,7 +572,8 @@ function mob_class:attempt_flight_correction(override)
local flyable_nodes = minetest.find_nodes_in_area( local flyable_nodes = minetest.find_nodes_in_area(
{x = pos.x - 1, y = pos.y - 1, z = pos.z - 1}, {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
{x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, searchnodes) {x = pos.x + 1, y = pos.y + 0, z = pos.z + 1}, searchnodes)
-- pos.y + 0 hopefully fixes floating swimmers
if #flyable_nodes < 1 then if #flyable_nodes < 1 then
return false return false
@ -828,6 +832,11 @@ end
-- check if mob is dead or only hurt -- check if mob is dead or only hurt
function mob_class:check_for_death(cmi_cause) function mob_class:check_for_death(cmi_cause)
-- We dead already
if self.state == "die" then
return true
end
-- has health actually changed? -- has health actually changed?
if self.health == self.old_health and self.health > 0 then if self.health == self.old_health and self.health > 0 then
return false return false
@ -877,7 +886,7 @@ function mob_class:check_for_death(cmi_cause)
local pos = self.object:get_pos() local pos = self.object:get_pos()
-- execute custom death function -- execute custom death function
if self.on_die then if pos and self.on_die then
self:on_die(pos) self:on_die(pos)
@ -897,27 +906,26 @@ function mob_class:check_for_death(cmi_cause)
local frames = self.animation.die_end - self.animation.die_start local frames = self.animation.die_end - self.animation.die_start
local speed = self.animation.die_speed or 15 local speed = self.animation.die_speed or 15
local length = max(frames / speed, 0) local length = max((frames / speed), 0)
local rot = self.animation.die_rotate and 5
self.attack = nil self.attack = nil
self.following = nil
self.v_start = false self.v_start = false
self.timer = 0 self.timer = 0
self.blinktimer = 0 self.blinktimer = 0
self.passive = true self.passive = true
self.state = "die" self.state = "die"
self.object:set_properties({
pointable = false, collide_with_objects = false,
automatic_rotate = rot, static_save = false
})
self:set_velocity(0) self:set_velocity(0)
self:set_animation("die") self:set_animation("die")
minetest.after(length, function(self) minetest.after(length, function(self)
if use_cmi and self.object:get_luaentity() then if self.object:get_luaentity() then
cmi.notify_die(self.object, cmi_cause)
end
remove_mob(self, true)
end, self)
else
if use_cmi then if use_cmi then
cmi.notify_die(self.object, cmi_cause) cmi.notify_die(self.object, cmi_cause)
@ -925,8 +933,20 @@ function mob_class:check_for_death(cmi_cause)
remove_mob(self, true) remove_mob(self, true)
end end
end, self)
return true
elseif pos then -- otherwise remove mod and show particle effect
if use_cmi then
cmi.notify_die(self.object, cmi_cause)
end
remove_mob(self, true)
effect(pos, 20, "tnt_smoke.png") effect(pos, 20, "tnt_smoke.png")
end
return true return true
end end
@ -1084,6 +1104,19 @@ function mob_class:do_env_damage()
end end
end end
-- air damage
if self.air_damage ~= 0 and self.standing_in == "air" then
self.health = self.health - self.air_damage
effect(pos, 3, "bubble.png", 1, 1, 1, 0.2)
if self:check_for_death({type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end
-- is mob light sensative, or scared of the dark :P -- is mob light sensative, or scared of the dark :P
if self.light_damage ~= 0 then if self.light_damage ~= 0 then
@ -1290,16 +1323,20 @@ function mob_class:follow_holding(clicker)
return false return false
end end
-- Thanks Wuzzy for the following editable settings
local HORNY_TIME = 30
local HORNY_AGAIN_TIME = 300
local CHILD_GROW_TIME = 60 * 20 -- 20 minutes
-- find two animals of same type and breed if nearby and horny -- find two animals of same type and breed if nearby and horny
function mob_class:breed() function mob_class:breed()
-- child takes 240 seconds before growing into adult -- child takes a long time before growing into adult
if self.child == true then if self.child == true then
self.hornytimer = self.hornytimer + 1 self.hornytimer = self.hornytimer + 1
if self.hornytimer > 240 then if self.hornytimer > CHILD_GROW_TIME then
self.child = false self.child = false
self.hornytimer = 0 self.hornytimer = 0
@ -1328,14 +1365,14 @@ function mob_class:breed()
return return
end end
-- horny animal can mate for 40 seconds, -- horny animal can mate for HORNY_TIME seconds,
-- afterwards horny animal cannot mate again for 200 seconds -- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
if self.horny == true if self.horny == true
and self.hornytimer < 240 then and self.hornytimer < HORNY_TIME + HORNY_AGAIN_TIME then
self.hornytimer = self.hornytimer + 1 self.hornytimer = self.hornytimer + 1
if self.hornytimer >= 240 then if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
self.hornytimer = 0 self.hornytimer = 0
self.horny = false self.horny = false
end end
@ -1343,7 +1380,7 @@ function mob_class:breed()
-- find another same animal who is also horny and mate if nearby -- find another same animal who is also horny and mate if nearby
if self.horny == true if self.horny == true
and self.hornytimer <= 40 then and self.hornytimer <= HORNY_TIME then
local pos = self.object:get_pos() local pos = self.object:get_pos()
@ -1351,7 +1388,6 @@ function mob_class:breed()
"heart.png", 3, 4, 1, 0.1) "heart.png", 3, 4, 1, 0.1)
local objs = minetest.get_objects_inside_radius(pos, 3) local objs = minetest.get_objects_inside_radius(pos, 3)
local num = 0
local ent local ent
for n = 1, #objs do for n = 1, #objs do
@ -1380,18 +1416,20 @@ function mob_class:breed()
end end
end end
if ent -- found another similar horny animal that isn't self?
if ent and ent.object ~= self.object
and canmate == true and canmate == true
and ent.horny == true and ent.horny == true
and ent.hornytimer <= 40 then and ent.hornytimer <= HORNY_TIME then
num = num + 1
end
-- found your mate? then have a baby local pos2 = ent.object:get_pos()
if num > 1 then
self.hornytimer = 41 -- Have mobs face one another
ent.hornytimer = 41 yaw_to_pos(self, pos2)
yaw_to_pos(ent, self.object:get_pos())
self.hornytimer = HORNY_TIME + 1
ent.hornytimer = HORNY_TIME + 1
-- have we reached active mob limit -- have we reached active mob limit
if active_limit > 0 and active_mobs >= active_limit then if active_limit > 0 and active_mobs >= active_limit then
@ -1459,8 +1497,6 @@ function mob_class:breed()
ent2.owner = self.owner ent2.owner = self.owner
end, self, ent) end, self, ent)
num = 0
break break
end end
end end
@ -1815,6 +1851,23 @@ function mob_class:smart_mobs(s, p, dist, dtime)
end end
-- peaceful player privilege support
local function is_peaceful_player(player)
if peaceful_player_enabled then
local player_name = player:get_player_name()
if player_name
and minetest.check_player_privs(player_name, "peaceful_player") then
return true
end
end
return false
end
-- general attack function for all mobs -- general attack function for all mobs
function mob_class:general_attack() function mob_class:general_attack()
@ -1887,7 +1940,8 @@ function mob_class:general_attack()
-- choose closest player to attack that isnt self -- choose closest player to attack that isnt self
if dist ~= 0 if dist ~= 0
and dist < min_dist and dist < min_dist
and self:line_of_sight(sp, p, 2) == true then and self:line_of_sight(sp, p, 2) == true
and not is_peaceful_player(player) then
min_dist = dist min_dist = dist
min_player = player min_player = player
end end
@ -2005,10 +2059,11 @@ function mob_class:follow_flop()
self.following = nil self.following = nil
end end
else else
-- stop following player if not holding specific item -- stop following player if not holding specific item or mob is horny
if self.following if self.following
and self.following:is_player() and self.following:is_player()
and self:follow_holding(self.following) == false then and (self:follow_holding(self.following) == false
or self.horny) then
self.following = nil self.following = nil
end end
@ -2064,6 +2119,15 @@ function mob_class:follow_flop()
if not self:attempt_flight_correction() then if not self:attempt_flight_correction() then
self.state = "flop" self.state = "flop"
-- do we have a custom on_flop function?
if self.on_flop then
if self:on_flop(self) then
return
end
end
self.object:set_velocity({x = 0, y = -5, z = 0}) self.object:set_velocity({x = 0, y = -5, z = 0})
self:set_animation("stand") self:set_animation("stand")
@ -3011,6 +3075,7 @@ function mob_class:mob_staticdata()
if t ~= "function" if t ~= "function"
and t ~= "nil" and t ~= "nil"
and t ~= "userdata" and t ~= "userdata"
and _ ~= "object"
and _ ~= "_cmi_components" then and _ ~= "_cmi_components" then
tmp[_] = self[_] tmp[_] = self[_]
end end
@ -3263,6 +3328,8 @@ function mob_class:on_step(dtime, moveresult)
}} }}
}]] }]]
if self.state == "die" then return end ----------------
if use_cmi then if use_cmi then
cmi.notify_step(self.object, dtime) cmi.notify_step(self.object, dtime)
end end
@ -3462,6 +3529,7 @@ minetest.register_entity(name, setmetatable({
owner = def.owner, owner = def.owner,
order = def.order, order = def.order,
on_die = def.on_die, on_die = def.on_die,
on_flop = def.on_flop,
do_custom = def.do_custom, do_custom = def.do_custom,
jump_height = def.jump_height, jump_height = def.jump_height,
drawtype = def.drawtype, -- DEPRECATED, use rotate instead drawtype = def.drawtype, -- DEPRECATED, use rotate instead
@ -3485,6 +3553,7 @@ minetest.register_entity(name, setmetatable({
light_damage_max = def.light_damage_max, light_damage_max = def.light_damage_max,
water_damage = def.water_damage, water_damage = def.water_damage,
lava_damage = def.lava_damage, lava_damage = def.lava_damage,
air_damage = def.air_damage,
suffocation = def.suffocation, suffocation = def.suffocation,
fall_damage = def.fall_damage, fall_damage = def.fall_damage,
fall_speed = def.fall_speed, fall_speed = def.fall_speed,
@ -3636,9 +3705,6 @@ local can_spawn = function(pos, name)
end end
end end
-- spawn mob 1/2 node above ground
pos.y = pos.y + 0.5
-- tweak X/Z spawn pos -- tweak X/Z spawn pos
if width_x % 2 == 0 then if width_x % 2 == 0 then
pos.x = pos.x + 0.5 pos.x = pos.x + 0.5
@ -3903,11 +3969,33 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter
end end
end end
local ent = minetest.registered_entities[name]
-- should we check mob area for obstructions ?
if mob_area_spawn ~= true then
-- do we have enough height clearance to spawn mob?
local height = max(0, ent.collisionbox[5] - ent.collisionbox[2])
for n = 0, floor(height) do
local pos2 = {x = pos.x, y = pos.y + n, z = pos.z}
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
--print ("--- inside block", name, node_ok(pos2).name)
return
end
end
else
-- returns position if we have enough space to spawn mob -- returns position if we have enough space to spawn mob
pos = can_spawn(pos, name) pos = can_spawn(pos, name)
end
if pos then if pos then
-- adjust for mob collision box
pos.y = pos.y + (ent.collisionbox[2] * -1) - 0.4
local mob = minetest.add_entity(pos, name) local mob = minetest.add_entity(pos, name)
-- print("[mobs] Spawned " .. name .. " at " -- print("[mobs] Spawned " .. name .. " at "
@ -4439,10 +4527,10 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net,
minetest.add_item(clicker:get_pos(), new_stack) minetest.add_item(clicker:get_pos(), new_stack)
end end
remove_mob(self, true)
self:mob_sound("default_place_node_hard") self:mob_sound("default_place_node_hard")
remove_mob(self, true)
return new_stack return new_stack
-- when chance above fails or set to 0, miss! -- when chance above fails or set to 0, miss!
@ -4547,8 +4635,11 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
-- make children grow quicker -- make children grow quicker
if self.child == true then if self.child == true then
self.hornytimer = self.hornytimer + 20 -- self.hornytimer = self.hornytimer + 20
-- deduct 10% of the time to adulthood
self.hornytimer = self.hornytimer + (
(CHILD_GROW_TIME - self.hornytimer) * 0.1)
print ("====", self.hornytimer)
return true return true
end end

16
api.txt
View File

@ -64,6 +64,7 @@ functions needed for the mob to work properly which contains the following:
'fall_damage' when true causes falling to inflict damage. 'fall_damage' when true causes falling to inflict damage.
'water_damage' holds the damage per second infliced to mobs when standing in 'water_damage' holds the damage per second infliced to mobs when standing in
water. water.
'air_damage' holds damage per second inflicted to mob when standing in air.
'lava_damage' holds the damage per second inflicted to mobs when standing 'lava_damage' holds the damage per second inflicted to mobs when standing
in lava or fire or an ignition source. in lava or fire or an ignition source.
'light_damage' holds the damage per second inflicted to mobs when light 'light_damage' holds the damage per second inflicted to mobs when light
@ -226,6 +227,7 @@ functions needed for the mob to work properly which contains the following:
'die_end' 'die_end'
'die_speed' 'die_speed'
'die_loop' when set to false stops the animation looping. 'die_loop' when set to false stops the animation looping.
'die_rotate' if true mob spins during death animation.
Using '_loop = false' setting will stop any of the above animations from Using '_loop = false' setting will stop any of the above animations from
looping. looping.
@ -298,6 +300,9 @@ enhance mob functionality and have them do many interesting things:
is returned normal attack function continued. is returned normal attack function continued.
'on_die' a function that is called when mob is killed (self, pos), also 'on_die' a function that is called when mob is killed (self, pos), also
has access to self.cause_of_death table. has access to self.cause_of_death table.
'on_flop' function called when flying or swimmimng mob is no longer in
air/water, (self) paramater and return true to skip the built
in api flop feature.
'do_custom' a custom function that is called every tick while mob is 'do_custom' a custom function that is called every tick while mob is
active and which has access to all of the self.* variables active and which has access to all of the self.* variables
e.g. (self.health for health or self.standing_in for node e.g. (self.health for health or self.standing_in for node
@ -329,6 +334,14 @@ for each mob.
'self.order' set to "follow" or "stand" so that npc will follow owner 'self.order' set to "follow" or "stand" so that npc will follow owner
or stand it's ground or stand it's ground
'self.nametag' contains the name of the mob which it can show above 'self.nametag' contains the name of the mob which it can show above
'self.state' Current mob state.
"stand": no movement (except turning around)
"walk": walk or move around aimlessly
"attack": chase and attack enemy
"runaway": flee from target
"flop": bounce around aimlessly
(for swimming mobs that have stranded)
"die": during death
Adding Mobs in World Adding Mobs in World
@ -697,6 +710,9 @@ External Settings for "minetest.conf"
function. function.
'mob_nospawn_range' Minimum range a mob can spawn near player (def: 12) 'mob_nospawn_range' Minimum range a mob can spawn near player (def: 12)
'mob_active_limit' Number of active mobs in game, 0 for unlimited 'mob_active_limit' Number of active mobs in game, 0 for unlimited
'mob_area_spawn' When true will check surrounding area the size of the
mob for obstructions before spawning, otherwise it
defaults to checking the height of the mob only.
Players can override the spawn chance for each mob registered by adding a line Players can override the spawn chance for each mob registered by adding a line
to their minetest.conf file with a new value, the lower the value the more each to their minetest.conf file with a new value, the lower the value the more each

View File

@ -5,7 +5,7 @@ local S = mobs.intllib
minetest.register_craftitem("mobs:nametag", { minetest.register_craftitem("mobs:nametag", {
description = S("Name Tag"), description = S("Name Tag"),
inventory_image = "mobs_nametag.png", inventory_image = "mobs_nametag.png",
groups = {flammable = 2} groups = {flammable = 2, nametag = 1}
}) })
if minetest.get_modpath("dye") and minetest.get_modpath("farming") then if minetest.get_modpath("dye") and minetest.get_modpath("farming") then
@ -20,7 +20,7 @@ end
minetest.register_craftitem("mobs:leather", { minetest.register_craftitem("mobs:leather", {
description = S("Leather"), description = S("Leather"),
inventory_image = "mobs_leather.png", inventory_image = "mobs_leather.png",
groups = {flammable = 2} groups = {flammable = 2, leather = 1}
}) })
-- raw meat -- raw meat
@ -119,7 +119,7 @@ minetest.register_craft({
minetest.register_craftitem("mobs:saddle", { minetest.register_craftitem("mobs:saddle", {
description = S("Saddle"), description = S("Saddle"),
inventory_image = "mobs_saddle.png", inventory_image = "mobs_saddle.png",
groups = {flammable = 2} groups = {flammable = 2, saddle = 1}
}) })
minetest.register_craft({ minetest.register_craft({

View File

@ -1,6 +1,12 @@
local path = minetest.get_modpath("mobs") local path = minetest.get_modpath("mobs")
-- Peaceful player privilege
minetest.register_privilege("peaceful_player", {
description = "Prevents Mobs Redo mobs from attacking player",
give_to_singleplayer = false
})
-- Mob API -- Mob API
dofile(path .. "/api.lua") dofile(path .. "/api.lua")

View File

@ -1 +1,4 @@
name = mobs name = mobs
depends = default
optional_depends = tnt, dye, farming, invisibility, intllib, lucky_block, cmi, toolranks
description = Adds a mob api for mods to add animals or monsters etc.

193
mount.lua
View File

@ -1,9 +1,7 @@
-- lib_mount by Blert2112 (edited by TenPlus1) -- lib_mount by Blert2112 (edited by TenPlus1)
local enable_crash = false local abs, cos, floor, sin, sqrt, pi =
local crash_threshold = 6.5 -- ignored if enable_crash=false math.abs, math.cos, math.floor, math.sin, math.sqrt, math.pi
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
-- --
@ -11,7 +9,6 @@ local crash_threshold = 6.5 -- ignored if enable_crash=false
-- --
local node_ok = function(pos, fallback) local node_ok = function(pos, fallback)
fallback = fallback or mobs.fallback_node fallback = fallback or mobs.fallback_node
local node = minetest.get_node_or_nil(pos) local node = minetest.get_node_or_nil(pos)
@ -25,7 +22,6 @@ end
local function node_is(pos) local function node_is(pos)
local node = node_ok(pos) local node = node_ok(pos)
if node.name == "air" then if node.name == "air" then
@ -49,33 +45,30 @@ end
local function get_sign(i) local function get_sign(i)
i = i or 0 i = i or 0
if i == 0 then if i == 0 then
return 0 return 0
else else
return i / math.abs(i) return i / abs(i)
end end
end end
local function get_velocity(v, yaw, y) local function get_velocity(v, yaw, y)
local x = -sin(yaw) * v
local x = -math.sin(yaw) * v local z = cos(yaw) * v
local z = math.cos(yaw) * v
return {x = x, y = y, z = z} return {x = x, y = y, z = z}
end end
local function get_v(v) local function get_v(v)
return math.sqrt(v.x * v.x + v.z * v.z) return sqrt(v.x * v.x + v.z * v.z)
end end
local function force_detach(player) local function force_detach(player)
local attached_to = player:get_attach() local attached_to = player:get_attach()
if not attached_to then if not attached_to then
@ -86,21 +79,19 @@ local function force_detach(player)
if entity and entity.driver if entity and entity.driver
and entity.driver == player then and entity.driver == player then
entity.driver = nil entity.driver = nil
end end
player:set_detach() player:set_detach()
default.player_attached[player:get_player_name()] = false player_api.player_attached[player:get_player_name()] = false
player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
default.player_set_animation(player, "stand" , 30) player_api.set_animation(player, "stand", 30)
player:set_properties({visual_size = {x = 1, y = 1} }) player:set_properties({visual_size = {x = 1, y = 1}})
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
minetest.register_on_leaveplayer(function(player) minetest.register_on_leaveplayer(function(player)
force_detach(player) force_detach(player)
end) end)
@ -119,10 +110,37 @@ end)
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Just for correct detaching
local function find_free_pos(pos)
local check = {
{x = 1, y = 0, z = 0},
{x = 1, y = 1, z = 0},
{x = -1, y = 0, z = 0},
{x = -1, y = 1, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 1, z = 1},
{x = 0, y = 0, z = -1},
{x = 0, y = 1, z = -1}
}
for _, c in pairs(check) do
local npos = {x = pos.x + c.x, y = pos.y + c.y, z = pos.z + c.z}
local node = minetest.get_node_or_nil(npos)
if node and node.name then
local def = minetest.registered_nodes[node.name]
if def and not def.walkable and
def.liquidtype == "none" then
return npos
end
end
end
return pos
end
-------------------------------------------------------------------------------
function mobs.attach(entity, player) function mobs.attach(entity, player)
local attach_at, eye_offset = {}, {}
entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0} entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0}
entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0} entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0}
entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0} entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0}
@ -131,17 +149,17 @@ function mobs.attach(entity, player)
local rot_view = 0 local rot_view = 0
if entity.player_rotation.y == 90 then if entity.player_rotation.y == 90 then
rot_view = math.pi / 2 rot_view = pi / 2
end end
attach_at = entity.driver_attach_at local attach_at = entity.driver_attach_at
eye_offset = entity.driver_eye_offset local eye_offset = entity.driver_eye_offset
entity.driver = player entity.driver = player
force_detach(player) force_detach(player)
player:set_attach(entity.object, "", attach_at, entity.player_rotation) player:set_attach(entity.object, "", attach_at, entity.player_rotation)
default.player_attached[player:get_player_name()] = true player_api.player_attached[player:get_player_name()] = true
player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0}) player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0})
player:set_properties({ player:set_properties({
@ -151,46 +169,36 @@ function mobs.attach(entity, player)
} }
}) })
minetest.after(0.2, function(name) minetest.after(0.2, function()
local player = minetest.get_player_by_name(name) if player and player:is_player() then
if player then player_api.set_animation(player, "sit", 30)
default.player_set_animation(player, "sit" , 30)
end end
end, player:get_player_name()) end)
player:set_look_horizontal(entity.object:get_yaw() - rot_view) player:set_look_horizontal(entity.object:get_yaw() - rot_view)
end end
function mobs.detach(player, offset) function mobs.detach(player)
force_detach(player) force_detach(player)
default.player_set_animation(player, "stand" , 30) minetest.after(0.1, function()
if player and player:is_player() then
local pos = player:get_pos() local pos = find_free_pos(player:get_pos())
pos.y = pos.y + 0.5
pos = {
x = pos.x + offset.x,
y = pos.y + 0.2 + offset.y,
z = pos.z + offset.z
}
minetest.after(0.1, function(name, pos)
local player = minetest.get_player_by_name(name)
if player then
player:set_pos(pos) player:set_pos(pos)
end end
end, player:get_player_name(), pos) end)
end end
function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
local yaw = entity.object:get_yaw() or 0
local rot_steer, rot_view = math.pi/2, 0 local rot_view = 0
if entity.player_rotation.y == 90 then if entity.player_rotation.y == 90 then
rot_steer, rot_view = 0, math.pi/2 rot_view = pi / 2
end end
local acce_y = 0 local acce_y = 0
@ -200,19 +208,14 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
-- process controls -- process controls
if entity.driver then if entity.driver then
--print ("---velo", get_v(velo))
local ctrl = entity.driver:get_player_control() local ctrl = entity.driver:get_player_control()
-- move forwards -- move forwards
if ctrl.up then if ctrl.up then
entity.v = entity.v + entity.accel / 10 entity.v = entity.v + entity.accel / 10
-- move backwards -- move backwards
elseif ctrl.down then elseif ctrl.down then
if entity.max_speed_reverse == 0 and entity.v == 0 then if entity.max_speed_reverse == 0 and entity.v == 0 then
return return
end end
@ -220,12 +223,24 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
entity.v = entity.v - entity.accel / 10 entity.v = entity.v - entity.accel / 10
end end
-- fix mob rotation -- mob rotation
local horz = entity.driver:get_look_horizontal() or 0 local horz
if entity.alt_turn == true then
horz = yaw
if ctrl.left then
horz = horz + 0.05
elseif ctrl.right then
horz = horz - 0.05
end
else
horz = entity.driver:get_look_horizontal() or 0
end
entity.object:set_yaw(horz - entity.rotate) entity.object:set_yaw(horz - entity.rotate)
if can_fly then if can_fly then
-- fly up -- fly up
if ctrl.jump then if ctrl.jump then
velo.y = velo.y + 1 velo.y = velo.y + 1
@ -245,9 +260,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
velo.y = velo.y + 0.1 velo.y = velo.y + 0.1
if velo.y > 0 then velo.y = 0 end if velo.y > 0 then velo.y = 0 end
end end
else else
-- jump -- jump
if ctrl.jump then if ctrl.jump then
@ -261,7 +274,6 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
-- if not moving then set animation and return -- if not moving then set animation and return
if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
if stand_anim then if stand_anim then
mobs:set_animation(entity, stand_anim) mobs:set_animation(entity, stand_anim)
end end
@ -280,7 +292,6 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
entity.v = entity.v - 0.02 * s entity.v = entity.v - 0.02 * s
if s ~= get_sign(entity.v) then if s ~= get_sign(entity.v) then
entity.object:set_velocity({x = 0, y = 0, z = 0}) entity.object:set_velocity({x = 0, y = 0, z = 0})
entity.v = 0 entity.v = 0
return return
@ -293,14 +304,15 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
max_spd = entity.max_speed_forward max_spd = entity.max_speed_forward
end end
if math.abs(entity.v) > max_spd then if abs(entity.v) > max_spd then
entity.v = entity.v - get_sign(entity.v) entity.v = entity.v - get_sign(entity.v)
end end
-- Set position, velocity and acceleration -- Set position, velocity and acceleration
local p = entity.object:get_pos() local p = entity.object:get_pos()
local new_velo if not p then return end
local new_acce = {x = 0, y = -9.8, z = 0}
local new_acce = {x = 0, y = -9.81, z = 0}
p.y = p.y - 0.5 p.y = p.y - 0.5
@ -308,23 +320,18 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
local v = entity.v local v = entity.v
if ni == "air" then if ni == "air" then
if can_fly == true then if can_fly == true then
new_acce.y = 0 new_acce.y = 0
end end
elseif ni == "liquid" or ni == "lava" then elseif ni == "liquid" or ni == "lava" then
if ni == "lava" and entity.lava_damage ~= 0 then if ni == "lava" and entity.lava_damage ~= 0 then
entity.lava_counter = (entity.lava_counter or 0) + dtime entity.lava_counter = (entity.lava_counter or 0) + dtime
if entity.lava_counter > 1 then if entity.lava_counter > 1 then
minetest.sound_play("default_punch", { minetest.sound_play("default_punch", {
object = entity.object, object = entity.object,
max_hear_distance = 5 max_hear_distance = 5
}, true) })
entity.object:punch(entity.object, 1.0, { entity.object:punch(entity.object, 1.0, {
full_punch_interval = 1.0, full_punch_interval = 1.0,
@ -335,14 +342,12 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
end end
end end
if entity.terrain_type == 2 local terrain_type = entity.terrain_type
or entity.terrain_type == 3 then if terrain_type == 2 or terrain_type == 3 then
new_acce.y = 0 new_acce.y = 0
p.y = p.y + 1 p.y = p.y + 1
if node_is(p) == "liquid" then if node_is(p) == "liquid" then
if velo.y >= 5 then if velo.y >= 5 then
velo.y = 5 velo.y = 5
elseif velo.y < 0 then elseif velo.y < 0 then
@ -351,9 +356,11 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
new_acce.y = 5 new_acce.y = 5
end end
else else
if math.abs(velo.y) < 1 then if abs(velo.y) < 1 then
local pos = entity.object:get_pos() local pos = entity.object:get_pos()
pos.y = math.floor(pos.y) + 0.5 if not pos then return end
pos.y = floor(pos.y) + 0.5
entity.object:set_pos(pos) entity.object:set_pos(pos)
velo.y = 0 velo.y = 0
end end
@ -363,46 +370,22 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
end end
end end
new_velo = get_velocity(v, entity.object:get_yaw() - rot_view, velo.y) local new_velo = get_velocity(v, yaw - rot_view, velo.y)
new_acce.y = new_acce.y + acce_y new_acce.y = new_acce.y + acce_y
entity.object:set_velocity(new_velo) entity.object:set_velocity(new_velo)
entity.object:set_acceleration(new_acce) entity.object:set_acceleration(new_acce)
-- CRASH!
if enable_crash then
local intensity = entity.v2 - v
if intensity >= crash_threshold then
--print("----------- crash", intensity)
entity.object:punch(entity.object, 1.0, {
full_punch_interval = 1.0,
damage_groups = {fleshy = intensity}
}, nil)
end
end
entity.v2 = v entity.v2 = v
end end
-- directional flying routine by D00Med (edited by TenPlus1) -- directional flying routine by D00Med (edited by TenPlus1)
function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim)
function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
local ctrl = entity.driver:get_player_control() local ctrl = entity.driver:get_player_control()
local velo = entity.object:get_velocity() local velo = entity.object:get_velocity()
local dir = entity.driver:get_look_dir() local dir = entity.driver:get_look_dir()
local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands
local rot_steer, rot_view = math.pi / 2, 0
if entity.player_rotation.y == 90 then
rot_steer, rot_view = 0, math.pi / 2
end
if ctrl.up then if ctrl.up then
entity.object:set_velocity({ entity.object:set_velocity({
@ -422,11 +405,10 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
entity.object:set_velocity({x = 0, y = -2, z = 0}) entity.object:set_velocity({x = 0, y = -2, z = 0})
end end
entity.object:set_yaw(yaw + math.pi + math.pi / 2 - entity.rotate) entity.object:set_yaw(yaw + pi + pi / 2 - entity.rotate)
-- firing arrows -- firing arrows
if ctrl.LMB and ctrl.sneak and shoots then if ctrl.LMB and ctrl.sneak and shoots then
local pos = entity.object:get_pos() local pos = entity.object:get_pos()
local obj = minetest.add_entity({ local obj = minetest.add_entity({
x = pos.x + 0 + dir.x * 2.5, x = pos.x + 0 + dir.x * 2.5,
@ -438,8 +420,8 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
ent.switch = 1 -- for mob specific arrows ent.switch = 1 -- for mob specific arrows
ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6} local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6}
local yaw = entity.driver:get_look_horizontal() yaw = entity.driver:get_look_horizontal()
obj:set_yaw(yaw + math.pi / 2) obj:set_yaw(yaw + pi / 2)
obj:set_velocity(vec) obj:set_velocity(vec)
else else
obj:remove() obj:remove()
@ -448,7 +430,6 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
-- change animation if stopped -- change animation if stopped
if velo.x == 0 and velo.y == 0 and velo.z == 0 then if velo.x == 0 and velo.y == 0 and velo.z == 0 then
mobs:set_animation(entity, stand_anim) mobs:set_animation(entity, stand_anim)
else else
-- moving animation -- moving animation

View File

@ -23,6 +23,8 @@ Lucky Blocks: 9
Changelog: Changelog:
- 1.55 - Add 'peaceful_player' privelage and setting so mobs don't attack specific players (thanks sfence)
- 1.54 - Simplified animal breeding function, added editable settings (thanks Wuzzy), Child mobs now take 20 mins to grow up, reverted to simple mob spawning with setting to use area checks, on_flop added, air_damage added.
- 1.53 - Added 'on_map_load' settings to mobs:spawn so that mobs will only spawn when new areas of map are loaded. - 1.53 - Added 'on_map_load' settings to mobs:spawn so that mobs will only spawn when new areas of map are loaded.
- 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game, - 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game,
(default is 0 for unlimited), removed {immortal} from mob armor, fluid viscocity slows mobs (default is 0 for unlimited), removed {immortal} from mob armor, fluid viscocity slows mobs

View File

@ -33,3 +33,9 @@ mob_nospawn_range (Mob no-spawn range) float 12.0
# Sets maximum number of active mobs in game (0 for unlimited) # Sets maximum number of active mobs in game (0 for unlimited)
mob_active_limit (Mob Active Limit) float 0 mob_active_limit (Mob Active Limit) float 0
# Enables area check when spawning mobs
mob_area_spawn (Mob Area Spawn) bool false
# Enable peaceful player attack prevention
enable_peaceful_player (Mobs do not attack peaceful player without reason) bool false

View File

@ -23,9 +23,9 @@ minetest.register_node("mobs:spawner", {
-- text entry formspec -- text entry formspec
meta:set_string("formspec", meta:set_string("formspec",
"size[9,3.5]" "size[10,3.5]"
.. "label[0.15,0.5;" .. minetest.formspec_escape(head) .. "]" .. "label[0.15,0.5;" .. minetest.formspec_escape(head) .. "]"
.. "field[1,2.5;7.5,0.8;text;" .. S("Command:") .. "field[1,2.5;8.5,0.8;text;" .. S("Command:")
.. ";${command}]") .. ";${command}]")
meta:set_string("infotext", S("Spawner Not Active (enter settings)")) meta:set_string("infotext", S("Spawner Not Active (enter settings)"))
@ -162,10 +162,17 @@ minetest.register_abm({
end end
end end
-- set medium mob usually spawns in (defaults to air)
local reg = minetest.registered_entities[mob].fly_in
if not reg or type(reg) == "string" then
reg = {(reg or "air")}
end
-- find air blocks within 5 nodes of spawner -- find air blocks within 5 nodes of spawner
local air = minetest.find_nodes_in_area( local air = minetest.find_nodes_in_area(
{x = pos.x - 5, y = pos.y + yof, z = pos.z - 5}, {x = pos.x - 5, y = pos.y + yof, z = pos.z - 5},
{x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, {"air"}) {x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, reg)
-- spawn in random air block -- spawn in random air block
if air and #air > 0 then if air and #air > 0 then