init.lua 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. --
  2. -- Helper functions
  3. --
  4. local function is_water(pos)
  5. local nn = minetest.get_node(pos).name
  6. return minetest.get_item_group(nn, "water") ~= 0
  7. end
  8. local function get_sign(i)
  9. if i == 0 then
  10. return 0
  11. else
  12. return i / math.abs(i)
  13. end
  14. end
  15. local function get_velocity(v, yaw, y)
  16. local x = -math.sin(yaw) * v
  17. local z = math.cos(yaw) * v
  18. return {x = x, y = y, z = z}
  19. end
  20. local function get_v(v)
  21. return math.sqrt(v.x ^ 2 + v.z ^ 2)
  22. end
  23. --
  24. -- Boat entity
  25. --
  26. local boat = {
  27. physical = true,
  28. -- Warning: Do not change the position of the collisionbox top surface,
  29. -- lowering it causes the boat to fall through the world if underwater
  30. collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
  31. visual = "mesh",
  32. mesh = "boats_boat.obj",
  33. textures = {"default_wood.png"},
  34. driver = nil,
  35. v = 0,
  36. last_v = 0,
  37. removed = false,
  38. auto = false
  39. }
  40. function boat.on_rightclick(self, clicker)
  41. if not clicker or not clicker:is_player() then
  42. return
  43. end
  44. local name = clicker:get_player_name()
  45. if self.driver and clicker == self.driver then
  46. self.driver = nil
  47. self.auto = false
  48. clicker:set_detach()
  49. player_api.player_attached[name] = false
  50. player_api.set_animation(clicker, "stand" , 30)
  51. local pos = clicker:get_pos()
  52. pos = {x = pos.x, y = pos.y + 0.2, z = pos.z}
  53. minetest.after(0.1, function()
  54. clicker:set_pos(pos)
  55. end)
  56. elseif not self.driver then
  57. local attach = clicker:get_attach()
  58. if attach and attach:get_luaentity() then
  59. local luaentity = attach:get_luaentity()
  60. if luaentity.driver then
  61. luaentity.driver = nil
  62. end
  63. clicker:set_detach()
  64. end
  65. self.driver = clicker
  66. clicker:set_attach(self.object, "",
  67. {x = 0.5, y = 1, z = -3}, {x = 0, y = 0, z = 0})
  68. player_api.player_attached[name] = true
  69. minetest.after(0.2, function()
  70. player_api.set_animation(clicker, "sit" , 30)
  71. end)
  72. clicker:set_look_horizontal(self.object:get_yaw())
  73. end
  74. end
  75. function boat.on_activate(self, staticdata, dtime_s)
  76. self.object:set_armor_groups({immortal = 1})
  77. if staticdata then
  78. self.v = tonumber(staticdata)
  79. end
  80. self.last_v = self.v
  81. end
  82. function boat.get_staticdata(self)
  83. return tostring(self.v)
  84. end
  85. function boat.on_punch(self, puncher)
  86. if not puncher or not puncher:is_player() or self.removed then
  87. return
  88. end
  89. if self.driver and puncher == self.driver then
  90. self.driver = nil
  91. puncher:set_detach()
  92. player_api.player_attached[puncher:get_player_name()] = false
  93. end
  94. if not self.driver then
  95. self.removed = true
  96. local inv = puncher:get_inventory()
  97. if not (creative and creative.is_enabled_for
  98. and creative.is_enabled_for(puncher:get_player_name()))
  99. or not inv:contains_item("main", "boats:boat") then
  100. local leftover = inv:add_item("main", "boats:boat")
  101. -- if no room in inventory add a replacement boat to the world
  102. if not leftover:is_empty() then
  103. minetest.add_item(self.object:get_pos(), leftover)
  104. end
  105. end
  106. -- delay remove to ensure player is detached
  107. minetest.after(0.1, function()
  108. self.object:remove()
  109. end)
  110. end
  111. end
  112. function boat.on_step(self, dtime)
  113. self.v = get_v(self.object:get_velocity()) * get_sign(self.v)
  114. if self.driver then
  115. local driver_name = self.driver:get_player_name()
  116. local ctrl = self.driver:get_player_control()
  117. if ctrl.up and ctrl.down then
  118. if not self.auto then
  119. self.auto = true
  120. minetest.chat_send_player(driver_name,
  121. "[boats] Cruise on")
  122. end
  123. elseif ctrl.down then
  124. self.v = self.v - 0.1
  125. if self.auto then
  126. self.auto = false
  127. minetest.chat_send_player(driver_name,
  128. "[boats] Cruise off")
  129. end
  130. elseif ctrl.up or self.auto then
  131. self.v = self.v + 0.1
  132. end
  133. if ctrl.left then
  134. if self.v < 0 then
  135. self.object:set_yaw(self.object:get_yaw() - (1 + dtime) * 0.03)
  136. else
  137. self.object:set_yaw(self.object:get_yaw() + (1 + dtime) * 0.03)
  138. end
  139. elseif ctrl.right then
  140. if self.v < 0 then
  141. self.object:set_yaw(self.object:get_yaw() + (1 + dtime) * 0.03)
  142. else
  143. self.object:set_yaw(self.object:get_yaw() - (1 + dtime) * 0.03)
  144. end
  145. end
  146. end
  147. local velo = self.object:get_velocity()
  148. if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
  149. self.object:set_pos(self.object:get_pos())
  150. return
  151. end
  152. local s = get_sign(self.v)
  153. self.v = self.v - 0.02 * s
  154. if s ~= get_sign(self.v) then
  155. self.object:set_velocity({x = 0, y = 0, z = 0})
  156. self.v = 0
  157. return
  158. end
  159. if math.abs(self.v) > 5 then
  160. self.v = 5 * get_sign(self.v)
  161. end
  162. local p = self.object:get_pos()
  163. p.y = p.y - 0.5
  164. local new_velo
  165. local new_acce = {x = 0, y = 0, z = 0}
  166. if not is_water(p) then
  167. local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
  168. if (not nodedef) or nodedef.walkable then
  169. self.v = 0
  170. new_acce = {x = 0, y = 1, z = 0}
  171. else
  172. new_acce = {x = 0, y = -9.8, z = 0}
  173. end
  174. new_velo = get_velocity(self.v, self.object:get_yaw(),
  175. self.object:get_velocity().y)
  176. self.object:set_pos(self.object:get_pos())
  177. else
  178. p.y = p.y + 1
  179. if is_water(p) then
  180. local y = self.object:get_velocity().y
  181. if y >= 5 then
  182. y = 5
  183. elseif y < 0 then
  184. new_acce = {x = 0, y = 20, z = 0}
  185. else
  186. new_acce = {x = 0, y = 5, z = 0}
  187. end
  188. new_velo = get_velocity(self.v, self.object:get_yaw(), y)
  189. self.object:set_pos(self.object:get_pos())
  190. else
  191. new_acce = {x = 0, y = 0, z = 0}
  192. if math.abs(self.object:get_velocity().y) < 1 then
  193. local pos = self.object:get_pos()
  194. pos.y = math.floor(pos.y) + 0.5
  195. self.object:set_pos(pos)
  196. new_velo = get_velocity(self.v, self.object:get_yaw(), 0)
  197. else
  198. new_velo = get_velocity(self.v, self.object:get_yaw(),
  199. self.object:get_velocity().y)
  200. self.object:set_pos(self.object:get_pos())
  201. end
  202. end
  203. end
  204. self.object:set_velocity(new_velo)
  205. self.object:set_acceleration(new_acce)
  206. end
  207. minetest.register_entity("boats:boat", boat)
  208. minetest.register_craftitem("boats:boat", {
  209. description = "Boat",
  210. inventory_image = "boats_inventory.png",
  211. wield_image = "boats_wield.png",
  212. wield_scale = {x = 2, y = 2, z = 1},
  213. liquids_pointable = true,
  214. groups = {flammable = 2},
  215. on_place = function(itemstack, placer, pointed_thing)
  216. local under = pointed_thing.under
  217. local node = minetest.get_node(under)
  218. local udef = minetest.registered_nodes[node.name]
  219. if udef and udef.on_rightclick and
  220. not (placer and placer:is_player() and
  221. placer:get_player_control().sneak) then
  222. return udef.on_rightclick(under, node, placer, itemstack,
  223. pointed_thing) or itemstack
  224. end
  225. if pointed_thing.type ~= "node" then
  226. return itemstack
  227. end
  228. if not is_water(pointed_thing.under) then
  229. return itemstack
  230. end
  231. pointed_thing.under.y = pointed_thing.under.y + 0.5
  232. boat = minetest.add_entity(pointed_thing.under, "boats:boat")
  233. if boat then
  234. if placer then
  235. boat:set_yaw(placer:get_look_horizontal())
  236. end
  237. local player_name = placer and placer:get_player_name() or ""
  238. if not (creative and creative.is_enabled_for and
  239. creative.is_enabled_for(player_name)) then
  240. itemstack:take_item()
  241. end
  242. end
  243. return itemstack
  244. end,
  245. })
  246. minetest.register_craft({
  247. output = "boats:boat",
  248. recipe = {
  249. {"", "", "" },
  250. {"group:wood", "", "group:wood"},
  251. {"group:wood", "group:wood", "group:wood"},
  252. },
  253. })
  254. minetest.register_craft({
  255. type = "fuel",
  256. recipe = "boats:boat",
  257. burntime = 20,
  258. })