1
0
mirror of https://codeberg.org/tenplus1/mobs_redo.git synced 2025-07-20 09:10:22 +02:00

Compare commits

...

19 Commits

Author SHA1 Message Date
c06d071220 allow players with 'protection_bypass' to rename owned tamed mobs 2021-04-07 10:35:36 +01:00
ccbfe93f12 players with protection_bypass can pick up owned animals from other players 2021-04-06 15:26:36 +01:00
6750d176ee tweak damage and protectors 2021-04-05 08:52:48 +01:00
246b2769f7 add level 2 mob protector 2021-04-04 22:25:40 +01:00
34b06df758 add ability to follow group: items 2021-03-23 17:09:26 +00:00
0f1f3b4fb3 simplify mobs floating in water 2021-03-18 20:23:55 +00:00
774ce6671f add 'ignore_invisibility' setting to mob definition 2021-03-10 08:14:42 +00:00
11e1d52cfb replace minetest 5.0 check 2021-02-06 17:10:50 +00:00
c158e84e28 stop grown child mobs sinking into blocks below 2021-02-03 21:29:26 +00:00
10e4dd92a0 spawner spawns mob in it's own medium (air, water etc.) 2021-01-21 11:10:17 +00:00
10053e05a9 update mod.conf info 2021-01-21 10:18:00 +00:00
425cadeb51 improve mount.lua (changes from lib_mount :) 2021-01-21 09:44:31 +00:00
2d1befacb3 nil check 2021-01-19 20:37:42 +00:00
ad2ccc5895 add a few compatibility groups to mob items 2021-01-14 19:24:40 +00:00
754321541a fix mob spawn height (thanks OgelGames) 2021-01-14 10:28:18 +00:00
622abd1486 hopefully fixed death animation crash when player away 2021-01-08 10:16:33 +00:00
55eb893a9a simple swimming fix to stop floaters 2021-01-07 10:42:10 +00:00
76ee3d0b51 add peaceful player privs and settings (thanks sfence) 2021-01-04 12:28:17 +00:00
00ac9efc8c fix global catch sound (thanks SwissalpS) 2021-01-02 20:31:01 +00:00
10 changed files with 312 additions and 213 deletions

273
api.lua
View File

