Mob API for mods to add monsters, animals and more. https://content.minetest.net/packages/TenPlus1/mobs/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

458 lines
9.2KB

  1. -- lib_mount by Blert2112 (edited by TenPlus1)
  2. local enable_crash = false
  3. local crash_threshold = 6.5 -- ignored if enable_crash=false
  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 / math.abs(i)
  38. end
  39. end
  40. local function get_velocity(v, yaw, y)
  41. local x = -math.sin(yaw) * v
  42. local z = math.cos(yaw) * v
  43. return {x = x, y = y, z = z}
  44. end
  45. local function get_v(v)
  46. return math.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. default.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. default.player_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. function mobs.attach(entity, player)
  80. local attach_at, eye_offset = {}, {}
  81. entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0}
  82. entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0}
  83. entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0}
  84. entity.driver_scale = entity.driver_scale or {x = 1, y = 1}
  85. local rot_view = 0
  86. if entity.player_rotation.y == 90 then
  87. rot_view = math.pi / 2
  88. end
  89. attach_at = entity.driver_attach_at
  90. eye_offset = entity.driver_eye_offset
  91. entity.driver = player
  92. force_detach(player)
  93. player:set_attach(entity.object, "", attach_at, entity.player_rotation)
  94. default.player_attached[player:get_player_name()] = true
  95. player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0})
  96. player:set_properties({
  97. visual_size = {
  98. x = entity.driver_scale.x,
  99. y = entity.driver_scale.y
  100. }
  101. })
  102. minetest.after(0.2, function(name)
  103. local player = minetest.get_player_by_name(name)
  104. if player then
  105. default.player_set_animation(player, "sit" , 30)
  106. end
  107. end, player:get_player_name())
  108. player:set_look_horizontal(entity.object:get_yaw() - rot_view)
  109. end
  110. function mobs.detach(player, offset)
  111. force_detach(player)
  112. default.player_set_animation(player, "stand" , 30)
  113. local pos = player:get_pos()
  114. pos = {
  115. x = pos.x + offset.x,
  116. y = pos.y + 0.2 + offset.y,
  117. z = pos.z + offset.z
  118. }
  119. minetest.after(0.1, function(name, pos)
  120. local player = minetest.get_player_by_name(name)
  121. if player then
  122. player:set_pos(pos)
  123. end
  124. end, player:get_player_name(), pos)
  125. end
  126. function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
  127. local rot_steer, rot_view = math.pi/2, 0
  128. if entity.player_rotation.y == 90 then
  129. rot_steer, rot_view = 0, math.pi/2
  130. end
  131. local acce_y = 0
  132. local velo = entity.object:get_velocity()
  133. entity.v = get_v(velo) * get_sign(entity.v)
  134. -- process controls
  135. if entity.driver then
  136. --print ("---velo", get_v(velo))
  137. local ctrl = entity.driver:get_player_control()
  138. -- move forwards
  139. if ctrl.up then
  140. entity.v = entity.v + entity.accel / 10
  141. -- move backwards
  142. elseif ctrl.down then
  143. if entity.max_speed_reverse == 0 and entity.v == 0 then
  144. return
  145. end
  146. entity.v = entity.v - entity.accel / 10
  147. end
  148. -- fix mob rotation
  149. local horz = entity.driver:get_look_horizontal() or 0
  150. entity.object:set_yaw(horz - entity.rotate)
  151. if can_fly then
  152. -- fly up
  153. if ctrl.jump then
  154. velo.y = velo.y + 1
  155. if velo.y > entity.accel then velo.y = entity.accel end
  156. elseif velo.y > 0 then
  157. velo.y = velo.y - 0.1
  158. if velo.y < 0 then velo.y = 0 end
  159. end
  160. -- fly down
  161. if ctrl.sneak then
  162. velo.y = velo.y - 1
  163. if velo.y < -entity.accel then velo.y = -entity.accel end
  164. elseif velo.y < 0 then
  165. velo.y = velo.y + 0.1
  166. if velo.y > 0 then velo.y = 0 end
  167. end
  168. else
  169. -- jump
  170. if ctrl.jump then
  171. if velo.y == 0 then
  172. velo.y = velo.y + entity.jump_height
  173. acce_y = acce_y + (acce_y * 3) + 1
  174. end
  175. end
  176. end
  177. end
  178. -- if not moving then set animation and return
  179. if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
  180. if stand_anim then
  181. mobs:set_animation(entity, stand_anim)
  182. end
  183. return
  184. end
  185. -- set moving animation
  186. if moving_anim then
  187. mobs:set_animation(entity, moving_anim)
  188. end
  189. -- Stop!
  190. local s = get_sign(entity.v)
  191. entity.v = entity.v - 0.02 * s
  192. if s ~= get_sign(entity.v) then
  193. entity.object:set_velocity({x = 0, y = 0, z = 0})
  194. entity.v = 0
  195. return
  196. end
  197. -- enforce speed limit forward and reverse
  198. local max_spd = entity.max_speed_reverse
  199. if get_sign(entity.v) >= 0 then
  200. max_spd = entity.max_speed_forward
  201. end
  202. if math.abs(entity.v) > max_spd then
  203. entity.v = entity.v - get_sign(entity.v)
  204. end
  205. -- Set position, velocity and acceleration
  206. local p = entity.object:get_pos()
  207. local new_velo
  208. local new_acce = {x = 0, y = -9.8, z = 0}
  209. p.y = p.y - 0.5
  210. local ni = node_is(p)
  211. local v = entity.v
  212. if ni == "air" then
  213. if can_fly == true then
  214. new_acce.y = 0
  215. end
  216. elseif ni == "liquid" or ni == "lava" then
  217. if ni == "lava" and entity.lava_damage ~= 0 then
  218. entity.lava_counter = (entity.lava_counter or 0) + dtime
  219. if entity.lava_counter > 1 then
  220. minetest.sound_play("default_punch", {
  221. object = entity.object,
  222. max_hear_distance = 5
  223. }, true)
  224. entity.object:punch(entity.object, 1.0, {
  225. full_punch_interval = 1.0,
  226. damage_groups = {fleshy = entity.lava_damage}
  227. }, nil)
  228. entity.lava_counter = 0
  229. end
  230. end
  231. if entity.terrain_type == 2
  232. or entity.terrain_type == 3 then
  233. new_acce.y = 0
  234. p.y = p.y + 1
  235. if node_is(p) == "liquid" then
  236. if velo.y >= 5 then
  237. velo.y = 5
  238. elseif velo.y < 0 then
  239. new_acce.y = 20
  240. else
  241. new_acce.y = 5
  242. end
  243. else
  244. if math.abs(velo.y) < 1 then
  245. local pos = entity.object:get_pos()
  246. pos.y = math.floor(pos.y) + 0.5
  247. entity.object:set_pos(pos)
  248. velo.y = 0
  249. end
  250. end
  251. else
  252. v = v * 0.25
  253. end
  254. end
  255. new_velo = get_velocity(v, entity.object:get_yaw() - rot_view, velo.y)
  256. new_acce.y = new_acce.y + acce_y
  257. entity.object:set_velocity(new_velo)
  258. entity.object:set_acceleration(new_acce)
  259. -- CRASH!
  260. if enable_crash then
  261. local intensity = entity.v2 - v
  262. if intensity >= crash_threshold then
  263. --print("----------- crash", intensity)
  264. entity.object:punch(entity.object, 1.0, {
  265. full_punch_interval = 1.0,
  266. damage_groups = {fleshy = intensity}
  267. }, nil)
  268. end
  269. end
  270. entity.v2 = v
  271. end
  272. -- directional flying routine by D00Med (edited by TenPlus1)
  273. function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
  274. local ctrl = entity.driver:get_player_control()
  275. local velo = entity.object:get_velocity()
  276. local dir = entity.driver:get_look_dir()
  277. local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands
  278. local rot_steer, rot_view = math.pi / 2, 0
  279. if entity.player_rotation.y == 90 then
  280. rot_steer, rot_view = 0, math.pi / 2
  281. end
  282. if ctrl.up then
  283. entity.object:set_velocity({
  284. x = dir.x * speed,
  285. y = dir.y * speed + 2,
  286. z = dir.z * speed
  287. })
  288. elseif ctrl.down then
  289. entity.object:set_velocity({
  290. x = -dir.x * speed,
  291. y = dir.y * speed + 2,
  292. z = -dir.z * speed
  293. })
  294. elseif not ctrl.down or ctrl.up or ctrl.jump then
  295. entity.object:set_velocity({x = 0, y = -2, z = 0})
  296. end
  297. entity.object:set_yaw(yaw + math.pi + math.pi / 2 - entity.rotate)
  298. -- firing arrows
  299. if ctrl.LMB and ctrl.sneak and shoots then
  300. local pos = entity.object:get_pos()
  301. local obj = minetest.add_entity({
  302. x = pos.x + 0 + dir.x * 2.5,
  303. y = pos.y + 1.5 + dir.y,
  304. z = pos.z + 0 + dir.z * 2.5}, arrow)
  305. local ent = obj:get_luaentity()
  306. if ent then
  307. ent.switch = 1 -- for mob specific arrows
  308. ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
  309. local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6}
  310. local yaw = entity.driver:get_look_horizontal()
  311. obj:set_yaw(yaw + math.pi / 2)
  312. obj:set_velocity(vec)
  313. else
  314. obj:remove()
  315. end
  316. end
  317. -- change animation if stopped
  318. if velo.x == 0 and velo.y == 0 and velo.z == 0 then
  319. mobs:set_animation(entity, stand_anim)
  320. else
  321. -- moving animation
  322. mobs:set_animation(entity, moving_anim)
  323. end
  324. end