Added rideable mob functions (thanks blert2112)

This commit is contained in:
TenPlus1 2016-12-29 12:28:25 +00:00
parent 671fab54eb
commit ef3217d77f
6 changed files with 509 additions and 1 deletions

148
api.txt
View File

@ -1,5 +1,5 @@
MOB API (27th December 2016)
MOB API (29th December 2016)
The mob api is a function that can be called on by other mods to add new animals or monsters into minetest.
@ -260,3 +260,149 @@ Useful Internal Variables
'self.gotten' true when sheep have been sheared or cows have been milked, a toggle switch which can be used for many functions
'self.child' true when mob is currently a child (when two mobs have bred and current mob is the outcome)
'self.hornytimer' background timer that controls breeding functions and mob childhood timings
Mobs can now be ridden by players and the following shows the functions and usage:
mobs:attach(self, player)
This function attaches a player to the mob so it can be ridden.
'self' mob information
'player' player information
mobs:detach(player, offset)
This function will detach the player currently riding a mob to an offset position.
'player' player information
'offset' position table containing offset values
mobs:drive(self, move_animation, stand_animation, can_fly)
This function allows an attached player to move the mob around and animate it at same time.
'self' mob information
'move_animation' string containing movement animation e.g. "walk"
'stand_animation' string containing standing animation e.g. "stand"
'can_fly' if true then jump and sneak controls will allow mob to fly up and down
Certain variables need to be set before using the above functions:
'self.v2' toggle switch
'self.max_speed_forward' max speed mob can move forward
'self.max_speed_reverse' max speed mob can move backwards
'self.accel' acceleration speed
'self.terrain_type' integer containing terrain mob can walk on (1 = water, 2 or 3 = land)
'self.driver_attach_at' position offset for attaching player to mob
'self.driver_eye_offset' position offset for attached player view
Here is an example mob to show how the above functions work:
-- rideable horse
mobs:register_mob("mob_horse:horse", {
type = "animal",
visual = "mesh",
visual_size = {x = 1.20, y = 1.20},
mesh = "mobs_horse.x",
collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.25, 0.4},
animation = {
speed_normal = 15, speed_run = 30,
stand_start = 25, stand_end = 75,
walk_start = 75, walk_end = 100,
run_start = 75, run_end = 100,
},
textures = {
{"mobs_horse.png"},
{"mobs_horsepeg.png"},
{"mobs_horseara.png"}
},
fear_height = 3,
runaway = true,
fly = false,
walk_chance = 60,
view_range = 5,
follow = {"farming:wheat"},
passive = true,
hp_min = 12,
hp_max = 16,
armor = 200,
lava_damage = 5,
fall_damage = 5,
water_damage = 1,
makes_footstep_sound = true,
drops = {
{name = "mobs:meat_raw", chance = 1, min = 2, max = 3}
},
-- custom function uses self.v2 toggle to set ride variables
do_custom = function(self, dtime)
if not self.v2 then
self.v2 = 0
self.max_speed_forward = 6
self.max_speed_reverse = 2
self.accel = 6
self.terrain_type = 3
self.driver_attach_at = {x = 0, y = 20, z = -2}
self.driver_eye_offset = {x = 0, y = 3, z = 0}
end
-- when riding mob call drive function to control
if self.driver then
mobs.drive(self, "walk", "stand", false)
return false -- skip rest of mob functions
end
return true
end,
on_rightclick = function(self, clicker)
-- check for actual player
if not clicker or not clicker:is_player() then
return
end
-- used to feed, tame and heal mob
if mobs:feed_tame(self, clicker, 10, true, true) then
return
end
-- check if mob has been tamed and player is owner
if self.tamed and self.owner == clicker:get_player_name() then
local inv = clicker:get_inventory()
-- detach player when riding mob (add saddle to inventory)
if self.driver and clicker == self.driver then
mobs.detach(clicker, {x = 1, y = 0, z = 1})
if inv:room_for_item("main", "mobs:saddle") then
inv:add_item("main", "mobs:saddle")
else
minetest.add_item(clicker.getpos(), "mobs:saddle")
end
-- attach player to mob if not riding (take saddle from inventory)
elseif not self.driver then
if clicker:get_wielded_item():get_name() == "mobs:saddle" then
self.object:set_properties({stepheight = 1.1})
mobs.attach(self, clicker)
inv:remove_item("main", "mobs:saddle")
end
end
end
mobs:capture_mob(self, clicker, 0, 0, 80, false, nil)
end
})

