Mobs_redo: update and add lib_mount

This commit is contained in:
Maksim 2020-10-11 09:25:39 +02:00
parent 97cfd63768
commit b66c6a59f3
10 changed files with 620 additions and 180 deletions

View File

@ -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

View File

@ -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()

View File

@ -1,3 +1,4 @@
default
player_api
experience?
pep?

View File

@ -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")

View File

@ -13,7 +13,7 @@ Active Mob Limit Reached!=Достигнут Лимит Активных Моб
Owned by @1=Владелец: @1
Player=Игрок
(Tamed)=(Прирученн.)
@1 (Tamed)=@1 (Прирученн.)
Name Tag=Именная Метка
Leather=Кожа
Raw Meat=Сырое Мясо

View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 677 B

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

After

Width:  |  Height:  |  Size: 540 B