init.lua 6.8 KB

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