View File

@ -98,3 +98,18 @@ minetest.register_craft({
{"default:stone", "default:stone", "default:stone"},
}
})
-- saddle
minetest.register_craftitem("mobs:saddle", {
description = "Saddle",
inventory_image = "mobs_saddle.png"
})
minetest.register_craft({
output = "mobs:saddle",
recipe = {
{"mobs:leather", "mobs:leather", "mobs:leather"},
{"mobs:leather", "default:steel_ingot", "mobs:leather"},
{"mobs:leather", "default:steel_ingot", "mobs:leather"},
}
})

View File

@ -4,6 +4,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")

343
mount.lua Normal file
View File

@ -0,0 +1,343 @@
-- lib_mount by Blert2112 (edited by TenPlus1)
local enable_crash = true
local crash_threshold = 6.5 -- ignored if enable_crash=false
------------------------------------------------------------------------------
--
-- Helper functions
--
local function node_is(pos)
local node = minetest.get_node(pos)
if node.name == "air" then
return "air"
end
if minetest.get_item_group(node.name, "liquid") ~= 0 then
return "liquid"
end
if minetest.get_item_group(node.name, "walkable") ~= 0 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 / math.abs(i)
end
end
local function get_velocity(v, yaw, y)
local x = -math.sin(yaw) * v
local z = math.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)
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.driver
and entity.driver == player then
entity.driver = nil
end
player:set_detach()
default.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)
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)
local attach_at, eye_offset = {}, {}
if not entity.player_rotation then
entity.player_rotation = {x = 0, y = 0, z = 0}
end
local rot_view = 0
if entity.player_rotation.y == 90 then
rot_view = math.pi/2
end
if not entity.driver_attach_at then
entity.driver_attach_at = {x = 0, y = 0, z = 0}
end
if not entity.driver_eye_offset then
entity.driver_eye_offset = {x = 0, y = 0, z = 0}
end
attach_at = entity.driver_attach_at
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:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0})
minetest.after(0.2, function()
default.player_set_animation(player, "sit" , 30)
end)
player:set_look_yaw(entity.object:getyaw() - rot_view)
end
function mobs.detach(player, offset)
force_detach(player)
default.player_set_animation(player, "stand" , 30)
local pos = player:getpos()
pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
minetest.after(0.1, function()
player:setpos(pos)
end)
end
function mobs.drive(entity, moving_anim, stand_anim, can_fly)
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
local acce_y = 0
local velo = entity.object:getvelocity()
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
--entity.object:setyaw(entity.driver:get_look_yaw() - rot_steer)
entity.object:setyaw(entity.driver:get_look_horizontal())-- - rot_steer)
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
set_animation(entity, stand_anim)
end
return
end
-- set moving animation
if moving_anim then
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:setvelocity({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 math.abs(entity.v) > max_spd then
entity.v = entity.v - get_sign(entity.v)
end
-- Set position, velocity and acceleration
local p = entity.object:getpos()
local new_velo = {x = 0, y = 0, z = 0}
local new_acce = {x = 0, y = -9.8, 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" then
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 math.abs(velo.y) < 1 then
local pos = entity.object:getpos()
pos.y = math.floor(pos.y) + 0.5
entity.object:setpos(pos)
velo.y = 0
end
end
else
v = v*0.25
end
-- elseif ni == "walkable" then
-- v = 0
-- new_acce.y = 1
end
new_velo = get_velocity(v, entity.object:getyaw() - rot_view, velo.y)
new_acce.y = new_acce.y + acce_y
entity.object:setvelocity(new_velo)
entity.object:setacceleration(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

View File

@ -23,6 +23,7 @@ Lucky Blocks: 9
Changelog:
- 1.33- Added functions to mount ride mobs (mobs.attach, mobs.detach, mobs.drive) many thanks to Blert2112
- 1.32- Added new spawn check to count specific mobs AND new minetest.conf setting to chance spawn chance and numbers, added ability to protect tamed mobs
- 1.31- Added 'attack_animals' and 'specific_attack' flags for custom monster attacks, also 'mob_difficulty' .conf setting to make mobs harder.
- 1.30- Added support for invisibility mod (mobs cant attack what they cant see), tweaked and tidied code

BIN
textures/mobs_saddle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B