@ -8,12 +8,12 @@ local use_cmi = minetest.global_exists("cmi")
mobs = { mobs = {
mod = "redo", mod = "redo",
version = "20201206", version = "20210407",
intllib = S, intllib = S,
invis = minetest.global_exists("invisibility") and invisibility or {} invis = minetest.global_exists("invisibility") and invisibility or {}
} }
-- localize math functions -- localize common functions
local pi = math.pi local pi = math.pi
local square = math.sqrt local square = math.sqrt
local sin = math.sin local sin = math.sin
@ -65,6 +65,7 @@ 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,7 +112,8 @@ local mob_class = {
light_damage_min = 14, light_damage_min = 14,
light_damage_max = 15, light_damage_max = 15,
water_damage = 0, water_damage = 0,
lava_damage = 0, lava_damage = 4,
fire_damage = 4,
air_damage = 0, air_damage = 0,
suffocation = 2, suffocation = 2,
fall_damage = 1, fall_damage = 1,
@ -163,6 +165,7 @@ local mob_class = {
local mob_class_meta = {__index = mob_class} local mob_class_meta = {__index = mob_class}
-- play sound -- play sound
function mob_class:mob_sound(sound) function mob_class:mob_sound(sound)
@ -250,9 +253,17 @@ local check_for = function(look_for, look_inside)
for _, str in pairs(look_inside) do for _, str in pairs(look_inside) do
if str == look_for then if str == look_for then
return true return true
end end
if str:find("group:") then
local group = str:split(":")[2]
if minetest.get_item_group(look_for, group) ~= 0 then
return true
end
end
end end
end end
@ -538,12 +549,10 @@ local ray_line_of_sight = function(self, pos1, pos2)
return true return true
end end
-- detect if using minetest 5.0 by searching for permafrost node
local is_50 = minetest.registered_nodes["default:permafrost"]
function mob_class:line_of_sight(pos1, pos2, stepsize) function mob_class:line_of_sight(pos1, pos2, stepsize)
if is_50 then -- only use if minetest 5.0 is detected if minetest.raycast then -- only use if minetest 5.0 is detected
return ray_line_of_sight(self, pos1, pos2) return ray_line_of_sight(self, pos1, pos2)
end end
@ -562,7 +571,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
@ -571,7 +580,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
@ -908,6 +918,7 @@ function mob_class:check_for_death(cmi_cause)
local rot = self.animation.die_rotate and 5 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
@ -915,7 +926,7 @@ function mob_class:check_for_death(cmi_cause)
self.state = "die" self.state = "die"
self.object:set_properties({ self.object:set_properties({
pointable = false, collide_with_objects = false, pointable = false, collide_with_objects = false,
automatic_rotate = rot, automatic_rotate = rot, static_save = false
}) })
self:set_velocity(0) self:set_velocity(0)
self:set_animation("die") self:set_animation("die")
@ -973,7 +984,12 @@ local is_node_dangerous = function(self, nodename)
end end
if self.lava_damage > 0 if self.lava_damage > 0
and minetest.get_item_group(nodename, "igniter") ~= 0 then and minetest.get_item_group(nodename, "lava") ~= 0 then
return true
end
if self.fire_damage > 0
and minetest.get_item_group(nodename, "fire") ~= 0 then
return true return true
end end
@ -1059,37 +1075,47 @@ function mob_class:do_env_damage()
local nodef = minetest.registered_nodes[self.standing_in] local nodef = minetest.registered_nodes[self.standing_in]
-- water -- water
if self.water_damage and nodef.groups.water then if self.water_damage ~= 0
and nodef.groups.water then
if self.water_damage ~= 0 then self.health = self.health - self.water_damage
self.health = self.health - self.water_damage effect(pos, 5, "bubble.png", nil, nil, 1, nil)
effect(pos, 5, "bubble.png", nil, nil, 1, nil) if self:check_for_death({type = "environment",
pos = pos, node = self.standing_in}) then
if self:check_for_death({type = "environment", return true
pos = pos, node = self.standing_in}) then
return true
end
end end
-- ignition source (fire or lava) -- lava damage
elseif self.lava_damage and nodef.groups.igniter then elseif self.lava_damage ~= 0
and nodef.groups.lava then
if self.lava_damage ~= 0 then self.health = self.health - self.lava_damage
self.health = self.health - self.lava_damage effect(pos, 15, "fire_basic_flame.png", 1, 5, 1, 0.2, 15, true)
effect(pos, 15, "fire_basic_flame.png", 1, 5, 1, 0.2, 15, true) if self:check_for_death({type = "environment", pos = pos,
node = self.standing_in, hot = true}) then
if self:check_for_death({type = "environment", pos = pos, return true
node = self.standing_in, hot = true}) then
return true
end
end end
-- damage_per_second node check -- fire damage
elseif nodef.damage_per_second ~= 0 then elseif self.fire_damage ~= 0
and nodef.groups.fire then
self.health = self.health - self.fire_damage
effect(pos, 15, "fire_basic_flame.png", 1, 5, 1, 0.2, 15, true)
if self:check_for_death({type = "environment", pos = pos,
node = self.standing_in, hot = true}) then
return true
end
-- damage_per_second node check (not fire and lava)
elseif nodef.damage_per_second ~= 0
and nodef.groups.lava == 0 and nodef.groups.fire == 0 then
self.health = self.health - nodef.damage_per_second self.health = self.health - nodef.damage_per_second
@ -1303,10 +1329,19 @@ local entity_physics = function(pos, radius)
end end
-- can mob see player
local is_invisible = function(self, player_name)
if mobs.invis[player_name] and not self.ignore_invisibility then
return true
end
end
-- should mob follow what I'm holding ? -- should mob follow what I'm holding ?
function mob_class:follow_holding(clicker) function mob_class:follow_holding(clicker)
if mobs.invis[clicker:get_player_name()] then if is_invisible(self, clicker:get_player_name()) then
return false return false
end end
@ -1351,11 +1386,15 @@ function mob_class:breed()
self.on_grown(self) self.on_grown(self)
else else
-- jump when fully grown so as not to fall into ground -- jump when fully grown so as not to fall into ground
self.object:set_velocity({ -- self.object:set_velocity({
x = 0, -- x = 0,
y = self.jump_height, -- y = self.jump_height,
z = 0 -- z = 0
}) -- })
local pos = self.object:get_pos() ; if not pos then return end
local ent = self.object:get_luaentity()
pos.y = pos.y + (ent.collisionbox[2] * -1) - 0.4
self.object:set_pos(pos)
end end
end end
@ -1455,6 +1494,8 @@ function mob_class:breed()
effect(pos, 15, "tnt_smoke.png", 1, 2, 2, 15, 5) effect(pos, 15, "tnt_smoke.png", 1, 2, 2, 15, 5)
end end
pos.y = pos.y + 0.5 -- spawn child a little higher
local mob = minetest.add_entity(pos, self.name) local mob = minetest.add_entity(pos, self.name)
local ent2 = mob:get_luaentity() local ent2 = mob:get_luaentity()
local textures = self.base_texture local textures = self.base_texture
@ -1848,6 +1889,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()
@ -1874,7 +1932,7 @@ function mob_class:general_attack()
if not damage_enabled if not damage_enabled
or self.attack_players == false or self.attack_players == false
or (self.owner and self.type ~= "monster") or (self.owner and self.type ~= "monster")
or mobs.invis[objs[n]:get_player_name()] or is_invisible(self, objs[n]:get_player_name())
or (self.specific_attack or (self.specific_attack
and not check_for("player", self.specific_attack)) then and not check_for("player", self.specific_attack)) then
objs[n] = nil objs[n] = nil
@ -1920,7 +1978,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
@ -1952,7 +2011,7 @@ function mob_class:do_runaway_from()
pname = objs[n]:get_player_name() pname = objs[n]:get_player_name()
if mobs.invis[pname] if is_invisible(self, pname)
or self.owner == pname then or self.owner == pname then
name = "" name = ""
@ -2017,7 +2076,7 @@ function mob_class:follow_flop()
for n = 1, #players do for n = 1, #players do
if get_distance(players[n]:get_pos(), s) < self.view_range if get_distance(players[n]:get_pos(), s) < self.view_range
and not mobs.invis[ players[n]:get_player_name() ] then and not is_invisible(self, players[n]:get_player_name()) then
self.following = players[n] self.following = players[n]
@ -2315,7 +2374,7 @@ function mob_class:do_states(dtime)
or not self.attack:get_pos() or not self.attack:get_pos()
or self.attack:get_hp() <= 0 or self.attack:get_hp() <= 0
or (self.attack:is_player() or (self.attack:is_player()
and mobs.invis[ self.attack:get_player_name() ]) then and is_invisible(self, self.attack:get_player_name())) then
--print(" ** stop attacking **", dist, self.view_range) --print(" ** stop attacking **", dist, self.view_range)
@ -2663,34 +2722,17 @@ function mob_class:falling(pos)
-- sanity check -- sanity check
if not v then return end if not v then return end
local fall_speed = -10 -- gravity local fall_speed = self.fall_speed
-- don't exceed mob fall speed
if v.y < self.fall_speed then
fall_speed = self.fall_speed
end
-- in water then use liquid viscosity for float/sink speed -- in water then use liquid viscosity for float/sink speed
if (self.standing_in if self.floats == 1 and self.standing_in
and minetest.registered_nodes[self.standing_in].groups.liquid) and minetest.registered_nodes[self.standing_in].groups.liquid then
or (self.standing_on
and minetest.registered_nodes[self.standing_in].groups.liquid) then
local visc = min( local visc = min(
minetest.registered_nodes[self.standing_in].liquid_viscosity, 7) minetest.registered_nodes[self.standing_in].liquid_viscosity, 7) + 1
if self.floats == 1 then self.object:set_velocity({x = v.x, y = 0.6, z = v.z})
fall_speed = -1.2 / visc
-- floating up
if visc > 0 then
fall_speed = max(1, v.y) / (visc + 1)
end
else
-- sinking down
if visc > 0 then
fall_speed = -(max(1, v.y) / (visc + 1))
end
end
else else
-- fall damage onto solid ground -- fall damage onto solid ground
@ -2747,15 +2789,37 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
return true return true
end end
-- is mob protected? -- is mob protected
if self.protected and hitter:is_player() if self.protected then
and minetest.is_protected(self.object:get_pos(),
hitter:get_player_name()) then
minetest.chat_send_player(hitter:get_player_name(), -- did player hit mob and if so is it in protected area
S("Mob has been protected!")) if hitter:is_player() then
return true local player_name = hitter:get_player_name()
if player_name ~= self.owner
and minetest.is_protected(self.object:get_pos(), player_name) then
minetest.chat_send_player(hitter:get_player_name(),
S("Mob has been protected!"))
return true
end
-- if protection is on level 2 then dont let arrows harm mobs
elseif self.protected == 2 then
local ent = hitter and hitter:get_luaentity()
if ent and ent._is_arrow then
return true -- arrow entity
elseif not ent then
return true -- non entity
end
end
end end
local weapon = hitter:get_wielded_item() local weapon = hitter:get_wielded_item()
@ -2787,7 +2851,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
end end
damage = damage + (tool_capabilities.damage_groups[group] or 0) damage = damage + (tool_capabilities.damage_groups[group] or 0)
* tmp * ((armor[group] or 0) / 100.0) * tmp * ((armor[group] or 0) / 100.0)
end end
end end
@ -2967,7 +3031,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
and self.child == false and self.child == false
and self.attack_players == true and self.attack_players == true
and hitter:get_player_name() ~= self.owner and hitter:get_player_name() ~= self.owner
and not mobs.invis[ name ] and not is_invisible(self, name)
and self.object ~= hitter then and self.object ~= hitter then
-- attack whoever punched mob -- attack whoever punched mob
@ -3054,6 +3118,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
@ -3123,8 +3188,7 @@ function mob_class:mob_activate(staticdata, def, dtime)
def.textures = {def.textures} def.textures = {def.textures}
end end
self.base_texture = def.textures and self.base_texture = def.textures and def.textures[random(#def.textures)]
def.textures[random(#def.textures)]
self.base_mesh = def.mesh self.base_mesh = def.mesh
self.base_size = self.visual_size self.base_size = self.visual_size
self.base_colbox = self.collisionbox self.base_colbox = self.collisionbox
@ -3306,6 +3370,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
@ -3529,6 +3595,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,
fire_damage = def.fire_damage,
air_damage = def.air_damage, air_damage = def.air_damage,
suffocation = def.suffocation, suffocation = def.suffocation,
fall_damage = def.fall_damage, fall_damage = def.fall_damage,
@ -3587,6 +3654,7 @@ minetest.register_entity(name, setmetatable({
pushable = def.pushable, pushable = def.pushable,
stay_near = def.stay_near, stay_near = def.stay_near,
randomly_turn = def.randomly_turn ~= false, randomly_turn = def.randomly_turn ~= false,
ignore_invisibility = def.ignore_invisibility,
on_spawn = def.on_spawn, on_spawn = def.on_spawn,
@ -3681,9 +3749,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
@ -3948,15 +4013,15 @@ 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 ? -- should we check mob area for obstructions ?
if mob_area_spawn ~= true then if mob_area_spawn ~= true then
-- do we have enough height clearance to spawn mob? -- do we have enough height clearance to spawn mob?
local ent = minetest.registered_entities[name] local height = max(0, ent.collisionbox[5] - ent.collisionbox[2])
local height = max(1, math.ceil(
(ent.collisionbox[5] or 0.25) - (ent.collisionbox[2] or -0.25) - 1))
for n = 0, height do for n = 0, floor(height) do
local pos2 = {x = pos.x, y = pos.y + n, z = pos.z} local pos2 = {x = pos.x, y = pos.y + n, z = pos.z}
@ -3972,6 +4037,9 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter
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 "
@ -4431,8 +4499,9 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net,
return false return false
end end
-- cannot pick up if not owner -- cannot pick up if not owner (unless player has protection_bypass priv)
if self.owner ~= name and force_take == false then if not minetest.check_player_privs(name, "protection_bypass")
and self.owner ~= name and force_take == false then
minetest.chat_send_player(name, S("@1 is owner!", self.owner)) minetest.chat_send_player(name, S("@1 is owner!", self.owner))
@ -4503,10 +4572,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!
@ -4533,19 +4602,21 @@ function mobs:protect(self, clicker)
local name = clicker:get_player_name() local name = clicker:get_player_name()
local tool = clicker:get_wielded_item() local tool = clicker:get_wielded_item()
local tool_name = tool:get_name()
if tool:get_name() ~= "mobs:protector" then if tool_name ~= "mobs:protector"
and tool_name ~= "mobs:protector2" then
return false return false
end end
if self.tamed == false then if not self.tamed then
minetest.chat_send_player(name, S("Not tamed!")) minetest.chat_send_player(name, S("Not tamed!"))
return true -- false return true
end end
if self.protected == true then if self.protected then
minetest.chat_send_player(name, S("Already protected!")) minetest.chat_send_player(name, S("Already protected!"))
return true -- false return true
end end
if not mobs.is_creative(clicker:get_player_name()) then if not mobs.is_creative(clicker:get_player_name()) then
@ -4553,9 +4624,15 @@ function mobs:protect(self, clicker)
clicker:set_wielded_item(tool) clicker:set_wielded_item(tool)
end end
self.protected = true -- set protection level
if tool_name == "mobs:protector" then
self.protected = true
else
self.protected = 2 ; self.fire_damage = 0
end
local pos = self.object:get_pos() local pos = self.object:get_pos()
pos.y = pos.y + self.collisionbox[2] + 0.5 pos.y = pos.y + self.collisionbox[2] + 0.5
effect(self.object:get_pos(), 25, "mobs_protect_particle.png", effect(self.object:get_pos(), 25, "mobs_protect_particle.png",
@ -4615,7 +4692,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
-- deduct 10% of the time to adulthood -- deduct 10% of the time to adulthood
self.hornytimer = self.hornytimer + ( self.hornytimer = self.hornytimer + (
(CHILD_GROW_TIME - self.hornytimer) * 0.1) (CHILD_GROW_TIME - self.hornytimer) * 0.1)
print ("====", self.hornytimer) --print ("====", self.hornytimer)
return true return true
end end
@ -4653,12 +4730,12 @@ print ("====", self.hornytimer)
end end
local item = clicker:get_wielded_item() local item = clicker:get_wielded_item()
local name = clicker:get_player_name()
-- if mob has been tamed you can name it with a nametag -- if mob has been tamed you can name it with a nametag
if item:get_name() == "mobs:nametag" if item:get_name() == "mobs:nametag"
and clicker:get_player_name() == self.owner then and (name == self.owner
or minetest.check_player_privs(name, "protection_bypass")) then
local name = clicker:get_player_name()
-- store mob and nametag stack in external variables -- store mob and nametag stack in external variables
mob_obj[name] = self mob_obj[name] = self

View File

@ -66,7 +66,10 @@ functions needed for the mob to work properly which contains the following:
water. water.
'air_damage' holds damage per second inflicted to mob when standing in air. '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.
'fire_damage' holds the damage per second inflicted to mobs when standing
in fire.
'light_damage' holds the damage per second inflicted to mobs when light 'light_damage' holds the damage per second inflicted to mobs when light
level is between the min and max values below level is between the min and max values below
'light_damage_min' minimum light value when mob is affected (default: 14) 'light_damage_min' minimum light value when mob is affected (default: 14)
@ -76,7 +79,7 @@ functions needed for the mob to work properly which contains the following:
'floats' when set to 1 mob will float in water, 0 has them sink. 'floats' when set to 1 mob will float in water, 0 has them sink.
'follow' mobs follow player when holding any of the items which appear 'follow' mobs follow player when holding any of the items which appear
on this table, the same items can be fed to a mob to tame or on this table, the same items can be fed to a mob to tame or
breed e.g. {"farming:wheat", "default:apple"} breed e.g. {"farming:wheat", "default:apple", "group:fish"}
'reach' is how far the mob can attack player when standing 'reach' is how far the mob can attack player when standing
nearby, default is 3 nodes. nearby, default is 3 nodes.
@ -131,6 +134,8 @@ functions needed for the mob to work properly which contains the following:
e.g. {"player", "mobs_animal:chicken"}. e.g. {"player", "mobs_animal:chicken"}.
'runaway_from' contains a table with mob names to run away from, add 'runaway_from' contains a table with mob names to run away from, add
"player" to list to runaway from player also. "player" to list to runaway from player also.
'ignore_invisibility' When true mob will still be able to see and attack
player even if invisible (invisibility mod only).
'blood_amount' contains the number of blood droplets to appear when 'blood_amount' contains the number of blood droplets to appear when
mob is hit. mob is hit.
'blood_texture' has the texture name to use for droplets e.g. 'blood_texture' has the texture name to use for droplets e.g.

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
@ -115,11 +115,27 @@ minetest.register_craft({
} }
}) })
-- level 2 protection rune
minetest.register_craftitem("mobs:protector2", {
description = S("Mob Protection Rune (Level 2)"),
inventory_image = "mobs_protector2.png",
groups = {flammable = 2}
})
minetest.register_craft({
output = "mobs:protector2",
recipe = {
{"mobs:protector", "default:mese_crystal", "mobs:protector"},
{"default:mese_crystal", "default:diamondblock", "default:mese_crystal"},
{"mobs:protector", "default:mese_crystal", "mobs:protector"}
}
})
-- saddle -- saddle
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.

195
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({
@ -414,7 +397,7 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
elseif ctrl.down then elseif ctrl.down then
entity.object:set_velocity({ entity.object:set_velocity({
x = -dir.x * speed, x = -dir.x * speed,
y = dir.y * speed + 2, y = dir.y * speed + 2,
z = -dir.z * speed z = -dir.z * speed
}) })
@ -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,7 @@ 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.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,

View File

@ -36,3 +36,6 @@ mob_active_limit (Mob Active Limit) float 0
# Enables area check when spawning mobs # Enables area check when spawning mobs
mob_area_spawn (Mob Area Spawn) bool false 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B