Mobs_redo: update and add lib_mount
@ -21,11 +21,6 @@ Copyright (C) 2019-2020 MultiCraft Development Team
|
||||
Graphics in this mod is NOT free and can be used only as part of the official MultiCraft build.
|
||||
Allowed to be used in non-official builds ONLY for personal use.
|
||||
|
||||
* Exceptions:
|
||||
mobs_cobweb.png, mobs_nametag.png, mobs_rotten_flesh.png, mobs_shears.png
|
||||
Copyright (C) 2013 Vattic
|
||||
https://github.com/MultiCraft/MultiCraft/blob/master/doc/Other%20License.md#license-of-media-textures
|
||||
|
||||
License of sounds
|
||||
-----------------
|
||||
Creative Commons sounds from Freesound.org
|
||||
|
@ -1,6 +1,6 @@
|
||||
mobs = {
|
||||
mod = "redo",
|
||||
version = "20200916",
|
||||
version = "20201003",
|
||||
invis = minetest.global_exists("invisibility") and invisibility or {}
|
||||
}
|
||||
|
||||
@ -16,13 +16,7 @@ end
|
||||
|
||||
local S = mobs.S
|
||||
|
||||
-- creative check
|
||||
local creative = minetest.settings:get_bool("creative_mode")
|
||||
function mobs.is_creative(name)
|
||||
return creative or minetest.check_player_privs(name, {creative = true})
|
||||
end
|
||||
|
||||
-- localize math functions
|
||||
-- localize functions
|
||||
local abs = math.abs
|
||||
local atan = function(x)
|
||||
return x and math.atan(x) or 0
|
||||
@ -30,11 +24,6 @@ end
|
||||
local ceil = math.ceil
|
||||
local cos = math.cos
|
||||
local floor = math.floor
|
||||
local add_vector = vector.add
|
||||
local get_distance = vector.distance
|
||||
local get_direction = vector.direction
|
||||
local multiply = vector.multiply
|
||||
local subtract = vector.subtract
|
||||
local min = math.min
|
||||
local max = math.max
|
||||
local pi = math.pi
|
||||
@ -42,22 +31,36 @@ local rad = math.rad
|
||||
local random = math.random
|
||||
local sin = math.sin
|
||||
local square = math.sqrt
|
||||
local vadd = vector.add
|
||||
local vdistance = vector.distance
|
||||
local vdirection = vector.direction
|
||||
local vmultiply = vector.multiply
|
||||
local vsubtract = vector.subtract
|
||||
local table_copy = table.copy
|
||||
local table_remove = table.remove
|
||||
local upper = string.upper
|
||||
local settings = minetest.settings
|
||||
|
||||
-- creative check
|
||||
local creative = settings:get_bool("creative_mode")
|
||||
function mobs.is_creative(name)
|
||||
return creative or minetest.check_player_privs(name, {creative = true})
|
||||
end
|
||||
|
||||
-- Load settings
|
||||
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
||||
local mobs_spawn = true
|
||||
local damage_enabled = settings:get_bool("enable_damage")
|
||||
local mobs_spawn = settings:get_bool("mobs_spawn") ~= false
|
||||
local peaceful_only = false
|
||||
local disable_blood = true
|
||||
local mobs_griefing = true
|
||||
local spawn_protected = true
|
||||
local spawn_protected = settings:get_bool("mobs_spawn_protected") ~= false
|
||||
local remove_far = false
|
||||
local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0
|
||||
local show_health = true
|
||||
local max_per_block = tonumber(minetest.settings:get("max_objects_per_block"))
|
||||
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"))
|
||||
local singleplayer = minetest.is_singleplayer()
|
||||
local lifetime = 900 -- 15 min
|
||||
local active_limit = tonumber(minetest.settings:get("mob_active_limit")) or 0
|
||||
local active_limit = tonumber(settings:get("mob_active_limit")) or 0
|
||||
local active_mobs = 0
|
||||
local spawn_interval = 20
|
||||
|
||||
@ -150,6 +153,7 @@ local mob_class = {
|
||||
facing_fence = false,
|
||||
obj_is_mob = true
|
||||
}
|
||||
|
||||
local mob_class_meta = {__index = mob_class}
|
||||
|
||||
-- play sound
|
||||
@ -347,7 +351,8 @@ function mob_class:set_animation(anim, force)
|
||||
x = self.animation[anim .. "_start"],
|
||||
y = self.animation[anim .. "_end"]
|
||||
},
|
||||
self.animation[anim .. "_speed"] or self.animation.speed_normal or 15,
|
||||
self.animation[anim .. "_speed"] or
|
||||
self.animation.speed_normal or 15,
|
||||
0, self.animation[anim .. "_loop"] ~= false)
|
||||
end
|
||||
|
||||
@ -377,7 +382,7 @@ local line_of_sight = function(self, pos1, pos2, stepsize)
|
||||
local nn = minetest.get_node(pos).name
|
||||
|
||||
-- Target Distance (td) to travel
|
||||
local td = get_distance(pos1, pos2)
|
||||
local td = vdistance(pos1, pos2)
|
||||
|
||||
-- Actual Distance (ad) traveled
|
||||
local ad = 0
|
||||
@ -393,7 +398,7 @@ local line_of_sight = function(self, pos1, pos2, stepsize)
|
||||
end
|
||||
|
||||
-- Moves the analyzed pos
|
||||
local d = get_distance(pos1, pos2)
|
||||
local d = vdistance(pos1, pos2)
|
||||
|
||||
npos1.x = ((pos2.x - pos1.x) / d * stepsize) + pos1.x
|
||||
npos1.y = ((pos2.y - pos1.y) / d * stepsize) + pos1.y
|
||||
@ -427,7 +432,7 @@ local new_line_of_sight = function(self, pos1, pos2, stepsize)
|
||||
if not pos1 or not pos2 then return end
|
||||
|
||||
stepsize = stepsize or 1
|
||||
local stepv = multiply(get_direction(pos1, pos2), stepsize)
|
||||
local stepv = vmultiply(vdirection(pos1, pos2), stepsize)
|
||||
local s, pos = minetest.line_of_sight(pos1, pos2, stepsize)
|
||||
|
||||
-- normal walking and flying mobs can see you through air
|
||||
@ -448,9 +453,9 @@ local new_line_of_sight = function(self, pos1, pos2, stepsize)
|
||||
while minetest.registered_nodes[nn]
|
||||
and not minetest.registered_nodes[nn].walkable do
|
||||
|
||||
npos1 = add_vector(npos1, stepv)
|
||||
npos1 = vadd(npos1, stepv)
|
||||
|
||||
if get_distance(npos1, pos2) < stepsize then return true end
|
||||
if vdistance(npos1, pos2) < stepsize then return true end
|
||||
|
||||
-- scan again
|
||||
r, pos = minetest.line_of_sight(npos1, pos2, stepsize)
|
||||
@ -518,10 +523,10 @@ function mob_class:attempt_flight_correction(override)
|
||||
end
|
||||
|
||||
local escape_target = flyable_nodes[random(#flyable_nodes)]
|
||||
local escape_direction = get_direction(pos, escape_target)
|
||||
local escape_direction = vdirection(pos, escape_target)
|
||||
|
||||
self.object:set_velocity(
|
||||
vector.multiply(escape_direction, 1)) --self.run_velocity))
|
||||
vmultiply(escape_direction, 1)) --self.run_velocity))
|
||||
|
||||
return true
|
||||
end
|
||||
@ -576,13 +581,10 @@ function mob_class:do_stay_near()
|
||||
if not self.stay_near then return false end
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
if not pos then
|
||||
return false
|
||||
end
|
||||
local searchnodes = self.stay_near[1]
|
||||
local chance = self.stay_near[2] or 10
|
||||
|
||||
if random(chance) > 1 then
|
||||
if not pos or random(chance) > 1 then
|
||||
return false
|
||||
end
|
||||
|
||||
@ -1144,7 +1146,7 @@ local entity_physics = function(pos, radius)
|
||||
|
||||
for n = 1, #objs do
|
||||
obj_pos = objs[n]:get_pos()
|
||||
dist = get_distance(pos, obj_pos)
|
||||
dist = vdistance(pos, obj_pos)
|
||||
|
||||
if dist < 1 then dist = 1 end
|
||||
|
||||
@ -1484,7 +1486,7 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
||||
end, self)
|
||||
end
|
||||
|
||||
if abs(subtract(s, target_pos).y) > self.stepheight then
|
||||
if abs(vsubtract(s, target_pos).y) > self.stepheight then
|
||||
if height_switcher then
|
||||
use_pathfind = true
|
||||
height_switcher = false
|
||||
@ -1722,7 +1724,7 @@ function mob_class:general_attack()
|
||||
p = player:get_pos()
|
||||
sp = s
|
||||
|
||||
dist = get_distance(p, s)
|
||||
dist = vdistance(p, s)
|
||||
|
||||
-- aim higher to make looking up hills more realistic
|
||||
p.y = p.y + 1
|
||||
@ -1751,9 +1753,7 @@ function mob_class:do_runaway_from()
|
||||
end
|
||||
|
||||
local s = self.object:get_pos()
|
||||
if not s then
|
||||
return
|
||||
end
|
||||
if not s then return end
|
||||
|
||||
local p, sp, dist, pname
|
||||
local player, obj, min_player, name
|
||||
@ -1791,7 +1791,7 @@ function mob_class:do_runaway_from()
|
||||
p.y = p.y + 1
|
||||
sp.y = sp.y + 1
|
||||
|
||||
dist = get_distance(p, s)
|
||||
dist = vdistance(p, s)
|
||||
|
||||
-- choose closest player/mob to runaway from
|
||||
if dist < min_dist
|
||||
@ -1834,7 +1834,7 @@ function mob_class:follow_flop()
|
||||
end
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player and not mobs.invis[name] and
|
||||
get_distance(player:get_pos(), s) < self.view_range then
|
||||
vdistance(player:get_pos(), s) < self.view_range then
|
||||
self.following = player
|
||||
break
|
||||
end
|
||||
@ -1872,7 +1872,7 @@ function mob_class:follow_flop()
|
||||
end
|
||||
|
||||
if p then
|
||||
local dist = get_distance(p, s)
|
||||
local dist = vdistance(p, s)
|
||||
|
||||
-- dont follow if out of range
|
||||
if dist > self.view_range then
|
||||
@ -2073,8 +2073,8 @@ function mob_class:do_states(dtime)
|
||||
elseif self.state == "attack" then
|
||||
-- get mob and enemy positions and distance between
|
||||
local s = self.object:get_pos()
|
||||
local p = self.attack:get_pos()
|
||||
local dist = p and get_distance(p, s) or 500
|
||||
local p = self.attack and self.attack:get_pos()
|
||||
local dist = p and vdistance(p, s) or 500
|
||||
|
||||
-- stop attacking if player out of range or invisible
|
||||
if dist > self.view_range
|
||||
@ -2766,7 +2766,8 @@ 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
|
||||
@ -2839,7 +2840,7 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
||||
-- Armor groups (immortal = 1 for custom damage handling)
|
||||
local armor
|
||||
if type(self.armor) == "table" then
|
||||
armor = table.copy(self.armor)
|
||||
armor = table_copy(self.armor)
|
||||
armor.immortal = 1
|
||||
else
|
||||
armor = {immortal = 1, fleshy = self.armor}
|
||||
@ -2995,12 +2996,12 @@ function mob_class:on_step(dtime)
|
||||
dif = 2 * pi - dif -- need to add
|
||||
yaw = yaw + dif / self.delay
|
||||
else
|
||||
yaw = yaw - dif / self.delay -- need to subtract
|
||||
yaw = yaw - dif / self.delay -- need to vsubtract
|
||||
end
|
||||
elseif yaw < self.target_yaw then
|
||||
if dif > pi then
|
||||
dif = 2 * pi - dif
|
||||
yaw = yaw - dif / self.delay -- need to subtract
|
||||
yaw = yaw - dif / self.delay -- need to vsubtract
|
||||
else
|
||||
yaw = yaw + dif / self.delay -- need to add
|
||||
end
|
||||
@ -3238,15 +3239,15 @@ function mobs:spawn_abm_check(pos, node, name)
|
||||
end
|
||||
|
||||
|
||||
function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
|
||||
interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
||||
function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
|
||||
chance, aoc, min_height, max_height, day_toggle, on_spawn, map_load)
|
||||
-- Do mobs spawn at all?
|
||||
if not mobs_spawn or not mobs.spawning_mobs[name] then
|
||||
return
|
||||
end
|
||||
|
||||
-- chance/spawn number override in minetest.conf for registered mob
|
||||
local numbers = minetest.settings:get(name)
|
||||
local numbers = settings:get(name)
|
||||
|
||||
if numbers then
|
||||
numbers = numbers:split(",")
|
||||
@ -3265,127 +3266,160 @@ interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
||||
|
||||
mobs.spawning_mobs[name].aoc = aoc
|
||||
|
||||
minetest.register_abm({
|
||||
label = name .. " spawning",
|
||||
nodenames = nodes,
|
||||
neighbors = neighbors,
|
||||
interval = interval,
|
||||
chance = max(1, chance),
|
||||
catch_up = false,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
-- is mob actually registered?
|
||||
if not mobs.spawning_mobs[name]
|
||||
or not minetest.registered_entities[name] then
|
||||
-- print ("--- mob doesn't exist", name)
|
||||
return
|
||||
end
|
||||
local spawn_action = function(pos, node, active_object_count,
|
||||
active_object_count_wider)
|
||||
|
||||
-- are we over active mob limit
|
||||
if active_limit > 0 and active_mobs >= active_limit then
|
||||
-- use instead of abm's chance setting when using lbm
|
||||
if map_load and random(max(1, chance)) > 1 then
|
||||
return
|
||||
end
|
||||
|
||||
-- use instead of abm's neighbor setting when using lbm
|
||||
if map_load and not minetest.find_node_near(pos, 1, neighbors) then
|
||||
--print("--- lbm neighbors not found")
|
||||
return
|
||||
end
|
||||
|
||||
-- is mob actually registered?
|
||||
if not mobs.spawning_mobs[name]
|
||||
or not minetest.registered_entities[name] then
|
||||
-- print ("--- mob doesn't exist", name)
|
||||
return
|
||||
end
|
||||
|
||||
-- are we over active mob limit
|
||||
if active_limit > 0 and active_mobs >= active_limit then
|
||||
--print("--- active mob limit reached", active_mobs, active_limit)
|
||||
return
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- additional custom checks for spawning mob
|
||||
if mobs:spawn_abm_check(pos, node, name) then
|
||||
return
|
||||
end
|
||||
-- additional custom checks for spawning mob
|
||||
if mobs:spawn_abm_check(pos, node, name) then
|
||||
return
|
||||
end
|
||||
|
||||
-- do not spawn if too many entities in area
|
||||
if active_object_count_wider >= max_per_block then
|
||||
-- print("--- too many entities in area", active_object_count_wider)
|
||||
return
|
||||
end
|
||||
-- do not spawn if too many entities in area
|
||||
if active_object_count_wider
|
||||
and active_object_count_wider >= max_per_block then
|
||||
-- print("--- too many entities in area", active_object_count_wider)
|
||||
return
|
||||
end
|
||||
|
||||
-- get total number of this mob in area
|
||||
local num_mob, is_pla = count_mobs(pos, name)
|
||||
-- get total number of this mob in area
|
||||
local num_mob, is_pla = count_mobs(pos, name)
|
||||
|
||||
if not is_pla then
|
||||
-- print ("--- no players within active area, will not spawn " .. name)
|
||||
return
|
||||
end
|
||||
if not is_pla then
|
||||
-- print ("--- no players within active area, will not spawn " .. name)
|
||||
return
|
||||
end
|
||||
|
||||
if num_mob >= aoc then
|
||||
-- print ("--- too many " .. name .. " in area", num_mob .. "/" .. aoc)
|
||||
return
|
||||
end
|
||||
if num_mob >= aoc then
|
||||
-- print ("--- too many " .. name .. " in area", num_mob .. "/" .. aoc)
|
||||
return
|
||||
end
|
||||
|
||||
-- mobs cannot spawn in protected areas when enabled
|
||||
if spawn_protected
|
||||
and minetest.is_protected(pos, "") then
|
||||
-- print ("--- inside protected area", name)
|
||||
return
|
||||
end
|
||||
-- mobs cannot spawn in protected areas when enabled
|
||||
if spawn_protected
|
||||
and minetest.is_protected(pos, "") then
|
||||
-- print ("--- inside protected area", name)
|
||||
return
|
||||
end
|
||||
|
||||
-- if toggle set to nil then ignore day/night check
|
||||
if day_toggle ~= nil then
|
||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||
-- if toggle set to nil then ignore day/night check
|
||||
if day_toggle ~= nil then
|
||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||
|
||||
if tod > 4500 and tod < 19500 then
|
||||
-- daylight, but mob wants night
|
||||
if not day_toggle then
|
||||
-- print ("--- mob needs night", name)
|
||||
return
|
||||
end
|
||||
else
|
||||
-- night time but mob wants day
|
||||
if day_toggle then
|
||||
-- print ("--- mob needs day", name)
|
||||
return
|
||||
end
|
||||
if tod > 4500 and tod < 19500 then
|
||||
-- daylight, but mob wants night
|
||||
if not day_toggle then
|
||||
-- print ("--- mob needs night", name)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- spawn above node
|
||||
pos.y = pos.y + 1
|
||||
|
||||
-- are we spawning within height limits?
|
||||
if pos.y > max_height
|
||||
or pos.y < min_height then
|
||||
-- print ("--- height limits not met", name, pos.y)
|
||||
return
|
||||
end
|
||||
|
||||
-- are light levels ok?
|
||||
if min_light ~= 0 or max_light ~= 15 then
|
||||
local light = minetest.get_node_light(pos)
|
||||
if not light
|
||||
or light > max_light
|
||||
or light < min_light then
|
||||
-- print ("--- light limits not met", name, light)
|
||||
else
|
||||
-- night time but mob wants day
|
||||
if day_toggle then
|
||||
-- print ("--- mob needs day", name)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- do we have enough height clearance to spawn mob?
|
||||
local ent = minetest.registered_entities[name]
|
||||
local height = max(1, ceil(
|
||||
(ent.collisionbox[5] or 0.25) -
|
||||
(ent.collisionbox[2] or -0.25) - 1))
|
||||
-- spawn above node
|
||||
pos.y = pos.y + 1
|
||||
|
||||
for n = 0, height do
|
||||
local pos2 = {x = pos.x, y = pos.y + n, z = pos.z}
|
||||
-- are we spawning within height limits?
|
||||
if pos.y > max_height
|
||||
or pos.y < min_height then
|
||||
-- print ("--- height limits not met", name, pos.y)
|
||||
return
|
||||
end
|
||||
|
||||
if minetest.registered_nodes[node_ok(pos2).name].walkable then
|
||||
-- print ("--- inside block", name, node_ok(pos2).name)
|
||||
return
|
||||
end
|
||||
-- are light levels ok?
|
||||
if min_light ~= 0 or max_light ~= 15 then
|
||||
local light = minetest.get_node_light(pos)
|
||||
if not light
|
||||
or light > max_light
|
||||
or light < min_light then
|
||||
-- print ("--- light limits not met", name, light)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- spawn mob 1/2 node above ground
|
||||
pos.y = pos.y + 0.5
|
||||
local mob = minetest.add_entity(pos, name)
|
||||
-- do we have enough height clearance to spawn mob?
|
||||
local ent = minetest.registered_entities[name]
|
||||
local height = max(1, ceil(
|
||||
(ent.collisionbox[5] or 0.25) -
|
||||
(ent.collisionbox[2] or -0.25) - 1))
|
||||
|
||||
for n = 0, height do
|
||||
local pos2 = {x = pos.x, y = pos.y + n, z = pos.z}
|
||||
|
||||
if minetest.registered_nodes[node_ok(pos2).name].walkable then
|
||||
-- print ("--- inside block", name, node_ok(pos2).name)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- spawn mob 1/2 node above ground
|
||||
pos.y = pos.y + 0.5
|
||||
local mob = minetest.add_entity(pos, name)
|
||||
|
||||
-- print("[mobs] Spawned " .. name .. " at "
|
||||
-- .. minetest.pos_to_string(pos) .. " on "
|
||||
-- .. node.name .. " near " .. neighbors[1])
|
||||
|
||||
if on_spawn then
|
||||
local ent = mob:get_luaentity()
|
||||
on_spawn(ent, pos)
|
||||
end
|
||||
if on_spawn then
|
||||
on_spawn(mob:get_luaentity(), pos)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- are we registering an abm or lbm?
|
||||
if map_load then
|
||||
minetest.register_lbm({
|
||||
name = name .. "_spawning",
|
||||
label = name .. " spawning",
|
||||
nodenames = nodes,
|
||||
run_at_every_load = false,
|
||||
|
||||
action = function(pos, node)
|
||||
spawn_action(pos, node)
|
||||
end
|
||||
})
|
||||
else
|
||||
minetest.register_abm({
|
||||
label = name .. " spawning",
|
||||
nodenames = nodes,
|
||||
neighbors = neighbors,
|
||||
interval = interval,
|
||||
chance = max(1, chance),
|
||||
catch_up = false,
|
||||
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
spawn_action(pos, node, active_object_count, active_object_count_wider)
|
||||
end
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -3403,7 +3437,8 @@ function mobs:spawn(def)
|
||||
def.min_height or -31000,
|
||||
def.max_height or 31000,
|
||||
def.day_toggle,
|
||||
def.on_spawn)
|
||||
def.on_spawn,
|
||||
def.on_map_load)
|
||||
end
|
||||
|
||||
|
||||
@ -3506,8 +3541,7 @@ end
|
||||
|
||||
-- compatibility function
|
||||
function mobs:explosion(pos, radius)
|
||||
local self = {sounds = {explode = "tnt_explode"}}
|
||||
mobs:boom(self, pos, radius)
|
||||
mobs:boom({sounds = {explode = "tnt_explode"}}, pos, radius)
|
||||
end
|
||||
|
||||
|
||||
@ -3537,7 +3571,8 @@ function mobs:boom(self, pos, radius)
|
||||
else
|
||||
mobs:safe_boom(self, pos, radius)
|
||||
end
|
||||
end]]
|
||||
end
|
||||
]]
|
||||
|
||||
-- Mob spawning, returns true on successful placement
|
||||
local function spawn_mob(pos, mob, data, placer, drop)
|
||||
@ -3645,7 +3680,7 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
|
||||
|
||||
-- register new spawn egg containing mob information
|
||||
minetest.register_craftitem(mob .. "_set", {
|
||||
description = desc .. " " .. S"(Tamed)",
|
||||
description = S("@1 (Tamed)", desc),
|
||||
inventory_image = invimg,
|
||||
groups = {spawn_egg = 2, not_in_creative_inventory = 1},
|
||||
stack_max = 1,
|
||||
@ -3657,7 +3692,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
|
||||
local under = minetest.get_node(pointed_thing.under)
|
||||
local def = minetest.registered_nodes[under.name]
|
||||
if def and def.on_rightclick then
|
||||
return def.on_rightclick(pointed_thing.under, under, placer, itemstack)
|
||||
return def.on_rightclick(
|
||||
pointed_thing.under, under, placer, itemstack)
|
||||
end
|
||||
|
||||
local mob = itemstack:get_name():gsub("_set$", "")
|
||||
@ -3684,7 +3720,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
|
||||
local under = minetest.get_node(pointed_thing.under)
|
||||
local def = minetest.registered_nodes[under.name]
|
||||
if def and def.on_rightclick then
|
||||
return def.on_rightclick(pointed_thing.under, under, placer, itemstack)
|
||||
return def.on_rightclick(
|
||||
pointed_thing.under, under, placer, itemstack)
|
||||
end
|
||||
|
||||
local mob = itemstack:get_name():gsub("_set$", "")
|
||||
@ -3700,9 +3737,9 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
-- force capture a mob if space available in inventory, or drop as spawn egg
|
||||
--[[function mobs:force_capture(self, clicker)
|
||||
function mobs:force_capture(self, clicker)
|
||||
-- add special mob egg with all mob information
|
||||
local new_stack = ItemStack(self.name .. "_set")
|
||||
local tmp, t = {}
|
||||
@ -3839,7 +3876,8 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net,
|
||||
end
|
||||
end
|
||||
return true
|
||||
end]]
|
||||
end
|
||||
]]
|
||||
|
||||
|
||||
local mob_obj = {}
|
||||
@ -3847,10 +3885,12 @@ local mob_sta = {}
|
||||
|
||||
-- feeding, taming and breeding (thanks blert2112)
|
||||
function mobs:feed_tame(self, clicker, feed_count, breed, tame)
|
||||
local name = clicker and clicker:get_player_name() or ""
|
||||
|
||||
-- can eat/tame with item in hand
|
||||
if self.follow and self:follow_holding(clicker) then
|
||||
-- if not in creative then take item
|
||||
if not mobs.is_creative(clicker:get_player_name()) then
|
||||
if not mobs.is_creative(name) then
|
||||
local item = clicker:get_wielded_item()
|
||||
item:take_item()
|
||||
clicker:set_wielded_item(item)
|
||||
@ -3863,8 +3903,8 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
|
||||
self.health = self.hp_max
|
||||
|
||||
if self.htimer < 1 then
|
||||
local mob_name = self.name:split(":")[2]:split("_")[1]:gsub("^%l", string.upper)
|
||||
minetest.chat_send_player(clicker:get_player_name(),
|
||||
local mob_name = self.name:split(":")[2]:split("_")[1]:gsub("^%l", upper)
|
||||
minetest.chat_send_player(name,
|
||||
S("\"@1\" at full health: @2",
|
||||
S(mob_name), tostring(self.health)))
|
||||
self.htimer = 5
|
||||
@ -3893,19 +3933,17 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
|
||||
|
||||
if tame then
|
||||
if self.tamed == false then
|
||||
local mob_name = self.name:split(":")[2]:split("_")[1]:gsub("^%l", string.upper)
|
||||
minetest.chat_send_player(clicker:get_player_name(),
|
||||
S("\"@1\" has been tamed!",
|
||||
S(mob_name)))
|
||||
local mob_name = self.name:split(":")[2]:split("_")[1]:gsub("^%l", upper)
|
||||
minetest.chat_send_player(name,
|
||||
S("\"@1\" has been tamed!", S(mob_name)))
|
||||
self:mob_sound("mobs_spell")
|
||||
end
|
||||
|
||||
self.tamed = true
|
||||
if not self.owner or self.owner == "" then
|
||||
local pn = clicker:get_player_name()
|
||||
self.owner = pn
|
||||
self.owner = name
|
||||
|
||||
local infotext = S("Owned by @1", S(pn))
|
||||
local infotext = S("Owned by @1", S(name))
|
||||
self.infotext = infotext
|
||||
self.object:set_properties({
|
||||
infotext = infotext
|
||||
@ -3923,8 +3961,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
|
||||
|
||||
-- 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 then
|
||||
|
||||
-- store mob and nametag stack in external variables
|
||||
mob_obj[name] = self
|
||||
@ -3932,10 +3969,10 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
|
||||
local tag = self.nametag or ""
|
||||
|
||||
minetest.show_formspec(name, "mobs_nametag",
|
||||
"size[5,3]"
|
||||
.. "field[1.35,1.25;2.9,1;name;"
|
||||
.. S("Enter name:") .. ";" .. tag .. "]"
|
||||
.. "button_exit[1.06,1.65;2.9,1;mob_rename;" .. S("Rename") .. "]")
|
||||
"size[5,3]" ..
|
||||
"field[1.35,1.25;2.9,1;name;" ..
|
||||
S"Enter name:" .. ";" .. tag .. "]" ..
|
||||
"button_exit[1.06,1.65;2.9,1;mob_rename;" .. S"Rename" .. "]")
|
||||
|
||||
return true
|
||||
end
|
||||
@ -3948,11 +3985,14 @@ end
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
-- right-clicked with nametag and name entered?
|
||||
if formname == "mobs_nametag"
|
||||
and fields.name and fields.name ~= "" then
|
||||
and fields.name
|
||||
and fields.name ~= "" then
|
||||
local name = player:get_player_name()
|
||||
|
||||
if not mob_obj[name]
|
||||
or not mob_obj[name].object then return end
|
||||
or not mob_obj[name].object then
|
||||
return
|
||||
end
|
||||
|
||||
-- make sure nametag is being used to name mob
|
||||
local item = player:get_wielded_item()
|
||||
|
@ -1,3 +1,4 @@
|
||||
default
|
||||
player_api
|
||||
experience?
|
||||
pep?
|
||||
|
@ -3,6 +3,9 @@ local path = minetest.get_modpath("mobs")
|
||||
-- Mob API
|
||||
dofile(path .. "/api.lua")
|
||||
|
||||
-- Rideable Mobs
|
||||
dofile(path .. "/mount.lua")
|
||||
|
||||
-- Mob Items
|
||||
dofile(path .. "/crafts.lua")
|
||||
|
||||
|
@ -13,7 +13,7 @@ Active Mob Limit Reached!=Достигнут Лимит Активных Моб
|
||||
Owned by @1=Владелец: @1
|
||||
Player=Игрок
|
||||
|
||||
(Tamed)=(Прирученн.)
|
||||
@1 (Tamed)=@1 (Прирученн.)
|
||||
Name Tag=Именная Метка
|
||||
Leather=Кожа
|
||||
Raw Meat=Сырое Мясо
|
||||
|
401
files/mobs/mobs_redo/mount.lua
Normal file
@ -0,0 +1,401 @@
|
||||
-- lib_mount by Blert2112 (edited by TenPlus1)
|
||||
|
||||
local abs, cos, floor, sin, sqrt, pi =
|
||||
math.abs, math.cos, math.floor, math.sin, math.sqrt, math.pi
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Helper functions
|
||||
--
|
||||
|
||||
local node_ok = function(pos, fallback)
|
||||
fallback = fallback or mobs.fallback_node
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
|
||||
return {name = fallback}
|
||||
end
|
||||
|
||||
|
||||
local function node_is(pos)
|
||||
local node = node_ok(pos)
|
||||
|
||||
if node.name == "air" then
|
||||
return "air"
|
||||
end
|
||||
|
||||
if minetest.get_item_group(node.name, "lava") ~= 0 then
|
||||
return "lava"
|
||||
end
|
||||
|
||||
if minetest.get_item_group(node.name, "liquid") ~= 0 then
|
||||
return "liquid"
|
||||
end
|
||||
|
||||
if minetest.registered_nodes[node.name].walkable == true then
|
||||
return "walkable"
|
||||
end
|
||||
|
||||
return "other"
|
||||
end
|
||||
|
||||
|
||||
local function get_sign(i)
|
||||
i = i or 0
|
||||
|
||||
if i == 0 then
|
||||
return 0
|
||||
else
|
||||
return i / abs(i)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_velocity(v, yaw, y)
|
||||
local x = -sin(yaw) * v
|
||||
local z = cos(yaw) * v
|
||||
|
||||
return {x = x, y = y, z = z}
|
||||
end
|
||||
|
||||
|
||||
local function get_v(v)
|
||||
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
|
||||
return
|
||||
end
|
||||
|
||||
local entity = attached_to:get_luaentity()
|
||||
|
||||
if entity and entity.driver
|
||||
and entity.driver == player then
|
||||
entity.driver = nil
|
||||
end
|
||||
|
||||
player:set_detach()
|
||||
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_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)
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
local players = minetest.get_connected_players()
|
||||
for i = 1, #players do
|
||||
force_detach(players[i])
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_dieplayer(function(player)
|
||||
force_detach(player)
|
||||
return true
|
||||
end)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function mobs.attach(entity, player)
|
||||
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}
|
||||
entity.driver_scale = entity.driver_scale or {x = 1, y = 1}
|
||||
|
||||
local rot_view = 0
|
||||
|
||||
if entity.player_rotation.y == 90 then
|
||||
rot_view = pi / 2
|
||||
end
|
||||
|
||||
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)
|
||||
player_api.player_attached[player:get_player_name()] = true
|
||||
player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0})
|
||||
|
||||
player:set_properties({
|
||||
visual_size = {
|
||||
x = entity.driver_scale.x,
|
||||
y = entity.driver_scale.y
|
||||
}
|
||||
})
|
||||
|
||||
minetest.after(0.2, function(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
player_api.set_animation(player, "sit", 30)
|
||||
end
|
||||
end, player:get_player_name())
|
||||
|
||||
player:set_look_horizontal(entity.object:get_yaw() - rot_view)
|
||||
end
|
||||
|
||||
|
||||
function mobs.detach(player, offset)
|
||||
force_detach(player)
|
||||
|
||||
player_api.set_animation(player, "stand", 30)
|
||||
|
||||
local pos = player:get_pos()
|
||||
|
||||
pos = {
|
||||
x = pos.x + offset.x,
|
||||
y = pos.y + offset.y + 0.2,
|
||||
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)
|
||||
end
|
||||
end, player:get_player_name(), pos)
|
||||
end
|
||||
|
||||
|
||||
function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
local rot_view = 0
|
||||
|
||||
if entity.player_rotation.y == 90 then
|
||||
rot_view = pi / 2
|
||||
end
|
||||
|
||||
local acce_y = 0
|
||||
local velo = entity.object:get_velocity()
|
||||
|
||||
entity.v = get_v(velo) * get_sign(entity.v)
|
||||
|
||||
-- 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
|
||||
|
||||
entity.v = entity.v - entity.accel / 10
|
||||
end
|
||||
|
||||
-- fix mob rotation
|
||||
local horz = entity.driver:get_look_horizontal() or 0
|
||||
entity.object:set_yaw(horz - entity.rotate)
|
||||
|
||||
if can_fly then
|
||||
-- fly up
|
||||
if ctrl.jump then
|
||||
velo.y = velo.y + 1
|
||||
if velo.y > entity.accel then velo.y = entity.accel end
|
||||
|
||||
elseif velo.y > 0 then
|
||||
velo.y = velo.y - 0.1
|
||||
if velo.y < 0 then velo.y = 0 end
|
||||
end
|
||||
|
||||
-- fly down
|
||||
if ctrl.sneak then
|
||||
velo.y = velo.y - 1
|
||||
if velo.y < -entity.accel then velo.y = -entity.accel end
|
||||
|
||||
elseif velo.y < 0 then
|
||||
velo.y = velo.y + 0.1
|
||||
if velo.y > 0 then velo.y = 0 end
|
||||
end
|
||||
else
|
||||
-- jump
|
||||
if ctrl.jump then
|
||||
|
||||
if velo.y == 0 then
|
||||
velo.y = velo.y + entity.jump_height
|
||||
acce_y = acce_y + (acce_y * 3) + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 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
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- set moving animation
|
||||
if moving_anim then
|
||||
mobs:set_animation(entity, moving_anim)
|
||||
end
|
||||
|
||||
-- Stop!
|
||||
local s = get_sign(entity.v)
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
-- enforce speed limit forward and reverse
|
||||
local max_spd = entity.max_speed_reverse
|
||||
|
||||
if get_sign(entity.v) >= 0 then
|
||||
max_spd = entity.max_speed_forward
|
||||
end
|
||||
|
||||
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.81, z = 0}
|
||||
|
||||
p.y = p.y - 0.5
|
||||
|
||||
local ni = node_is(p)
|
||||
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
|
||||
})
|
||||
|
||||
entity.object:punch(entity.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = entity.lava_damage}
|
||||
}, nil)
|
||||
|
||||
entity.lava_counter = 0
|
||||
end
|
||||
end
|
||||
|
||||
if entity.terrain_type == 2
|
||||
or entity.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
|
||||
new_acce.y = 20
|
||||
else
|
||||
new_acce.y = 5
|
||||
end
|
||||
else
|
||||
if abs(velo.y) < 1 then
|
||||
local pos = entity.object:get_pos()
|
||||
pos.y = floor(pos.y) + 0.5
|
||||
entity.object:set_pos(pos)
|
||||
velo.y = 0
|
||||
end
|
||||
end
|
||||
else
|
||||
v = v * 0.25
|
||||
end
|
||||
end
|
||||
|
||||
new_velo = get_velocity(v, entity.object:get_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)
|
||||
|
||||
entity.v2 = v
|
||||
end
|
||||
|
||||
|
||||
-- directional flying routine by D00Med (edited by TenPlus1)
|
||||
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
|
||||
|
||||
if ctrl.up then
|
||||
entity.object:set_velocity({
|
||||
x = dir.x * speed,
|
||||
y = dir.y * speed + 2,
|
||||
z = dir.z * speed
|
||||
})
|
||||
|
||||
elseif ctrl.down then
|
||||
entity.object:set_velocity({
|
||||
x = -dir.x * speed,
|
||||
y = dir.y * speed + 2,
|
||||
z = -dir.z * speed
|
||||
})
|
||||
|
||||
elseif not ctrl.down or ctrl.up or ctrl.jump then
|
||||
entity.object:set_velocity({x = 0, y = -2, z = 0})
|
||||
end
|
||||
|
||||
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,
|
||||
y = pos.y + 1.5 + dir.y,
|
||||
z = pos.z + 0 + dir.z * 2.5}, arrow)
|
||||
|
||||
local ent = obj:get_luaentity()
|
||||
if ent then
|
||||
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}
|
||||
yaw = entity.driver:get_look_horizontal()
|
||||
obj:set_yaw(yaw + pi / 2)
|
||||
obj:set_velocity(vec)
|
||||
else
|
||||
obj:remove()
|
||||
end
|
||||
end
|
||||
|
||||
-- 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
|
||||
mobs:set_animation(entity, moving_anim)
|
||||
end
|
||||
end
|
Before Width: | Height: | Size: 271 B After Width: | Height: | Size: 620 B |
Before Width: | Height: | Size: 263 B After Width: | Height: | Size: 584 B |
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 788 B |
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 540 B |