init.lua 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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 row_boat = {
  27. physical = true,
  28. collisionbox = {-1,-0.5,-1, 1,0.5,1},
  29. visual = "mesh",
  30. mesh = "rowboat.b3d",
  31. textures = {"default_wood.png"},
  32. driver = nil,
  33. v = 0,
  34. stepcount = 0,
  35. unattended = 0
  36. }
  37. function row_boat.on_rightclick(self, clicker)
  38. if not clicker or not clicker:is_player() then
  39. return
  40. end
  41. local name = clicker:get_player_name()
  42. if self.driver and clicker == self.driver then
  43. self.driver = nil
  44. clicker:set_detach()
  45. default.player_attached[name] = false
  46. default.player_set_animation(clicker, "stand" , 30)
  47. elseif not self.driver then
  48. self.driver = clicker
  49. clicker:set_attach(self.object, "", {x=0,y=1,z=-5}, {x=0,y=0,z=0})
  50. default.player_attached[name] = true
  51. minetest.after(0.2, function()
  52. default.player_set_animation(clicker, "sit" , 30)
  53. end)
  54. self.object:setyaw(clicker:get_look_yaw() - math.pi / 2)
  55. end
  56. end
  57. function row_boat.on_activate(self, staticdata, dtime_s)
  58. self.object:set_armor_groups({fleshy = 100})
  59. if staticdata then
  60. self.v = tonumber(staticdata)
  61. end
  62. self.last_v = self.v
  63. end
  64. function row_boat.get_staticdata(self)
  65. return tostring(self.v)
  66. end
  67. function row_boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, direction)
  68. if not puncher or not puncher:is_player() or self.removed then
  69. return
  70. end
  71. puncher:set_detach()
  72. default.player_attached[puncher:get_player_name()] = false
  73. self.removed = true
  74. -- delay remove to ensure player is detached
  75. minetest.after(0.1, function()
  76. self.object:remove()
  77. end)
  78. if not minetest.setting_getbool("creative_mode") then
  79. puncher:get_inventory():add_item("main", "boats:row_boat")
  80. end
  81. end
  82. function row_boat.on_step(self, dtime)
  83. self.v = get_v(self.object:getvelocity()) * get_sign(self.v)
  84. if self.driver then
  85. local ctrl = self.driver:get_player_control()
  86. local yaw = self.object:getyaw()
  87. if ctrl.up then
  88. self.v = self.v + 0.1
  89. end
  90. if ctrl.down then
  91. self.v = self.v - 0.08
  92. end
  93. if ctrl.left then
  94. if ctrl.down then
  95. self.object:setyaw(yaw - (1 + dtime) * 0.03)
  96. else
  97. self.object:setyaw(yaw + (1 + dtime) * 0.03)
  98. end
  99. end
  100. if ctrl.right then
  101. if ctrl.down then
  102. self.object:setyaw(yaw + (1 + dtime) * 0.03)
  103. else
  104. self.object:setyaw(yaw - (1 + dtime) * 0.03)
  105. end
  106. end
  107. end
  108. local velo = self.object:getvelocity()
  109. if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
  110. return
  111. end
  112. local s = get_sign(self.v)
  113. self.v = self.v - 0.02 * s
  114. if s ~= get_sign(self.v) then
  115. self.object:setvelocity({x = 0, y = 0, z = 0})
  116. self.v = 0
  117. return
  118. end
  119. if math.abs(self.v) > 4.5 then
  120. self.v = 4.5 * get_sign(self.v)
  121. end
  122. local p = self.object:getpos()
  123. p.y = p.y - 0.5
  124. local new_velo = {x = 0, y = 0, z = 0}
  125. local new_acce = {x = 0, y = 0, z = 0}
  126. if not is_water(p) then
  127. local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
  128. if (not nodedef) or nodedef.walkable then
  129. self.v = 0
  130. new_acce = {x = 0, y = 1, z = 0}
  131. else
  132. new_acce = {x = 0, y = -9.8, z = 0} -- freefall in air -9.81
  133. end
  134. new_velo = get_velocity(self.v, self.object:getyaw(), self.object:getvelocity().y)
  135. else
  136. p.y = p.y + 1
  137. if is_water(p) then
  138. new_acce = {x = 0, y = 3, z = 0}
  139. local y = self.object:getvelocity().y
  140. if y > 2 then
  141. y = 2
  142. end
  143. if y < 0 then
  144. self.object:setacceleration({x = 0, y = 10, z = 0})
  145. end
  146. new_velo = get_velocity(self.v, self.object:getyaw(), y)
  147. else
  148. new_acce = {x = 0, y = 0, z = 0}
  149. if math.abs(self.object:getvelocity().y) <= 2 then
  150. local pos = self.object:getpos()
  151. pos.y = math.floor(pos.y) + 0.5
  152. self.object:setpos(pos)
  153. new_velo = get_velocity(self.v, self.object:getyaw(), 0)
  154. else
  155. new_velo = get_velocity(self.v, self.object:getyaw(), self.object:getvelocity().y)
  156. end
  157. end
  158. end
  159. self.object:setvelocity(new_velo)
  160. self.object:setacceleration(new_acce)
  161. end
  162. minetest.register_entity("boats:row_boat", row_boat)
  163. minetest.register_craftitem("boats:row_boat", {
  164. description = "Boat",
  165. inventory_image = "rowboat_inventory.png",
  166. wield_image = "rowboat_wield.png",
  167. wield_scale = {x = 2, y = 2, z = 1},
  168. liquids_pointable = true,
  169. on_place = function(itemstack, placer, pointed_thing)
  170. if pointed_thing.type ~= "node" then
  171. return
  172. end
  173. if not is_water(pointed_thing.under) then
  174. return
  175. end
  176. pointed_thing.under.y = pointed_thing.under.y + 0.5
  177. minetest.add_entity(pointed_thing.under, "boats:row_boat")
  178. if not minetest.setting_getbool("creative_mode") then
  179. itemstack:take_item()
  180. end
  181. return itemstack
  182. end,
  183. })
  184. minetest.register_craft({
  185. output = "boats:row_boat",
  186. recipe = {
  187. {"", "", "" },
  188. {"group:wood", "", "group:wood"},
  189. {"group:wood", "group:wood", "group:wood"},
  190. },
  191. })
  192. local sail_boat = {
  193. physical = true,
  194. collisionbox = {-1,-0.5,-1, 1,2,1},
  195. visual = "mesh",
  196. mesh = "sailboat.b3d",
  197. textures = {"sailboat.png"},
  198. driver = nil,
  199. v = 0,
  200. stepcount = 0,
  201. unattended = 0
  202. }
  203. function sail_boat.on_rightclick(self, clicker)
  204. if not clicker or not clicker:is_player() then
  205. return
  206. end
  207. local name = clicker:get_player_name()
  208. if self.driver and clicker == self.driver then
  209. self.driver = nil
  210. clicker:set_detach()
  211. default.player_attached[name] = false
  212. default.player_set_animation(clicker, "stand" , 30)
  213. elseif not self.driver then
  214. self.driver = clicker
  215. clicker:set_attach(self.object, "", {x=0,y=1,z=0}, {x=0,y=0,z=0})
  216. default.player_attached[name] = true
  217. minetest.after(0.2, function()
  218. default.player_set_animation(clicker, "sit" , 30)
  219. end)
  220. self.object:setyaw(clicker:get_look_yaw() - math.pi / 2)
  221. end
  222. end
  223. function sail_boat.on_activate(self, staticdata, dtime_s)
  224. self.object:set_armor_groups({fleshy = 100})
  225. if staticdata then
  226. self.v = tonumber(staticdata)
  227. end
  228. self.last_v = self.v
  229. end
  230. function sail_boat.get_staticdata(self)
  231. return tostring(self.v)
  232. end
  233. function sail_boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, direction)
  234. if not puncher or not puncher:is_player() or self.removed then
  235. return
  236. end
  237. puncher:set_detach()
  238. default.player_attached[puncher:get_player_name()] = false
  239. self.removed = true
  240. -- delay remove to ensure player is detached
  241. minetest.after(0.1, function()
  242. self.object:remove()
  243. end)
  244. if not minetest.setting_getbool("creative_mode") then
  245. puncher:get_inventory():add_item("main", "boats:sail_boat")
  246. end
  247. end
  248. function sail_boat.on_step(self, dtime)
  249. self.v = get_v(self.object:getvelocity()) * get_sign(self.v)
  250. if self.driver then
  251. local ctrl = self.driver:get_player_control()
  252. local yaw = self.object:getyaw()
  253. if ctrl.up then
  254. self.v = self.v + 0.1
  255. end
  256. if ctrl.down then
  257. self.v = self.v - 0.08
  258. end
  259. if ctrl.left then
  260. if ctrl.down then
  261. self.object:setyaw(yaw - (1 + dtime) * 0.03)
  262. else
  263. self.object:setyaw(yaw + (1 + dtime) * 0.03)
  264. end
  265. end
  266. if ctrl.right then
  267. if ctrl.down then
  268. self.object:setyaw(yaw + (1 + dtime) * 0.03)
  269. else
  270. self.object:setyaw(yaw - (1 + dtime) * 0.03)
  271. end
  272. end
  273. end
  274. local velo = self.object:getvelocity()
  275. if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
  276. return
  277. end
  278. local s = get_sign(self.v)
  279. self.v = self.v - 0.015 * s
  280. if s ~= get_sign(self.v) then
  281. self.object:setvelocity({x = 0, y = 0, z = 0})
  282. self.v = 0
  283. return
  284. end
  285. if math.abs(self.v) > 7.5 then
  286. self.v = 7.5 * get_sign(self.v)
  287. end
  288. local p = self.object:getpos()
  289. p.y = p.y - 0.5
  290. local new_velo = {x = 0, y = 0, z = 0}
  291. local new_acce = {x = 0, y = 0, z = 0}
  292. if not is_water(p) then
  293. local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
  294. if (not nodedef) or nodedef.walkable then
  295. self.v = 0
  296. new_acce = {x = 0, y = 1, z = 0}
  297. else
  298. new_acce = {x = 0, y = -9.8, z = 0} -- freefall in air -9.81
  299. end
  300. new_velo = get_velocity(self.v, self.object:getyaw(), self.object:getvelocity().y)
  301. else
  302. p.y = p.y + 1
  303. if is_water(p) then
  304. new_acce = {x = 0, y = 3, z = 0}
  305. local y = self.object:getvelocity().y
  306. if y > 2 then
  307. y = 2
  308. end
  309. if y < 0 then
  310. self.object:setacceleration({x = 0, y = 10, z = 0})
  311. end
  312. new_velo = get_velocity(self.v, self.object:getyaw(), y)
  313. else
  314. new_acce = {x = 0, y = 0, z = 0}
  315. if math.abs(self.object:getvelocity().y) <= 2 then
  316. local pos = self.object:getpos()
  317. pos.y = math.floor(pos.y) + 0.5
  318. self.object:setpos(pos)
  319. new_velo = get_velocity(self.v, self.object:getyaw(), 0)
  320. else
  321. new_velo = get_velocity(self.v, self.object:getyaw(), self.object:getvelocity().y)
  322. end
  323. end
  324. end
  325. self.object:setvelocity(new_velo)
  326. self.object:setacceleration(new_acce)
  327. end
  328. minetest.register_entity("boats:sail_boat", sail_boat)
  329. minetest.register_craftitem("boats:sail_boat", {
  330. description = "Sail Boat",
  331. inventory_image = "sailboat_inventory.png",
  332. wield_image = "sailboat_wield.png",
  333. wield_scale = {x=2, y=2, z=1},
  334. liquids_pointable = true,
  335. on_place = function(itemstack, placer, pointed_thing)
  336. if pointed_thing.type ~= "node" then
  337. return
  338. end
  339. if not is_water(pointed_thing.under) then
  340. return
  341. end
  342. pointed_thing.under.y = pointed_thing.under.y+0.5
  343. minetest.add_entity(pointed_thing.under, "boats:sail_boat")
  344. if not minetest.setting_getbool("creative_mode") then
  345. itemstack:take_item()
  346. end
  347. return itemstack
  348. end,
  349. })
  350. minetest.register_craft({
  351. output = "boats:sail_boat",
  352. recipe = {
  353. {"", "wool:white", ""},
  354. {"group:wood", "wool:white", "group:wood"},
  355. {"group:tree", "boats:row_boat", "group:tree"},
  356. },
  357. })