mirror of
https://codeberg.org/tenplus1/mobs_redo.git
synced 2025-07-19 16:50:25 +02:00
Compare commits
25 Commits
d12576f0c8
...
c06d071220
Author | SHA1 | Date | |
---|---|---|---|
c06d071220 | |||
ccbfe93f12 | |||
6750d176ee | |||
246b2769f7 | |||
34b06df758 | |||
0f1f3b4fb3 | |||
774ce6671f | |||
11e1d52cfb | |||
c158e84e28 | |||
10e4dd92a0 | |||
10053e05a9 | |||
425cadeb51 | |||
2d1befacb3 | |||
ad2ccc5895 | |||
754321541a | |||
622abd1486 | |||
55eb893a9a | |||
76ee3d0b51 | |||
00ac9efc8c | |||
bf32a09e5e | |||
9489ff6965 | |||
1dfd7e1af1 | |||
4a6518eeba | |||
a4d2918383 | |||
bdea826b7c |
368
api.lua
368
api.lua
@ -8,12 +8,12 @@ local use_cmi = minetest.global_exists("cmi")
|
||||
|
||||
mobs = {
|
||||
mod = "redo",
|
||||
version = "20201029",
|
||||
version = "20210407",
|
||||
intllib = S,
|
||||
invis = minetest.global_exists("invisibility") and invisibility or {}
|
||||
}
|
||||
|
||||
-- localize math functions
|
||||
-- localize common functions
|
||||
local pi = math.pi
|
||||
local square = math.sqrt
|
||||
local sin = math.sin
|
||||
@ -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 spawn_protected = settings:get_bool("mobs_spawn_protected") ~= 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 show_health = settings:get_bool("mob_show_health") ~= false
|
||||
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 active_limit = tonumber(settings:get("mob_active_limit") or 0)
|
||||
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
|
||||
|
||||
|
||||
@ -110,7 +112,9 @@ local mob_class = {
|
||||
light_damage_min = 14,
|
||||
light_damage_max = 15,
|
||||
water_damage = 0,
|
||||
lava_damage = 0,
|
||||
lava_damage = 4,
|
||||
fire_damage = 4,
|
||||
air_damage = 0,
|
||||
suffocation = 2,
|
||||
fall_damage = 1,
|
||||
fall_speed = -10, -- must be lower than -2 (default: -10)
|
||||
@ -161,6 +165,7 @@ local mob_class = {
|
||||
|
||||
local mob_class_meta = {__index = mob_class}
|
||||
|
||||
|
||||
-- play sound
|
||||
function mob_class:mob_sound(sound)
|
||||
|
||||
@ -248,9 +253,17 @@ local check_for = function(look_for, look_inside)
|
||||
for _, str in pairs(look_inside) do
|
||||
|
||||
if str == look_for then
|
||||
|
||||
return true
|
||||
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
|
||||
|
||||
@ -276,10 +289,10 @@ function mob_class:set_velocity(v)
|
||||
c_x, c_y = unpack(self:collision())
|
||||
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
|
||||
v = v or 0
|
||||
v = v or 0.01
|
||||
|
||||
-- check if standing in liquid with max viscosity of 7
|
||||
local visc = min(minetest.registered_nodes[self.standing_in].liquid_viscosity, 7)
|
||||
@ -291,8 +304,8 @@ function mob_class:set_velocity(v)
|
||||
v = v / (visc + 1)
|
||||
end
|
||||
|
||||
-- set velocity with hard limit of 10
|
||||
local vel = self.object:get_velocity()
|
||||
-- set velocity
|
||||
local vel = self.object:get_velocity() or 0
|
||||
|
||||
local new_vel = {
|
||||
x = (sin(yaw) * -v) + c_x,
|
||||
@ -536,12 +549,10 @@ local ray_line_of_sight = function(self, pos1, pos2)
|
||||
return true
|
||||
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)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
@ -560,7 +571,7 @@ function mob_class:attempt_flight_correction(override)
|
||||
|
||||
-- We are not flying in what we are supposed to.
|
||||
-- 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
|
||||
|
||||
if type(searchnodes) == "string" then
|
||||
@ -569,7 +580,8 @@ function mob_class:attempt_flight_correction(override)
|
||||
|
||||
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}, 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
|
||||
return false
|
||||
@ -828,6 +840,11 @@ end
|
||||
-- check if mob is dead or only hurt
|
||||
function mob_class:check_for_death(cmi_cause)
|
||||
|
||||
-- We dead already
|
||||
if self.state == "die" then
|
||||
return true
|
||||
end
|
||||
|
||||
-- has health actually changed?
|
||||
if self.health == self.old_health and self.health > 0 then
|
||||
return false
|
||||
@ -877,7 +894,7 @@ function mob_class:check_for_death(cmi_cause)
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
-- execute custom death function
|
||||
if self.on_die then
|
||||
if pos and self.on_die then
|
||||
|
||||
self:on_die(pos)
|
||||
|
||||
@ -897,27 +914,26 @@ function mob_class:check_for_death(cmi_cause)
|
||||
|
||||
local frames = self.animation.die_end - self.animation.die_start
|
||||
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.following = nil
|
||||
self.v_start = false
|
||||
self.timer = 0
|
||||
self.blinktimer = 0
|
||||
self.passive = true
|
||||
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_animation("die")
|
||||
|
||||
minetest.after(length, function(self)
|
||||
|
||||
if use_cmi and self.object:get_luaentity() then
|
||||
cmi.notify_die(self.object, cmi_cause)
|
||||
end
|
||||
|
||||
remove_mob(self, true)
|
||||
|
||||
end, self)
|
||||
else
|
||||
if self.object:get_luaentity() then
|
||||
|
||||
if use_cmi then
|
||||
cmi.notify_die(self.object, cmi_cause)
|
||||
@ -925,8 +941,20 @@ function mob_class:check_for_death(cmi_cause)
|
||||
|
||||
remove_mob(self, true)
|
||||
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")
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
@ -956,7 +984,12 @@ local is_node_dangerous = function(self, nodename)
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
@ -1042,9 +1075,8 @@ function mob_class:do_env_damage()
|
||||
local nodef = minetest.registered_nodes[self.standing_in]
|
||||
|
||||
-- water
|
||||
if self.water_damage and nodef.groups.water then
|
||||
|
||||
if self.water_damage ~= 0 then
|
||||
if self.water_damage ~= 0
|
||||
and nodef.groups.water then
|
||||
|
||||
self.health = self.health - self.water_damage
|
||||
|
||||
@ -1054,12 +1086,10 @@ function mob_class:do_env_damage()
|
||||
pos = pos, node = self.standing_in}) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- ignition source (fire or lava)
|
||||
elseif self.lava_damage and nodef.groups.igniter then
|
||||
|
||||
if self.lava_damage ~= 0 then
|
||||
-- lava damage
|
||||
elseif self.lava_damage ~= 0
|
||||
and nodef.groups.lava then
|
||||
|
||||
self.health = self.health - self.lava_damage
|
||||
|
||||
@ -1069,10 +1099,23 @@ function mob_class:do_env_damage()
|
||||
node = self.standing_in, hot = true}) then
|
||||
return true
|
||||
end
|
||||
|
||||
-- fire damage
|
||||
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
|
||||
elseif nodef.damage_per_second ~= 0 then
|
||||
-- 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
|
||||
|
||||
@ -1084,6 +1127,19 @@ function mob_class:do_env_damage()
|
||||
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
|
||||
if self.light_damage ~= 0 then
|
||||
|
||||
@ -1273,10 +1329,19 @@ local entity_physics = function(pos, radius)
|
||||
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 ?
|
||||
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
|
||||
end
|
||||
|
||||
@ -1290,16 +1355,20 @@ function mob_class:follow_holding(clicker)
|
||||
return false
|
||||
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
|
||||
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
|
||||
|
||||
self.hornytimer = self.hornytimer + 1
|
||||
|
||||
if self.hornytimer > 240 then
|
||||
if self.hornytimer > CHILD_GROW_TIME then
|
||||
|
||||
self.child = false
|
||||
self.hornytimer = 0
|
||||
@ -1317,25 +1386,29 @@ function mob_class:breed()
|
||||
self.on_grown(self)
|
||||
else
|
||||
-- jump when fully grown so as not to fall into ground
|
||||
self.object:set_velocity({
|
||||
x = 0,
|
||||
y = self.jump_height,
|
||||
z = 0
|
||||
})
|
||||
-- self.object:set_velocity({
|
||||
-- x = 0,
|
||||
-- y = self.jump_height,
|
||||
-- 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
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- horny animal can mate for 40 seconds,
|
||||
-- afterwards horny animal cannot mate again for 200 seconds
|
||||
-- horny animal can mate for HORNY_TIME seconds,
|
||||
-- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
|
||||
if self.horny == true
|
||||
and self.hornytimer < 240 then
|
||||
and self.hornytimer < HORNY_TIME + HORNY_AGAIN_TIME then
|
||||
|
||||
self.hornytimer = self.hornytimer + 1
|
||||
|
||||
if self.hornytimer >= 240 then
|
||||
if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
|
||||
self.hornytimer = 0
|
||||
self.horny = false
|
||||
end
|
||||
@ -1343,7 +1416,7 @@ function mob_class:breed()
|
||||
|
||||
-- find another same animal who is also horny and mate if nearby
|
||||
if self.horny == true
|
||||
and self.hornytimer <= 40 then
|
||||
and self.hornytimer <= HORNY_TIME then
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
@ -1351,7 +1424,6 @@ function mob_class:breed()
|
||||
"heart.png", 3, 4, 1, 0.1)
|
||||
|
||||
local objs = minetest.get_objects_inside_radius(pos, 3)
|
||||
local num = 0
|
||||
local ent
|
||||
|
||||
for n = 1, #objs do
|
||||
@ -1380,18 +1452,20 @@ function mob_class:breed()
|
||||
end
|
||||
end
|
||||
|
||||
if ent
|
||||
-- found another similar horny animal that isn't self?
|
||||
if ent and ent.object ~= self.object
|
||||
and canmate == true
|
||||
and ent.horny == true
|
||||
and ent.hornytimer <= 40 then
|
||||
num = num + 1
|
||||
end
|
||||
and ent.hornytimer <= HORNY_TIME then
|
||||
|
||||
-- found your mate? then have a baby
|
||||
if num > 1 then
|
||||
local pos2 = ent.object:get_pos()
|
||||
|
||||
self.hornytimer = 41
|
||||
ent.hornytimer = 41
|
||||
-- Have mobs face one another
|
||||
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
|
||||
if active_limit > 0 and active_mobs >= active_limit then
|
||||
@ -1420,6 +1494,8 @@ function mob_class:breed()
|
||||
effect(pos, 15, "tnt_smoke.png", 1, 2, 2, 15, 5)
|
||||
end
|
||||
|
||||
pos.y = pos.y + 0.5 -- spawn child a little higher
|
||||
|
||||
local mob = minetest.add_entity(pos, self.name)
|
||||
local ent2 = mob:get_luaentity()
|
||||
local textures = self.base_texture
|
||||
@ -1459,8 +1535,6 @@ function mob_class:breed()
|
||||
ent2.owner = self.owner
|
||||
end, self, ent)
|
||||
|
||||
num = 0
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
@ -1815,6 +1889,23 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
||||
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
|
||||
function mob_class:general_attack()
|
||||
|
||||
@ -1841,7 +1932,7 @@ function mob_class:general_attack()
|
||||
if not damage_enabled
|
||||
or self.attack_players == false
|
||||
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
|
||||
and not check_for("player", self.specific_attack)) then
|
||||
objs[n] = nil
|
||||
@ -1887,7 +1978,8 @@ function mob_class:general_attack()
|
||||
-- choose closest player to attack that isnt self
|
||||
if dist ~= 0
|
||||
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_player = player
|
||||
end
|
||||
@ -1919,7 +2011,7 @@ function mob_class:do_runaway_from()
|
||||
|
||||
pname = objs[n]:get_player_name()
|
||||
|
||||
if mobs.invis[pname]
|
||||
if is_invisible(self, pname)
|
||||
or self.owner == pname then
|
||||
|
||||
name = ""
|
||||
@ -1984,7 +2076,7 @@ function mob_class:follow_flop()
|
||||
for n = 1, #players do
|
||||
|
||||
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]
|
||||
|
||||
@ -2005,10 +2097,11 @@ function mob_class:follow_flop()
|
||||
self.following = nil
|
||||
end
|
||||
else
|
||||
-- stop following player if not holding specific item
|
||||
-- stop following player if not holding specific item or mob is horny
|
||||
if self.following
|
||||
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
|
||||
end
|
||||
|
||||
@ -2064,6 +2157,15 @@ function mob_class:follow_flop()
|
||||
if not self:attempt_flight_correction() then
|
||||
|
||||
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:set_animation("stand")
|
||||
@ -2272,7 +2374,7 @@ function mob_class:do_states(dtime)
|
||||
or not self.attack:get_pos()
|
||||
or self.attack:get_hp() <= 0
|
||||
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)
|
||||
|
||||
@ -2620,34 +2722,17 @@ function mob_class:falling(pos)
|
||||
-- sanity check
|
||||
if not v then return end
|
||||
|
||||
local fall_speed = -10 -- gravity
|
||||
|
||||
-- don't exceed mob fall speed
|
||||
if v.y < self.fall_speed then
|
||||
fall_speed = self.fall_speed
|
||||
end
|
||||
local fall_speed = self.fall_speed
|
||||
|
||||
-- in water then use liquid viscosity for float/sink speed
|
||||
if (self.standing_in
|
||||
and minetest.registered_nodes[self.standing_in].groups.liquid)
|
||||
or (self.standing_on
|
||||
and minetest.registered_nodes[self.standing_in].groups.liquid) then
|
||||
if self.floats == 1 and self.standing_in
|
||||
and minetest.registered_nodes[self.standing_in].groups.liquid then
|
||||
|
||||
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
|
||||
|
||||
-- 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
|
||||
self.object:set_velocity({x = v.x, y = 0.6, z = v.z})
|
||||
fall_speed = -1.2 / visc
|
||||
else
|
||||
|
||||
-- fall damage onto solid ground
|
||||
@ -2704,10 +2789,16 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
|
||||
return true
|
||||
end
|
||||
|
||||
-- is mob protected?
|
||||
if self.protected and hitter:is_player()
|
||||
and minetest.is_protected(self.object:get_pos(),
|
||||
hitter:get_player_name()) then
|
||||
-- is mob protected
|
||||
if self.protected then
|
||||
|
||||
-- did player hit mob and if so is it in protected area
|
||||
if hitter:is_player() then
|
||||
|
||||
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!"))
|
||||
@ -2715,6 +2806,22 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
|
||||
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
|
||||
|
||||
local weapon = hitter:get_wielded_item()
|
||||
local weapon_def = weapon:get_definition() or {}
|
||||
local punch_interval = 1.4
|
||||
@ -2924,7 +3031,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
|
||||
and self.child == false
|
||||
and self.attack_players == true
|
||||
and hitter:get_player_name() ~= self.owner
|
||||
and not mobs.invis[ name ]
|
||||
and not is_invisible(self, name)
|
||||
and self.object ~= hitter then
|
||||
|
||||
-- attack whoever punched mob
|
||||
@ -3011,6 +3118,7 @@ function mob_class:mob_staticdata()
|
||||
if t ~= "function"
|
||||
and t ~= "nil"
|
||||
and t ~= "userdata"
|
||||
and _ ~= "object"
|
||||
and _ ~= "_cmi_components" then
|
||||
tmp[_] = self[_]
|
||||
end
|
||||
@ -3080,8 +3188,7 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
||||
def.textures = {def.textures}
|
||||
end
|
||||
|
||||
self.base_texture = def.textures and
|
||||
def.textures[random(#def.textures)]
|
||||
self.base_texture = def.textures and def.textures[random(#def.textures)]
|
||||
self.base_mesh = def.mesh
|
||||
self.base_size = self.visual_size
|
||||
self.base_colbox = self.collisionbox
|
||||
@ -3263,6 +3370,8 @@ function mob_class:on_step(dtime, moveresult)
|
||||
}}
|
||||
}]]
|
||||
|
||||
if self.state == "die" then return end ----------------
|
||||
|
||||
if use_cmi then
|
||||
cmi.notify_step(self.object, dtime)
|
||||
end
|
||||
@ -3462,6 +3571,7 @@ minetest.register_entity(name, setmetatable({
|
||||
owner = def.owner,
|
||||
order = def.order,
|
||||
on_die = def.on_die,
|
||||
on_flop = def.on_flop,
|
||||
do_custom = def.do_custom,
|
||||
jump_height = def.jump_height,
|
||||
drawtype = def.drawtype, -- DEPRECATED, use rotate instead
|
||||
@ -3485,6 +3595,8 @@ minetest.register_entity(name, setmetatable({
|
||||
light_damage_max = def.light_damage_max,
|
||||
water_damage = def.water_damage,
|
||||
lava_damage = def.lava_damage,
|
||||
fire_damage = def.fire_damage,
|
||||
air_damage = def.air_damage,
|
||||
suffocation = def.suffocation,
|
||||
fall_damage = def.fall_damage,
|
||||
fall_speed = def.fall_speed,
|
||||
@ -3542,6 +3654,7 @@ minetest.register_entity(name, setmetatable({
|
||||
pushable = def.pushable,
|
||||
stay_near = def.stay_near,
|
||||
randomly_turn = def.randomly_turn ~= false,
|
||||
ignore_invisibility = def.ignore_invisibility,
|
||||
|
||||
on_spawn = def.on_spawn,
|
||||
|
||||
@ -3636,9 +3749,6 @@ local can_spawn = function(pos, name)
|
||||
end
|
||||
end
|
||||
|
||||
-- spawn mob 1/2 node above ground
|
||||
pos.y = pos.y + 0.5
|
||||
|
||||
-- tweak X/Z spawn pos
|
||||
if width_x % 2 == 0 then
|
||||
pos.x = pos.x + 0.5
|
||||
@ -3903,11 +4013,33 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter
|
||||
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
|
||||
pos = can_spawn(pos, name)
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
-- print("[mobs] Spawned " .. name .. " at "
|
||||
@ -4367,8 +4499,9 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net,
|
||||
return false
|
||||
end
|
||||
|
||||
-- cannot pick up if not owner
|
||||
if self.owner ~= name and force_take == false then
|
||||
-- cannot pick up if not owner (unless player has protection_bypass priv)
|
||||
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))
|
||||
|
||||
@ -4439,10 +4572,10 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net,
|
||||
minetest.add_item(clicker:get_pos(), new_stack)
|
||||
end
|
||||
|
||||
remove_mob(self, true)
|
||||
|
||||
self:mob_sound("default_place_node_hard")
|
||||
|
||||
remove_mob(self, true)
|
||||
|
||||
return new_stack
|
||||
|
||||
-- when chance above fails or set to 0, miss!
|
||||
@ -4469,19 +4602,21 @@ function mobs:protect(self, clicker)
|
||||
|
||||
local name = clicker:get_player_name()
|
||||
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
|
||||
end
|
||||
|
||||
if self.tamed == false then
|
||||
if not self.tamed then
|
||||
minetest.chat_send_player(name, S("Not tamed!"))
|
||||
return true -- false
|
||||
return true
|
||||
end
|
||||
|
||||
if self.protected == true then
|
||||
if self.protected then
|
||||
minetest.chat_send_player(name, S("Already protected!"))
|
||||
return true -- false
|
||||
return true
|
||||
end
|
||||
|
||||
if not mobs.is_creative(clicker:get_player_name()) then
|
||||
@ -4489,9 +4624,15 @@ function mobs:protect(self, clicker)
|
||||
clicker:set_wielded_item(tool)
|
||||
end
|
||||
|
||||
-- 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()
|
||||
|
||||
pos.y = pos.y + self.collisionbox[2] + 0.5
|
||||
|
||||
effect(self.object:get_pos(), 25, "mobs_protect_particle.png",
|
||||
@ -4547,8 +4688,11 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
|
||||
-- make children grow quicker
|
||||
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
|
||||
end
|
||||
|
||||
@ -4586,12 +4730,12 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
|
||||
end
|
||||
|
||||
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 item:get_name() == "mobs:nametag"
|
||||
and clicker:get_player_name() == self.owner then
|
||||
|
||||
local name = clicker:get_player_name()
|
||||
and (name == self.owner
|
||||
or minetest.check_player_privs(name, "protection_bypass")) then
|
||||
|
||||
-- store mob and nametag stack in external variables
|
||||
mob_obj[name] = self
|
||||
|
25
api.txt
25
api.txt
@ -64,8 +64,12 @@ functions needed for the mob to work properly which contains the following:
|
||||
'fall_damage' when true causes falling to inflict damage.
|
||||
'water_damage' holds the damage per second infliced to mobs when standing in
|
||||
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
|
||||
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
|
||||
level is between the min and max values below
|
||||
'light_damage_min' minimum light value when mob is affected (default: 14)
|
||||
@ -75,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.
|
||||
'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
|
||||
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
|
||||
nearby, default is 3 nodes.
|
||||
@ -130,6 +134,8 @@ functions needed for the mob to work properly which contains the following:
|
||||
e.g. {"player", "mobs_animal:chicken"}.
|
||||
'runaway_from' contains a table with mob names to run away from, add
|
||||
"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
|
||||
mob is hit.
|
||||
'blood_texture' has the texture name to use for droplets e.g.
|
||||
@ -226,6 +232,7 @@ functions needed for the mob to work properly which contains the following:
|
||||
'die_end'
|
||||
'die_speed'
|
||||
'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
|
||||
looping.
|
||||
@ -298,6 +305,9 @@ enhance mob functionality and have them do many interesting things:
|
||||
is returned normal attack function continued.
|
||||
'on_die' a function that is called when mob is killed (self, pos), also
|
||||
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
|
||||
active and which has access to all of the self.* variables
|
||||
e.g. (self.health for health or self.standing_in for node
|
||||
@ -329,6 +339,14 @@ for each mob.
|
||||
'self.order' set to "follow" or "stand" so that npc will follow owner
|
||||
or stand it's ground
|
||||
'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
|
||||
@ -697,6 +715,9 @@ External Settings for "minetest.conf"
|
||||
function.
|
||||
'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_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
|
||||
to their minetest.conf file with a new value, the lower the value the more each
|
||||
|
22
crafts.lua
22
crafts.lua
@ -5,7 +5,7 @@ local S = mobs.intllib
|
||||
minetest.register_craftitem("mobs:nametag", {
|
||||
description = S("Name Tag"),
|
||||
inventory_image = "mobs_nametag.png",
|
||||
groups = {flammable = 2}
|
||||
groups = {flammable = 2, nametag = 1}
|
||||
})
|
||||
|
||||
if minetest.get_modpath("dye") and minetest.get_modpath("farming") then
|
||||
@ -20,7 +20,7 @@ end
|
||||
minetest.register_craftitem("mobs:leather", {
|
||||
description = S("Leather"),
|
||||
inventory_image = "mobs_leather.png",
|
||||
groups = {flammable = 2}
|
||||
groups = {flammable = 2, leather = 1}
|
||||
})
|
||||
|
||||
-- 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
|
||||
minetest.register_craftitem("mobs:saddle", {
|
||||
description = S("Saddle"),
|
||||
inventory_image = "mobs_saddle.png",
|
||||
groups = {flammable = 2}
|
||||
groups = {flammable = 2, saddle = 1}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
|
6
init.lua
6
init.lua
@ -1,6 +1,12 @@
|
||||
|
||||
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
|
||||
dofile(path .. "/api.lua")
|
||||
|
||||
|
3
mod.conf
3
mod.conf
@ -1 +1,4 @@
|
||||
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.
|
||||
|
191
mount.lua
191
mount.lua
@ -1,9 +1,7 @@
|
||||
|
||||
-- lib_mount by Blert2112 (edited by TenPlus1)
|
||||
|
||||
local enable_crash = false
|
||||
local crash_threshold = 6.5 -- ignored if enable_crash=false
|
||||
|
||||
local abs, cos, floor, sin, sqrt, pi =
|
||||
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)
|
||||
|
||||
fallback = fallback or mobs.fallback_node
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
@ -25,7 +22,6 @@ end
|
||||
|
||||
|
||||
local function node_is(pos)
|
||||
|
||||
local node = node_ok(pos)
|
||||
|
||||
if node.name == "air" then
|
||||
@ -49,33 +45,30 @@ end
|
||||
|
||||
|
||||
local function get_sign(i)
|
||||
|
||||
i = i or 0
|
||||
|
||||
if i == 0 then
|
||||
return 0
|
||||
else
|
||||
return i / math.abs(i)
|
||||
return i / abs(i)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_velocity(v, yaw, y)
|
||||
|
||||
local x = -math.sin(yaw) * v
|
||||
local z = math.cos(yaw) * v
|
||||
local x = -sin(yaw) * v
|
||||
local z = cos(yaw) * v
|
||||
|
||||
return {x = x, y = y, z = z}
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
local function force_detach(player)
|
||||
|
||||
local attached_to = player:get_attach()
|
||||
|
||||
if not attached_to then
|
||||
@ -86,21 +79,19 @@ local function force_detach(player)
|
||||
|
||||
if entity and entity.driver
|
||||
and entity.driver == player then
|
||||
|
||||
entity.driver = nil
|
||||
end
|
||||
|
||||
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})
|
||||
default.player_set_animation(player, "stand" , 30)
|
||||
player_api.set_animation(player, "stand", 30)
|
||||
player:set_properties({visual_size = {x = 1, y = 1}})
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
force_detach(player)
|
||||
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)
|
||||
|
||||
local attach_at, eye_offset = {}, {}
|
||||
|
||||
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_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
|
||||
|
||||
if entity.player_rotation.y == 90 then
|
||||
rot_view = math.pi / 2
|
||||
rot_view = pi / 2
|
||||
end
|
||||
|
||||
attach_at = entity.driver_attach_at
|
||||
eye_offset = entity.driver_eye_offset
|
||||
local attach_at = entity.driver_attach_at
|
||||
local eye_offset = entity.driver_eye_offset
|
||||
entity.driver = player
|
||||
|
||||
force_detach(player)
|
||||
|
||||
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_properties({
|
||||
@ -151,46 +169,36 @@ function mobs.attach(entity, player)
|
||||
}
|
||||
})
|
||||
|
||||
minetest.after(0.2, function(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
default.player_set_animation(player, "sit" , 30)
|
||||
minetest.after(0.2, function()
|
||||
if player and player:is_player() then
|
||||
player_api.set_animation(player, "sit", 30)
|
||||
end
|
||||
end, player:get_player_name())
|
||||
end)
|
||||
|
||||
player:set_look_horizontal(entity.object:get_yaw() - rot_view)
|
||||
end
|
||||
|
||||
|
||||
function mobs.detach(player, offset)
|
||||
|
||||
function mobs.detach(player)
|
||||
force_detach(player)
|
||||
|
||||
default.player_set_animation(player, "stand" , 30)
|
||||
|
||||
local pos = player:get_pos()
|
||||
|
||||
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
|
||||
minetest.after(0.1, function()
|
||||
if player and player:is_player() then
|
||||
local pos = find_free_pos(player:get_pos())
|
||||
pos.y = pos.y + 0.5
|
||||
player:set_pos(pos)
|
||||
end
|
||||
end, player:get_player_name(), pos)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
rot_steer, rot_view = 0, math.pi/2
|
||||
rot_view = pi / 2
|
||||
end
|
||||
|
||||
local acce_y = 0
|
||||
@ -200,19 +208,14 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
|
||||
-- process controls
|
||||
if entity.driver then
|
||||
|
||||
--print ("---velo", get_v(velo))
|
||||
|
||||
local ctrl = entity.driver:get_player_control()
|
||||
|
||||
-- move forwards
|
||||
if ctrl.up then
|
||||
|
||||
entity.v = entity.v + entity.accel / 10
|
||||
|
||||
-- move backwards
|
||||
elseif ctrl.down then
|
||||
|
||||
if entity.max_speed_reverse == 0 and entity.v == 0 then
|
||||
return
|
||||
end
|
||||
@ -220,12 +223,24 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
entity.v = entity.v - entity.accel / 10
|
||||
end
|
||||
|
||||
-- fix mob rotation
|
||||
local horz = entity.driver:get_look_horizontal() or 0
|
||||
-- mob rotation
|
||||
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)
|
||||
|
||||
if can_fly then
|
||||
|
||||
-- fly up
|
||||
if ctrl.jump then
|
||||
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
|
||||
if velo.y > 0 then velo.y = 0 end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- jump
|
||||
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 entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||
|
||||
if stand_anim then
|
||||
mobs:set_animation(entity, stand_anim)
|
||||
end
|
||||
@ -280,7 +292,6 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
entity.v = entity.v - 0.02 * s
|
||||
|
||||
if s ~= get_sign(entity.v) then
|
||||
|
||||
entity.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
entity.v = 0
|
||||
return
|
||||
@ -293,14 +304,15 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
max_spd = entity.max_speed_forward
|
||||
end
|
||||
|
||||
if math.abs(entity.v) > max_spd then
|
||||
if abs(entity.v) > max_spd then
|
||||
entity.v = entity.v - get_sign(entity.v)
|
||||
end
|
||||
|
||||
-- Set position, velocity and acceleration
|
||||
local p = entity.object:get_pos()
|
||||
local new_velo
|
||||
local new_acce = {x = 0, y = -9.8, z = 0}
|
||||
if not p then return end
|
||||
|
||||
local new_acce = {x = 0, y = -9.81, z = 0}
|
||||
|
||||
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
|
||||
|
||||
if ni == "air" then
|
||||
|
||||
if can_fly == true then
|
||||
new_acce.y = 0
|
||||
end
|
||||
|
||||
elseif ni == "liquid" or ni == "lava" then
|
||||
|
||||
if ni == "lava" and entity.lava_damage ~= 0 then
|
||||
|
||||
entity.lava_counter = (entity.lava_counter or 0) + dtime
|
||||
|
||||
if entity.lava_counter > 1 then
|
||||
|
||||
minetest.sound_play("default_punch", {
|
||||
object = entity.object,
|
||||
max_hear_distance = 5
|
||||
}, true)
|
||||
})
|
||||
|
||||
entity.object:punch(entity.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
@ -335,14 +342,12 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
end
|
||||
end
|
||||
|
||||
if entity.terrain_type == 2
|
||||
or entity.terrain_type == 3 then
|
||||
|
||||
local terrain_type = entity.terrain_type
|
||||
if terrain_type == 2 or terrain_type == 3 then
|
||||
new_acce.y = 0
|
||||
p.y = p.y + 1
|
||||
|
||||
if node_is(p) == "liquid" then
|
||||
|
||||
if velo.y >= 5 then
|
||||
velo.y = 5
|
||||
elseif velo.y < 0 then
|
||||
@ -351,9 +356,11 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
new_acce.y = 5
|
||||
end
|
||||
else
|
||||
if math.abs(velo.y) < 1 then
|
||||
if abs(velo.y) < 1 then
|
||||
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)
|
||||
velo.y = 0
|
||||
end
|
||||
@ -363,46 +370,22 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
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
|
||||
|
||||
entity.object:set_velocity(new_velo)
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
-- directional flying routine by D00Med (edited by TenPlus1)
|
||||
|
||||
function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
||||
|
||||
function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim)
|
||||
local ctrl = entity.driver:get_player_control()
|
||||
local velo = entity.object:get_velocity()
|
||||
local dir = entity.driver:get_look_dir()
|
||||
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
|
||||
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})
|
||||
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
|
||||
if ctrl.LMB and ctrl.sneak and shoots then
|
||||
|
||||
local pos = entity.object:get_pos()
|
||||
local obj = minetest.add_entity({
|
||||
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.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 yaw = entity.driver:get_look_horizontal()
|
||||
obj:set_yaw(yaw + math.pi / 2)
|
||||
yaw = entity.driver:get_look_horizontal()
|
||||
obj:set_yaw(yaw + pi / 2)
|
||||
obj:set_velocity(vec)
|
||||
else
|
||||
obj:remove()
|
||||
@ -448,7 +430,6 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
||||
|
||||
-- change animation if stopped
|
||||
if velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||
|
||||
mobs:set_animation(entity, stand_anim)
|
||||
else
|
||||
-- moving animation
|
||||
|
@ -23,6 +23,8 @@ Lucky Blocks: 9
|
||||
|
||||
|
||||
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.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
|
||||
|
@ -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)
|
||||
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
|
||||
|
13
spawner.lua
13
spawner.lua
@ -23,9 +23,9 @@ minetest.register_node("mobs:spawner", {
|
||||
|
||||
-- text entry formspec
|
||||
meta:set_string("formspec",
|
||||
"size[9,3.5]"
|
||||
"size[10,3.5]"
|
||||
.. "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}]")
|
||||
|
||||
meta:set_string("infotext", S("Spawner Not Active (enter settings)"))
|
||||
@ -162,10 +162,17 @@ minetest.register_abm({
|
||||
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
|
||||
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}, {"air"})
|
||||
{x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, reg)
|
||||
|
||||
-- spawn in random air block
|
||||
if air and #air > 0 then
|
||||
|
BIN
textures/mobs_protector2.png
Normal file
BIN
textures/mobs_protector2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 164 B |
Reference in New Issue
Block a user