123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- -- lib_mount by Blert2112 (edited by TenPlus1)
- local is_50 = minetest.get_modpath("player_api") -- 5.x compatibility
- 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)
- if not player then return end
- 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()
- local name = player:get_player_name()
- if is_50 then
- player_api.player_attached[name] = false
- player_api.set_animation(player, "stand", 30)
- else
- default.player_attached[name] = false
- default.player_set_animation(player, "stand", 30)
- end
- player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
- 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)
- -- 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)
- 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)
- if is_50 then
- player_api.player_attached[player:get_player_name()] = true
- else
- default.player_attached[player:get_player_name()] = true
- end
- player:set_attach(entity.object, "", attach_at, entity.player_rotation)
- 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()
- if player and player:is_player() then
- if is_50 then
- player_api.set_animation(player, "sit", 30)
- else
- default.player_set_animation(player, "sit", 30)
- end
- end
- end)
- player:set_look_horizontal(entity.object:get_yaw() - rot_view)
- end
- function mobs.detach(player)
- force_detach(player)
- 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)
- end
- function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
- local yaw = entity.object:get_yaw() or 0
- 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() ; if not velo then return end
- entity.v = get_v(velo) * get_sign(entity.v)
- -- process controls
- if entity.driver then
- local ctrl = entity.driver:get_player_control()
- -- move forwards
- if ctrl.up then
- entity.v = entity.v + entity.accel * dtime
- -- 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 * dtime
- end
- -- 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
- if velo.y > entity.accel then velo.y = entity.accel end
- elseif velo.y > 0 then
- velo.y = velo.y - dtime
- 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 + dtime
- 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
- if entity.v > entity.max_speed_forward then
- entity.v = entity.max_speed_forward
- elseif entity.v < -entity.max_speed_reverse then
- entity.v = -entity.max_speed_reverse
- end
- -- Set position, velocity and acceleration
- local p = entity.object:get_pos()
- if not p then return end
- local new_acce = {x = 0, y = entity.fall_speed, 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
- }, true)
- 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
- 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
- new_acce.y = 20
- else
- new_acce.y = 5
- end
- else
- if abs(velo.y) < 1 then
- local pos = entity.object:get_pos()
- if not pos then return end
- pos.y = floor(pos.y) + 0.5
- entity.object:set_pos(pos)
- velo.y = 0
- end
- end
- else
- v = v * 0.25
- end
- end
- 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)
- 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() ; if not ctrl then return end
- local velo = entity.object:get_velocity()
- local dir = entity.driver:get_look_dir()
- local yaw = entity.driver:get_look_horizontal() + 1.57
- if not ctrl or not velo then return end
- 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
|