init.lua 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  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. }
  39. function boat.on_rightclick(self, clicker)
  40. if not clicker or not clicker:is_player() then
  41. return
  42. end
  43. local name = clicker:get_player_name()
  44. if self.driver and clicker == self.driver then
  45. self.driver = nil
  46. clicker:set_detach()
  47. player_api.player_attached[name] = false
  48. player_api.set_animation(clicker, "stand" , 30)
  49. local pos = clicker:getpos()
  50. pos = {x = pos.x, y = pos.y + 0.2, z = pos.z}
  51. minetest.after(0.1, function()
  52. clicker:setpos(pos)
  53. end)
  54. elseif not self.driver then
  55. local attach = clicker:get_attach()
  56. if attach and attach:get_luaentity() then
  57. local luaentity = attach:get_luaentity()
  58. if luaentity.driver then
  59. luaentity.driver = nil
  60. end
  61. clicker:set_detach()
  62. end
  63. self.driver = clicker
  64. clicker:set_attach(self.object, "",
  65. {x = 0.5, y = 1, z = -3}, {x = 0, y = 0, z = 0})
  66. player_api.player_attached[name] = true
  67. minetest.after(0.2, function()
  68. player_api.set_animation(clicker, "sit" , 30)
  69. end)
  70. clicker:set_look_horizontal(self.object:getyaw())
  71. end
  72. end
  73. function boat.on_activate(self, staticdata, dtime_s)
  74. self.object:set_armor_groups({immortal = 1})
  75. if staticdata then
  76. self.v = tonumber(staticdata)
  77. end
  78. self.last_v = self.v
  79. end
  80. function boat.get_staticdata(self)
  81. return tostring(self.v)
  82. end
  83. function boat.on_punch(self, puncher)
  84. if not puncher or not puncher:is_player() or self.removed then
  85. return
  86. end
  87. if self.driver and puncher == self.driver then
  88. self.driver = nil
  89. puncher:set_detach()
  90. player_api.player_attached[puncher:get_player_name()] = false
  91. end
  92. if not self.driver then
  93. self.removed = true
  94. local inv = puncher:get_inventory()
  95. if not (creative and creative.is_enabled_for
  96. and creative.is_enabled_for(puncher:get_player_name()))
  97. or not inv:contains_item("main", "boats:boat") then
  98. local leftover = inv:add_item("main", "boats:boat")
  99. -- if no room in inventory add a replacement boat to the world
  100. if not leftover:is_empty() then
  101. minetest.add_item(self.object:getpos(), leftover)
  102. end
  103. end
  104. -- delay remove to ensure player is detached
  105. minetest.after(0.1, function()
  106. self.object:remove()
  107. end)
  108. end
  109. end
  110. function boat.on_step(self, dtime)
  111. self.v = get_v(self.object:getvelocity()) * get_sign(self.v)
  112. if self.driver then
  113. local ctrl = self.driver:get_player_control()
  114. local yaw = self.object:getyaw()
  115. if ctrl.up then
  116. self.v = self.v + 0.1
  117. elseif ctrl.down then
  118. self.v = self.v - 0.1
  119. end
  120. if ctrl.left then
  121. if self.v < 0 then
  122. self.object:setyaw(yaw - (1 + dtime) * 0.03)
  123. else
  124. self.object:setyaw(yaw + (1 + dtime) * 0.03)
  125. end
  126. elseif ctrl.right then
  127. if self.v < 0 then
  128. self.object:setyaw(yaw + (1 + dtime) * 0.03)
  129. else
  130. self.object:setyaw(yaw - (1 + dtime) * 0.03)
  131. end
  132. end
  133. end
  134. local velo = self.object:getvelocity()
  135. if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
  136. self.object:setpos(self.object:getpos())
  137. return
  138. end
  139. local s = get_sign(self.v)
  140. self.v = self.v - 0.02 * s
  141. if s ~= get_sign(self.v) then
  142. self.object:setvelocity({x = 0, y = 0, z = 0})
  143. self.v = 0
  144. return
  145. end
  146. if math.abs(self.v) > 5 then
  147. self.v = 5 * get_sign(self.v)
  148. end
  149. local p = self.object:getpos()
  150. p.y = p.y - 0.5
  151. local new_velo
  152. local new_acce = {x = 0, y = 0, z = 0}
  153. if not is_water(p) then
  154. local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
  155. if (not nodedef) or nodedef.walkable then
  156. self.v = 0
  157. new_acce = {x = 0, y = 1, z = 0}
  158. else
  159. new_acce = {x = 0, y = -9.8, z = 0}
  160. end
  161. new_velo = get_velocity(self.v, self.object:getyaw(),
  162. self.object:getvelocity().y)
  163. self.object:setpos(self.object:getpos())
  164. else
  165. p.y = p.y + 1
  166. if is_water(p) then
  167. local y = self.object:getvelocity().y
  168. if y >= 5 then
  169. y = 5
  170. elseif y < 0 then
  171. new_acce = {x = 0, y = 20, z = 0}
  172. else
  173. new_acce = {x = 0, y = 5, z = 0}
  174. end
  175. new_velo = get_velocity(self.v, self.object:getyaw(), y)
  176. self.object:setpos(self.object:getpos())
  177. else
  178. new_acce = {x = 0, y = 0, z = 0}
  179. if math.abs(self.object:getvelocity().y) < 1 then
  180. local pos = self.object:getpos()
  181. pos.y = math.floor(pos.y) + 0.5
  182. self.object:setpos(pos)
  183. new_velo = get_velocity(self.v, self.object:getyaw(), 0)
  184. else
  185. new_velo = get_velocity(self.v, self.object:getyaw(),
  186. self.object:getvelocity().y)
  187. self.object:setpos(self.object:getpos())
  188. end
  189. end
  190. end
  191. self.object:setvelocity(new_velo)
  192. self.object:setacceleration(new_acce)
  193. end
  194. minetest.register_entity("boats:boat", boat)
  195. minetest.register_craftitem("boats:boat", {
  196. description = "Boat",
  197. inventory_image = "boats_inventory.png",
  198. wield_image = "boats_wield.png",
  199. wield_scale = {x = 2, y = 2, z = 1},
  200. liquids_pointable = true,
  201. groups = {flammable = 2},
  202. stack_max = 1,
  203. on_place = function(itemstack, placer, pointed_thing)
  204. local under = pointed_thing.under
  205. local node = minetest.get_node(under)
  206. local udef = minetest.registered_nodes[node.name]
  207. if udef and udef.on_rightclick and
  208. not (placer and placer:is_player() and
  209. placer:get_player_control().sneak) then
  210. return udef.on_rightclick(under, node, placer, itemstack,
  211. pointed_thing) or itemstack
  212. end
  213. if pointed_thing.type ~= "node" then
  214. return itemstack
  215. end
  216. if not is_water(pointed_thing.under) then
  217. return itemstack
  218. end
  219. pointed_thing.under.y = pointed_thing.under.y + 0.5
  220. boat = minetest.add_entity(pointed_thing.under, "boats:boat")
  221. if boat then
  222. if placer then
  223. boat:setyaw(placer:get_look_horizontal())
  224. end
  225. local player_name = placer and placer:get_player_name() or ""
  226. if not (creative and creative.is_enabled_for and
  227. creative.is_enabled_for(player_name)) then
  228. itemstack:take_item()
  229. end
  230. end
  231. return itemstack
  232. end,
  233. })
  234. minetest.register_craft({
  235. output = "boats:boat",
  236. recipe = {
  237. {"", "", "" },
  238. {"group:wood", "", "group:wood"},
  239. {"group:wood", "group:wood", "group:wood"},
  240. },
  241. })
  242. minetest.register_craft({
  243. type = "fuel",
  244. recipe = "boats:boat",
  245. burntime = 20,
  246. })
  247. -- ---------------------------------------------------
  248. -- Tin boat
  249. local boat_tin = {
  250. physical = true,
  251. -- Warning: Do not change the position of the collisionbox top surface,
  252. -- lowering it causes the boat to fall through the world if underwater
  253. collisionbox = {-0.35, -0.35, -0.35, 0.35, 0.3, 0.35},
  254. visual = "mesh",
  255. mesh = "boats_canoe.obj",
  256. textures = {"default_tin_block.png"},
  257. driver = nil,
  258. v = 0,
  259. last_v = 0,
  260. removed = false
  261. }
  262. function boat_tin.on_rightclick(self, clicker)
  263. if not clicker or not clicker:is_player() then
  264. return
  265. end
  266. local name = clicker:get_player_name()
  267. if self.driver and clicker == self.driver then
  268. self.driver = nil
  269. clicker:set_detach()
  270. player_api.player_attached[name] = false
  271. player_api.set_animation(clicker, "stand" , 30)
  272. local pos = clicker:getpos()
  273. pos = {x = pos.x, y = pos.y + 0.2, z = pos.z}
  274. minetest.after(0.1, function()
  275. clicker:setpos(pos)
  276. end)
  277. elseif not self.driver then
  278. local attach = clicker:get_attach()
  279. if attach and attach:get_luaentity() then
  280. local luaentity = attach:get_luaentity()
  281. if luaentity.driver then
  282. luaentity.driver = nil
  283. end
  284. clicker:set_detach()
  285. end
  286. self.driver = clicker
  287. clicker:set_attach(self.object, "",
  288. {x = 0.5, y = 1, z = -3}, {x = 0, y = 0, z = 0})
  289. player_api.player_attached[name] = true
  290. minetest.after(0.2, function()
  291. player_api.set_animation(clicker, "sit" , 30)
  292. end)
  293. clicker:set_look_horizontal(self.object:getyaw())
  294. end
  295. end
  296. function boat_tin.on_activate(self, staticdata, dtime_s)
  297. self.object:set_armor_groups({immortal = 1})
  298. if staticdata then
  299. self.v = tonumber(staticdata)
  300. end
  301. self.last_v = self.v
  302. end
  303. function boat_tin.get_staticdata(self)
  304. return tostring(self.v)
  305. end
  306. function boat_tin.on_punch(self, puncher)
  307. if not puncher or not puncher:is_player() or self.removed then
  308. return
  309. end
  310. if self.driver and puncher == self.driver then
  311. self.driver = nil
  312. puncher:set_detach()
  313. player_api.player_attached[puncher:get_player_name()] = false
  314. end
  315. if not self.driver then
  316. self.removed = true
  317. local inv = puncher:get_inventory()
  318. if not (creative and creative.is_enabled_for
  319. and creative.is_enabled_for(puncher:get_player_name()))
  320. or not inv:contains_item("main", "boats:boat_tin") then
  321. local leftover = inv:add_item("main", "boats:boat_tin")
  322. -- if no room in inventory add a replacement boat to the world
  323. if not leftover:is_empty() then
  324. minetest.add_item(self.object:getpos(), leftover)
  325. end
  326. end
  327. -- delay remove to ensure player is detached
  328. minetest.after(0.1, function()
  329. self.object:remove()
  330. end)
  331. end
  332. end
  333. function boat_tin.on_step(self, dtime)
  334. self.v = get_v(self.object:getvelocity()) * get_sign(self.v)
  335. if self.driver then
  336. local ctrl = self.driver:get_player_control()
  337. local yaw = self.object:getyaw()
  338. if ctrl.up then
  339. self.v = self.v + 0.2
  340. elseif ctrl.down then
  341. self.v = self.v - 0.2
  342. end
  343. if ctrl.left then
  344. if self.v < 0 then
  345. self.object:setyaw(yaw - (1 + dtime) * 0.04)
  346. else
  347. self.object:setyaw(yaw + (1 + dtime) * 0.04)
  348. end
  349. elseif ctrl.right then
  350. if self.v < 0 then
  351. self.object:setyaw(yaw + (1 + dtime) * 0.04)
  352. else
  353. self.object:setyaw(yaw - (1 + dtime) * 0.04)
  354. end
  355. end
  356. end
  357. local velo = self.object:getvelocity()
  358. if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
  359. self.object:setpos(self.object:getpos())
  360. return
  361. end
  362. local s = get_sign(self.v)
  363. self.v = self.v - 0.002 * s
  364. if s ~= get_sign(self.v) then
  365. self.object:setvelocity({x = 0, y = 0, z = 0})
  366. self.v = 0
  367. return
  368. end
  369. if math.abs(self.v) > 15 then
  370. self.v = 15 * get_sign(self.v)
  371. end
  372. local p = self.object:getpos()
  373. p.y = p.y - 0.5
  374. local new_velo
  375. local new_acce = {x = 0, y = 0, z = 0}
  376. if not is_water(p) then
  377. local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
  378. if (not nodedef) or nodedef.walkable then
  379. self.v = 0
  380. new_acce = {x = 0, y = 1, z = 0}
  381. else
  382. new_acce = {x = 0, y = -9.8, z = 0}
  383. end
  384. new_velo = get_velocity(self.v, self.object:getyaw(),
  385. self.object:getvelocity().y)
  386. self.object:setpos(self.object:getpos())
  387. else
  388. p.y = p.y + 1
  389. if is_water(p) then
  390. local y = self.object:getvelocity().y
  391. if y >= 5 then
  392. y = 5
  393. elseif y < 0 then
  394. new_acce = {x = 0, y = 3, z = 0} -- float parameters. bigger = bouncier
  395. else
  396. new_acce = {x = 0, y = 1, z = 0}
  397. end
  398. new_velo = get_velocity(self.v, self.object:getyaw(), y)
  399. self.object:setpos(self.object:getpos())
  400. else
  401. new_acce = {x = 0, y = 0, z = 0}
  402. if math.abs(self.object:getvelocity().y) < 1 then
  403. local pos = self.object:getpos()
  404. pos.y = math.floor(pos.y) + 0.5
  405. self.object:setpos(pos)
  406. new_velo = get_velocity(self.v, self.object:getyaw(), 0)
  407. else
  408. new_velo = get_velocity(self.v, self.object:getyaw(),
  409. self.object:getvelocity().y)
  410. self.object:setpos(self.object:getpos())
  411. end
  412. end
  413. end
  414. self.object:setvelocity(new_velo)
  415. self.object:setacceleration(new_acce)
  416. end
  417. minetest.register_entity("boats:boat_tin", boat_tin)
  418. minetest.register_craftitem("boats:boat_tin", {
  419. description = "Metal Boat",
  420. inventory_image = "boats_tin_inventory.png",
  421. wield_image = "boats_tin_wield.png",
  422. wield_scale = {x = 2, y = 2, z = 1},
  423. liquids_pointable = true,
  424. groups = {flammable = 2},
  425. stack_max = 1,
  426. on_place = function(itemstack, placer, pointed_thing)
  427. local under = pointed_thing.under
  428. local node = minetest.get_node(under)
  429. local udef = minetest.registered_nodes[node.name]
  430. if udef and udef.on_rightclick and
  431. not (placer and placer:is_player() and
  432. placer:get_player_control().sneak) then
  433. return udef.on_rightclick(under, node, placer, itemstack,
  434. pointed_thing) or itemstack
  435. end
  436. if pointed_thing.type ~= "node" then
  437. return itemstack
  438. end
  439. if not is_water(pointed_thing.under) then
  440. return itemstack
  441. end
  442. pointed_thing.under.y = pointed_thing.under.y + 0.5
  443. boat = minetest.add_entity(pointed_thing.under, "boats:boat_tin")
  444. if boat then
  445. if placer then
  446. boat:setyaw(placer:get_look_horizontal())
  447. end
  448. local player_name = placer and placer:get_player_name() or ""
  449. if not (creative and creative.is_enabled_for and
  450. creative.is_enabled_for(player_name)) then
  451. itemstack:take_item()
  452. end
  453. end
  454. return itemstack
  455. end,
  456. })
  457. minetest.register_craft({
  458. output = "boats:boat_tin",
  459. recipe = {
  460. {"", "", "" },
  461. {"default:tin_ingot", "", "default:tin_ingot"},
  462. {"default:tin_ingot", "default:tin_ingot", "default:tin_ingot"},
  463. },
  464. })
  465. -- ---------------------------------------------------
  466. -- Steel boat
  467. local boat_steel = {
  468. physical = true,
  469. -- Warning: Do not change the position of the collisionbox top surface,
  470. -- lowering it causes the boat to fall through the world if underwater
  471. collisionbox = {-2.35, -01.35, -2.35, 02.35, 0.3, 02.35},
  472. visual = "mesh",
  473. visual_size = {x=8,y=8,z=8},
  474. mesh = "cargo_ship.obj",
  475. textures = {"default_bronze_block.png"},
  476. driver = nil,
  477. v = 0,
  478. last_v = 0,
  479. removed = false,
  480. -- box = nil,
  481. }
  482. -- minetest.register_entity("boats:boat_cargo_box", boat_cargo_box)
  483. local modpath = minetest.get_modpath("boats")
  484. local mod_storage = minetest.get_mod_storage()
  485. local boat_data = {}
  486. boat_data.objects = {}
  487. boat_data.next_entity = mod_storage:get_int("next_entity") or 1
  488. boat_data.entities = minetest.deserialize(mod_storage:get_string("entities")) or {}
  489. if type(boat_data.entities) ~= "table" then
  490. boat_data.entities = {}
  491. end
  492. -- recreate detached inventories
  493. for id,v in pairs(boat_data.entities) do
  494. local inv1 = minetest.create_detached_inventory("boats_steel_boat_"..id.."_hold1")
  495. local inv2 = minetest.create_detached_inventory("boats_steel_boat_"..id.."_hold2")
  496. local inv3 = minetest.create_detached_inventory("boats_steel_boat_"..id.."_hold3")
  497. inv1:set_size(5 * 8)
  498. inv2:set_size(5 * 8)
  499. inv3:set_size(5 * 8)
  500. if v.inventories then
  501. inv1:set_lists(v.inventories[1])
  502. inv2:set_lists(v.inventories[2])
  503. inv3:set_lists(v.inventories[3])
  504. end
  505. end
  506. local function serialize_inventory(id, n)
  507. local inv = minetest.get_inventory({type="detached", name="boats_steel_boat_"..id.."_hold"..n})
  508. return inv:get_lists()
  509. end
  510. local function save_data()
  511. --print("saving")
  512. mod_storage:set_int("next_entity", boat_data.next_entity);
  513. for id,v in pairs(boat_data.entities) do
  514. v.inventories[1] = serialize_inventory(id, 1)
  515. v.inventories[2] = serialize_inventory(id, 2)
  516. v.inventories[3] = serialize_inventory(id, 3)
  517. end
  518. mod_storage:set_string("entities", minetest.serialize(boat_data.entities))
  519. end
  520. local function deploy_boat(boat)
  521. local id = boat_data.next_entity
  522. boat_data.next_entity = boat_data.next_entity + 1
  523. boat.data = boat.data or {}
  524. boat.data.id = id
  525. boat.data.driver = nil
  526. boat_data.objects[id] = boat
  527. boat_data.entities[id] = {
  528. id = id,
  529. inventories = {}
  530. }
  531. local inv1 = minetest.create_detached_inventory("boats_steel_boat_"..id.."_hold1")
  532. local inv2 = minetest.create_detached_inventory("boats_steel_boat_"..id.."_hold2")
  533. local inv3 = minetest.create_detached_inventory("boats_steel_boat_"..id.."_hold3")
  534. inv1:set_size(5 * 8)
  535. inv2:set_size(5 * 8)
  536. inv3:set_size(5 * 8)
  537. save_data()
  538. end
  539. local function enter_boat(self, clicker)
  540. local name = clicker:get_player_name()
  541. if not self.driver then
  542. local attach = clicker:get_attach()
  543. if attach and attach:get_luaentity() then
  544. local luaentity = attach:get_luaentity()
  545. if luaentity.driver then
  546. luaentity.driver = nil
  547. end
  548. clicker:set_detach()
  549. end
  550. self.driver = clicker
  551. clicker:set_attach(self.object, "",
  552. {x = 0.5, y = 1, z = -3}, {x = 0, y = 0, z = 0})
  553. player_api.player_attached[name] = true
  554. minetest.after(0.2, function()
  555. player_api.set_animation(clicker, "sit" , 30)
  556. end)
  557. clicker:set_look_horizontal(self.object:getyaw())
  558. end
  559. end
  560. local function get_steel_boat_formspec()
  561. local state_str = "Sailing"
  562. return "" ..
  563. "size[10,8;]" ..
  564. default.gui_bg ..
  565. default.gui_bg_img ..
  566. default.gui_slots ..
  567. "label[1,1;"..state_str.."]" ..
  568. "button[5,1;5,1;board;Board]" ..
  569. ""
  570. end
  571. local function get_steel_boat_inv_formspec(boat, hold)
  572. local state_str = "Sailing"
  573. return "size[8,8.5]"..
  574. default.gui_bg..
  575. default.gui_bg_img..
  576. default.gui_slots..
  577. "list[context;hold;4,2;3,2;]"..
  578. "image[2,2.5;1,1;default_furnace_fire_bg.png]"..
  579. "list[current_player;main;0,4.25;8,1;]"..
  580. "list[current_player;main;0,5.5;8,3;8]"..
  581. "listring[context;dst]"..
  582. "listring[current_player;main]"..
  583. "listring[context;src]"..
  584. "listring[current_player;main]"..
  585. "listring[context;fuel]"..
  586. "listring[current_player;main]"..
  587. default.get_hotbar_bg(0, 4.25)
  588. end
  589. function boat_steel.on_rightclick(self, clicker)
  590. if not clicker or not clicker:is_player() then
  591. return
  592. end
  593. local name = clicker:get_player_name()
  594. if self.driver and clicker == self.driver then
  595. -- exit boat
  596. self.driver = nil
  597. clicker:set_detach()
  598. player_api.player_attached[name] = false
  599. player_api.set_animation(clicker, "stand" , 30)
  600. local pos = clicker:getpos()
  601. pos = {x = pos.x, y = pos.y + 0.2, z = pos.z}
  602. minetest.after(0.1, function()
  603. clicker:setpos(pos)
  604. end)
  605. else
  606. -- show formspec
  607. minetest.show_formspec(clicker:get_player_name(), "boats:steel_boat_formQ"..self.data.id, get_steel_boat_formspec(self))
  608. end
  609. end
  610. local function splitname(name)
  611. local c = string.find(name, "Q")
  612. if c == nil then return nil, nil end
  613. --print("c " ..c)
  614. return string.sub(name, 1, c - 1), string.sub(name, c + 1, string.len(name))
  615. end
  616. minetest.register_on_player_receive_fields(function(player, formname, fields)
  617. local formprefix, id = splitname(formname)
  618. if formprefix ~= "boats:steel_boat_form" then
  619. --print("wrong prefix: " .. formname .. " - " .. formprefix)
  620. return
  621. end
  622. if fields.board then
  623. id = id + 0
  624. local boat = boat_data.objects[id]
  625. print("id ".. id)
  626. if not boat then
  627. print("no boat " .. dump(boat) .. " " .. dump(id))
  628. print(dump(boat_data))
  629. --enter_boat(boat, player)
  630. else
  631. enter_boat(boat, player)
  632. end
  633. return
  634. elseif fields.hold_a then
  635. minetest.show_formspec(player:get_player_name(), "boats:steel_boat_formQ"..self.data.id, get_steel_boat_inv_formspec(self, "a"))
  636. end
  637. end)
  638. minetest.register_node("boats:bollard", {
  639. paramtype = "light",
  640. description = "Mooring Bollard",
  641. tiles = {"default_bronze_block.png", "default_bronze_block.png", "default_bronze_block.png",
  642. "default_bronze_block.png", "default_bronze_block.png", "default_bronze_block.png"},
  643. node_box = {
  644. type = "fixed",
  645. fixed = {
  646. --11.25
  647. {-0.49, -0.5, -0.10, 0.49, 0.5, 0.10},
  648. {-0.10, -0.5, -0.49, 0.10, 0.5, 0.49},
  649. --22.5
  650. {-0.46, -0.5, -0.19, 0.46, 0.5, 0.19},
  651. {-0.19, -0.5, -0.46, 0.19, 0.5, 0.46},
  652. -- 33.75
  653. {-0.416, -0.5, -0.28, 0.416, 0.5, 0.28},
  654. {-0.28, -0.5, -0.416, 0.28, 0.5, 0.416},
  655. --45
  656. {-0.35, -0.5, -0.35, 0.35, 0.5, 0.35},
  657. },
  658. },
  659. selection_box = {
  660. type = "fixed",
  661. fixed = {
  662. {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
  663. },
  664. },
  665. drawtype = "nodebox",
  666. groups = {cracky=3,oddly_breakable_by_hand=3 },
  667. legacy_facedir_simple = true,
  668. sounds = default.node_sound_wood_defaults(),
  669. on_construct = function(pos)
  670. end,
  671. })
  672. function boat_steel.on_activate(self, staticdata, dtime_s)
  673. self.object:set_armor_groups({immortal = 1})
  674. if staticdata then
  675. self.data = minetest.deserialize(staticdata)
  676. if not self.data or not self.data.id then
  677. deploy_boat(self)
  678. end
  679. print("self.data.id = "..self.data.id)
  680. print(dump(self))
  681. boat_data.objects[self.data.id] = self
  682. else
  683. self.data = {}
  684. print("steel boat with no staticdata")
  685. end
  686. end
  687. function boat_steel.get_staticdata(self)
  688. return minetest.serialize(self.data)
  689. end
  690. function boat_steel.on_punch(self, puncher)
  691. if not puncher or not puncher:is_player() or self.removed then
  692. return
  693. end
  694. if self.driver and puncher == self.driver then
  695. self.driver = nil
  696. puncher:set_detach()
  697. player_api.player_attached[puncher:get_player_name()] = false
  698. end
  699. if not self.driver then
  700. self.removed = true
  701. local inv = puncher:get_inventory()
  702. if not (creative and creative.is_enabled_for
  703. and creative.is_enabled_for(puncher:get_player_name()))
  704. or not inv:contains_item("main", "boats:boat_steel") then
  705. local leftover = inv:add_item("main", "boats:boat_steel")
  706. -- if no room in inventory add a replacement boat to the world
  707. if not leftover:is_empty() then
  708. minetest.add_item(self.object:getpos(), leftover)
  709. end
  710. end
  711. -- delay remove to ensure player is detached
  712. minetest.after(0.1, function()
  713. self.object:remove()
  714. end)
  715. end
  716. end
  717. function boat_steel.on_step(self, dtime)
  718. self.v = get_v(self.object:getvelocity()) * get_sign(self.v)
  719. local yaw = self.object:getyaw()
  720. local bp = self.object:getpos()
  721. local velo = self.object:getvelocity()
  722. bp.y = bp.y + 4
  723. local speed = vector.length(velo)
  724. if self.driver then
  725. local ctrl = self.driver:get_player_control()
  726. local yaw = self.object:getyaw()
  727. if ctrl.up then
  728. self.v = self.v + 0.02
  729. elseif ctrl.down then
  730. self.v = self.v - 0.02
  731. end
  732. if ctrl.left then
  733. if self.v < 0 then
  734. self.object:setyaw(yaw - (1 + dtime) * 0.0005 * speed)
  735. else
  736. self.object:setyaw(yaw + (1 + dtime) * 0.0005 * speed)
  737. end
  738. elseif ctrl.right then
  739. if self.v < 0 then
  740. self.object:setyaw(yaw + (1 + dtime) * 0.0005 * speed)
  741. else
  742. self.object:setyaw(yaw - (1 + dtime) * 0.0005 * speed)
  743. end
  744. end
  745. end
  746. -- local drift = {x=0,y=0,z=0}
  747. -- if speed < .1 then
  748. -- print("drifting")
  749. -- -- float away randomly
  750. -- drift = {
  751. -- x = math.random(5.1, 10.9),
  752. -- y = 0,
  753. -- z = math.random(5.1, 10.9),
  754. -- }
  755. -- end
  756. --
  757. if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
  758. self.object:setpos(self.object:getpos())
  759. return
  760. end
  761. local s = get_sign(self.v)
  762. -- self.v = self.v - 0.002 * s
  763. if s ~= get_sign(self.v) then
  764. self.object:setvelocity({x = 0, y = 0, z = 0})
  765. self.v = 0
  766. return
  767. end
  768. if math.abs(self.v) > 4 then
  769. self.v = 4 * get_sign(self.v)
  770. end
  771. local p = self.object:getpos()
  772. p.y = p.y - 0.5
  773. local new_velo
  774. local new_acce = {x = 0, y = 0, z = 0}
  775. if not is_water(p) then
  776. local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
  777. if (not nodedef) or nodedef.walkable then
  778. self.v = 0
  779. new_acce = {x = 0, y = 1, z = 0}
  780. else
  781. new_acce = {x = 0, y = -9.8, z = 0}
  782. end
  783. new_velo = get_velocity(self.v, self.object:getyaw(),
  784. self.object:getvelocity().y)
  785. self.object:setpos(self.object:getpos())
  786. else
  787. p.y = p.y + 1
  788. if is_water(p) then
  789. local y = self.object:getvelocity().y
  790. if y >= 5 then
  791. y = 5
  792. elseif y < 0 then
  793. new_acce = {x = 0, y = 3, z = 0} -- float parameters. bigger = bouncier
  794. else
  795. new_acce = {x = 0, y = 1, z = 0}
  796. end
  797. new_velo = get_velocity(self.v, self.object:getyaw(), y)
  798. self.object:setpos(self.object:getpos())
  799. else
  800. new_acce = {x = 0, y = 0, z = 0}
  801. if math.abs(self.object:getvelocity().y) < 1 then
  802. local pos = self.object:getpos()
  803. pos.y = math.floor(pos.y) + 0.5
  804. self.object:setpos(pos)
  805. new_velo = get_velocity(self.v, self.object:getyaw(), 0)
  806. else
  807. new_velo = get_velocity(self.v, self.object:getyaw(),
  808. self.object:getvelocity().y)
  809. self.object:setpos(self.object:getpos())
  810. end
  811. end
  812. end
  813. self.object:setvelocity(new_velo)
  814. self.object:setacceleration(new_acce)
  815. end
  816. minetest.register_entity("boats:boat_steel", boat_steel)
  817. minetest.register_craftitem("boats:boat_steel", {
  818. description = "Steel Boat",
  819. inventory_image = "boats_tin_inventory.png^[colorize:#600:80",
  820. wield_image = "boats_tin_wield.png",
  821. wield_scale = {x = 2, y = 2, z = 1},
  822. liquids_pointable = true,
  823. groups = {flammable = 2},
  824. stack_max = 1,
  825. on_place = function(itemstack, placer, pointed_thing)
  826. local under = pointed_thing.under
  827. local node = minetest.get_node(under)
  828. local udef = minetest.registered_nodes[node.name]
  829. if udef and udef.on_rightclick and
  830. not (placer and placer:is_player() and
  831. placer:get_player_control().sneak) then
  832. return udef.on_rightclick(under, node, placer, itemstack,
  833. pointed_thing) or itemstack
  834. end
  835. if pointed_thing.type ~= "node" then
  836. return itemstack
  837. end
  838. if not is_water(pointed_thing.under) then
  839. return itemstack
  840. end
  841. pointed_thing.under.y = pointed_thing.under.y + 0.5
  842. boat = minetest.add_entity(pointed_thing.under, "boats:boat_steel")
  843. --deploy_boat(boat)
  844. if boat then
  845. if placer then
  846. boat:setyaw(placer:get_look_horizontal())
  847. end
  848. local player_name = placer and placer:get_player_name() or ""
  849. if not (creative and creative.is_enabled_for and
  850. creative.is_enabled_for(player_name)) then
  851. itemstack:take_item()
  852. end
  853. end
  854. return itemstack
  855. end,
  856. })
  857. minetest.register_craft({
  858. output = "boats:boat_steel",
  859. recipe = {
  860. {"", "", "" },
  861. {"default:steel_ingot", "", "default:steel_ingot"},
  862. {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
  863. },
  864. })