mount.lua 10 KB

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