mount.lua 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. -- lib_mount by Blert2112 (edited by TenPlus1)
  2. local abs, cos, floor, sin, sqrt, pi =
  3. math.abs, math.cos, math.floor, math.sin, math.sqrt, math.pi
  4. ------------------------------------------------------------------------------
  5. --
  6. -- Helper functions
  7. --
  8. local node_ok = function(pos, fallback)
  9. fallback = fallback or mobs.fallback_node
  10. local node = minetest.get_node_or_nil(pos)
  11. if node and minetest.registered_nodes[node.name] then
  12. return node
  13. end
  14. return {name = fallback}
  15. end
  16. local function node_is(pos)
  17. local node = node_ok(pos)
  18. if node.name == "air" then
  19. return "air"
  20. end
  21. if minetest.get_item_group(node.name, "lava") ~= 0 then
  22. return "lava"
  23. end
  24. if minetest.get_item_group(node.name, "liquid") ~= 0 then
  25. return "liquid"
  26. end
  27. if minetest.registered_nodes[node.name].walkable == true then
  28. return "walkable"
  29. end
  30. return "other"
  31. end
  32. local function get_sign(i)
  33. i = i or 0
  34. if i == 0 then
  35. return 0
  36. else
  37. return i / abs(i)
  38. end
  39. end
  40. local function get_velocity(v, yaw, y)
  41. local x = -sin(yaw) * v
  42. local z = cos(yaw) * v
  43. return {x = x, y = y, z = z}
  44. end
  45. local function get_v(v)
  46. return sqrt(v.x * v.x + v.z * v.z)
  47. end
  48. local function force_detach(player)
  49. local attached_to = player:get_attach()
  50. if not attached_to then
  51. return
  52. end
  53. local entity = attached_to:get_luaentity()
  54. if entity and entity.driver
  55. and entity.driver == player then
  56. entity.driver = nil
  57. end
  58. player:set_detach()
  59. player_api.player_attached[player:get_player_name()] = false
  60. player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
  61. player_api.set_animation(player, "stand", 30)
  62. player:set_properties({visual_size = {x = 1, y = 1}})
  63. end
  64. -------------------------------------------------------------------------------
  65. minetest.register_on_leaveplayer(function(player)
  66. force_detach(player)
  67. end)
  68. minetest.register_on_shutdown(function()
  69. local players = minetest.get_connected_players()
  70. for i = 1, #players do
  71. force_detach(players[i])
  72. end
  73. end)
  74. minetest.register_on_dieplayer(function(player)
  75. force_detach(player)
  76. return true
  77. end)
  78. -------------------------------------------------------------------------------
  79. -- Just for correct detaching
  80. local function find_free_pos(pos)
  81. local check = {
  82. {x = 1, y = 0, z = 0},
  83. {x = 1, y = 1, z = 0},
  84. {x = -1, y = 0, z = 0},
  85. {x = -1, y = 1, z = 0},
  86. {x = 0, y = 0, z = 1},
  87. {x = 0, y = 1, z = 1},
  88. {x = 0, y = 0, z = -1},
  89. {x = 0, y = 1, z = -1}
  90. }
  91. for _, c in pairs(check) do
  92. local npos = {x = pos.x + c.x, y = pos.y + c.y, z = pos.z + c.z}
  93. local node = minetest.get_node_or_nil(npos)
  94. if node and node.name then
  95. local def = minetest.registered_nodes[node.name]
  96. if def and not def.walkable and
  97. def.liquidtype == "none" then
  98. return npos
  99. end
  100. end
  101. end
  102. return pos
  103. end
  104. -------------------------------------------------------------------------------
  105. function mobs.attach(entity, player)
  106. entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0}
  107. entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0}
  108. entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0}
  109. entity.driver_scale = entity.driver_scale or {x = 1, y = 1}
  110. local rot_view = 0
  111. if entity.player_rotation.y == 90 then
  112. rot_view = pi / 2
  113. end
  114. local attach_at = entity.driver_attach_at
  115. local eye_offset = entity.driver_eye_offset
  116. entity.driver = player
  117. force_detach(player)
  118. player:set_attach(entity.object, "", attach_at, entity.player_rotation)
  119. player_api.player_attached[player:get_player_name()] = true
  120. player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0})
  121. player:set_properties({
  122. visual_size = {
  123. x = entity.driver_scale.x,
  124. y = entity.driver_scale.y
  125. }
  126. })
  127. minetest.after(0.2, function()
  128. if player and player:is_player() then
  129. player_api.set_animation(player, "sit", 30)
  130. end
  131. end)
  132. player:set_look_horizontal(entity.object:get_yaw() - rot_view)
  133. end
  134. function mobs.detach(player)
  135. force_detach(player)
  136. minetest.after(0.1, function()
  137. if player and player:is_player() then
  138. local pos = find_free_pos(player:get_pos())
  139. pos.y = pos.y + 0.5
  140. player:set_pos(pos)
  141. end
  142. end)
  143. end
  144. function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
  145. local yaw = entity.object:get_yaw() or 0
  146. local rot_view = 0
  147. if entity.player_rotation.y == 90 then
  148. rot_view = pi / 2
  149. end
  150. local acce_y = 0
  151. local velo = entity.object:get_velocity()
  152. entity.v = get_v(velo) * get_sign(entity.v)
  153. -- process controls
  154. if entity.driver then
  155. local ctrl = entity.driver:get_player_control()
  156. -- move forwards
  157. if ctrl.up then
  158. entity.v = entity.v + entity.accel / 10
  159. -- move backwards
  160. elseif ctrl.down then
  161. if entity.max_speed_reverse == 0 and entity.v == 0 then
  162. return
  163. end
  164. entity.v = entity.v - entity.accel / 10
  165. end
  166. -- mob rotation
  167. local horz
  168. if entity.alt_turn == true then
  169. horz = yaw
  170. if ctrl.left then
  171. horz = horz + 0.05
  172. elseif ctrl.right then
  173. horz = horz - 0.05
  174. end
  175. else
  176. horz = entity.driver:get_look_horizontal() or 0
  177. end
  178. entity.object:set_yaw(horz - entity.rotate)
  179. if can_fly then
  180. -- fly up
  181. if ctrl.jump then
  182. velo.y = velo.y + 1
  183. if velo.y > entity.accel then velo.y = entity.accel end
  184. elseif velo.y > 0 then
  185. velo.y = velo.y - 0.1
  186. if velo.y < 0 then velo.y = 0 end
  187. end
  188. -- fly down
  189. if ctrl.sneak then
  190. velo.y = velo.y - 1
  191. if velo.y < -entity.accel then velo.y = -entity.accel end
  192. elseif velo.y < 0 then
  193. velo.y = velo.y + 0.1
  194. if velo.y > 0 then velo.y = 0 end
  195. end
  196. else
  197. -- jump
  198. if ctrl.jump then
  199. if velo.y == 0 then
  200. velo.y = velo.y + entity.jump_height
  201. acce_y = acce_y + (acce_y * 3) + 1
  202. end
  203. end
  204. end
  205. end
  206. -- if not moving then set animation and return
  207. if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
  208. if stand_anim then
  209. mobs:set_animation(entity, stand_anim)
  210. end
  211. return
  212. end
  213. -- set moving animation
  214. if moving_anim then
  215. mobs:set_animation(entity, moving_anim)
  216. end
  217. -- Stop!
  218. local s = get_sign(entity.v)
  219. entity.v = entity.v - 0.02 * s
  220. if s ~= get_sign(entity.v) then
  221. entity.object:set_velocity({x = 0, y = 0, z = 0})
  222. entity.v = 0
  223. return
  224. end
  225. -- enforce speed limit forward and reverse
  226. local max_spd = entity.max_speed_reverse
  227. if get_sign(entity.v) >= 0 then
  228. max_spd = entity.max_speed_forward
  229. end
  230. if abs(entity.v) > max_spd then
  231. entity.v = entity.v - get_sign(entity.v)
  232. end
  233. -- Set position, velocity and acceleration
  234. local p = entity.object:get_pos()
  235. if not p then return end
  236. local new_acce = {x = 0, y = -9.81, z = 0}
  237. p.y = p.y - 0.5
  238. local ni = node_is(p)
  239. local v = entity.v
  240. if ni == "air" then
  241. if can_fly == true then
  242. new_acce.y = 0
  243. end
  244. elseif ni == "liquid" or ni == "lava" then
  245. if ni == "lava" and entity.lava_damage ~= 0 then
  246. entity.lava_counter = (entity.lava_counter or 0) + dtime
  247. if entity.lava_counter > 1 then
  248. minetest.sound_play("default_punch", {
  249. object = entity.object,
  250. max_hear_distance = 5
  251. })
  252. entity.object:punch(entity.object, 1.0, {
  253. full_punch_interval = 1.0,
  254. damage_groups = {fleshy = entity.lava_damage}
  255. }, nil)
  256. entity.lava_counter = 0
  257. end
  258. end
  259. local terrain_type = entity.terrain_type
  260. if terrain_type == 2 or terrain_type == 3 then
  261. new_acce.y = 0
  262. p.y = p.y + 1
  263. if node_is(p) == "liquid" then
  264. if velo.y >= 5 then
  265. velo.y = 5
  266. elseif velo.y < 0 then
  267. new_acce.y = 20
  268. else
  269. new_acce.y = 5
  270. end
  271. else
  272. if abs(velo.y) < 1 then
  273. local pos = entity.object:get_pos()
  274. if not pos then return end
  275. pos.y = floor(pos.y) + 0.5
  276. entity.object:set_pos(pos)
  277. velo.y = 0
  278. end
  279. end
  280. else
  281. v = v * 0.25
  282. end
  283. end
  284. local new_velo = get_velocity(v, yaw - rot_view, velo.y)
  285. new_acce.y = new_acce.y + acce_y
  286. entity.object:set_velocity(new_velo)
  287. entity.object:set_acceleration(new_acce)
  288. entity.v2 = v
  289. end
  290. -- directional flying routine by D00Med (edited by TenPlus1)
  291. function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim)
  292. local ctrl = entity.driver:get_player_control()
  293. local velo = entity.object:get_velocity()
  294. local dir = entity.driver:get_look_dir()
  295. local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands
  296. if ctrl.up then
  297. entity.object:set_velocity({
  298. x = dir.x * speed,
  299. y = dir.y * speed + 2,
  300. z = dir.z * speed
  301. })
  302. elseif ctrl.down then
  303. entity.object:set_velocity({
  304. x = -dir.x * speed,
  305. y = dir.y * speed + 2,
  306. z = -dir.z * speed
  307. })
  308. elseif not ctrl.down or ctrl.up or ctrl.jump then
  309. entity.object:set_velocity({x = 0, y = -2, z = 0})
  310. end
  311. entity.object:set_yaw(yaw + pi + pi / 2 - entity.rotate)
  312. -- firing arrows
  313. if ctrl.LMB and ctrl.sneak and shoots then
  314. local pos = entity.object:get_pos()
  315. local obj = minetest.add_entity({
  316. x = pos.x + 0 + dir.x * 2.5,
  317. y = pos.y + 1.5 + dir.y,
  318. z = pos.z + 0 + dir.z * 2.5}, arrow)
  319. local ent = obj:get_luaentity()
  320. if ent then
  321. ent.switch = 1 -- for mob specific arrows
  322. ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
  323. local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6}
  324. yaw = entity.driver:get_look_horizontal()
  325. obj:set_yaw(yaw + pi / 2)
  326. obj:set_velocity(vec)
  327. else
  328. obj:remove()
  329. end
  330. end
  331. -- change animation if stopped
  332. if velo.x == 0 and velo.y == 0 and velo.z == 0 then
  333. mobs:set_animation(entity, stand_anim)
  334. else
  335. -- moving animation
  336. mobs:set_animation(entity, moving_anim)
  337. end
  338. end