5 local function is_water(pos
)
6 local nn
= minetest
.get_node(pos
).name
7 return minetest
.get_item_group(nn
, "water") ~= 0
11 local function get_sign(i
)
15 return i
/ math
.abs(i
)
20 local function get_velocity(v
, yaw
, y
)
21 local x
= -math
.sin(yaw
) * v
22 local z
= math
.cos(yaw
) * v
23 return {x
= x
, y
= y
, z
= z
}
27 local function get_v(v
)
28 return math
.sqrt(v
.x ^
2 + v
.z ^
2)
31 local boat_visual_size
= {x
= 3, y
= 3}
32 -- Note: This mod assumes the default player visual_size is {x=1, y=1}
33 local driver_visual_size
= { x
= 1/boat_visual_size
.x
, y
= 1/boat_visual_size
.y
}
34 local paddling_speed
= 22
35 local boat_y_offset
= 0.35
43 -- Warning: Do not change the position of the collisionbox top surface,
44 -- lowering it causes the boat to fall through the world if underwater
45 collisionbox
= {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
47 mesh
= "mcl_boats_boat.b3d",
48 textures
= {"mcl_boats_texture_oak_boat.png"},
49 visual_size
= boat_visual_size
,
51 _driver
= nil, -- Attached driver (player) or nil if none
53 _last_v
= 0, -- Temporary speed variable
54 _removed
= false, -- If true, boat entity is considered removed (e.g. after punch) and should be ignored
55 _itemstring
= "mcl_boats:boat", -- Itemstring of the boat item (implies boat type)
56 _animation
= 0, -- 0: not animated; 1: paddling forwards; -1: paddling forwards
59 function boat
.on_rightclick(self
, clicker
)
60 if not clicker
or not clicker
:is_player() then
63 local name
= clicker
:get_player_name()
64 if self
._driver
and clicker
== self
._driver
then
67 clicker
:set_properties({visual_size
= {x
=1, y
=1}})
68 mcl_player
.player_attached
[name
] = false
69 mcl_player
.player_set_animation(clicker
, "stand" , 30)
70 local pos
= clicker
:getpos()
71 pos
= {x
= pos
.x
, y
= pos
.y
+ 0.2, z
= pos
.z
}
73 elseif not self
._driver
then
74 local attach
= clicker
:get_attach()
75 if attach
and attach
:get_luaentity() then
76 local luaentity
= attach
:get_luaentity()
77 if luaentity
._driver
then
78 luaentity
._driver
= nil
81 clicker
:set_properties({visual_size
= {x
=1, y
=1}})
83 self
._driver
= clicker
84 clicker
:set_attach(self
.object
, "",
85 {x
= 0, y
= 3.75, z
= -1}, {x
= 0, y
= 0, z
= 0})
86 clicker
:set_properties({ visual_size
= driver_visual_size
})
87 mcl_player
.player_attached
[name
] = true
88 minetest
.after(0.2, function(clicker
)
89 if clicker
:is_player() then
90 mcl_player
.player_set_animation(clicker
, "sit" , 30)
93 clicker
:set_look_horizontal(self
.object
:getyaw())
98 function boat
.on_activate(self
, staticdata
, dtime_s
)
99 self
.object
:set_armor_groups({immortal
= 1})
100 local data
= minetest
.deserialize(staticdata
)
101 if type(data
) == "table" then
103 self
._last_v
= self
._v
104 self
._itemstring
= data
.itemstring
105 self
.object
:set_properties({textures
=data
.textures
})
110 function boat
.get_staticdata(self
)
111 return minetest
.serialize({
113 itemstring
= self
._itemstring
,
114 textures
= self
.object
:get_properties().textures
119 function boat
.on_punch(self
, puncher
)
120 if not puncher
or not puncher
:is_player() or self
._removed
then
123 if self
._driver
and puncher
== self
._driver
then
126 puncher
:set_properties({visual_size
= {x
=1, y
=1}})
127 mcl_player
.player_attached
[puncher
:get_player_name()] = false
129 if not self
._driver
then
131 -- Drop boat as item on the ground after punching
132 if not minetest
.settings
:get_bool("creative_mode") then
133 minetest
.add_item(self
.object
:getpos(), self
._itemstring
)
139 function boat
.on_step(self
, dtime
)
140 self
._v
= get_v(self
.object
:getvelocity()) * get_sign(self
._v
)
142 local ctrl
= self
._driver
:get_player_control()
143 local yaw
= self
.object
:getyaw()
146 self
._v
= self
._v
+ 0.1
148 -- Paddling animation
149 if self
._animation
~= 1 then
150 self
.object
:set_animation({x
=0, y
=40}, paddling_speed
, 0, true)
153 elseif ctrl
.down
then
155 self
._v
= self
._v
- 0.1
157 -- Paddling animation, reversed
158 if self
._animation
~= -1 then
159 self
.object
:set_animation({x
=0, y
=40}, -paddling_speed
, 0, true)
163 -- Stop paddling animation if no control pressed
164 if self
._animation
~= 0 then
165 self
.object
:set_animation({x
=0, y
=40}, 0, 0, true)
171 self
.object
:setyaw(yaw
- (1 + dtime
) * 0.03)
173 self
.object
:setyaw(yaw
+ (1 + dtime
) * 0.03)
175 elseif ctrl
.right
then
177 self
.object
:setyaw(yaw
+ (1 + dtime
) * 0.03)
179 self
.object
:setyaw(yaw
- (1 + dtime
) * 0.03)
183 -- Stop paddling without driver
184 if self
._animation
~= 0 then
185 self
.object
:set_animation({x
=0, y
=40}, 0, 0, true)
189 local velo
= self
.object
:getvelocity()
190 if self
._v
== 0 and velo
.x
== 0 and velo
.y
== 0 and velo
.z
== 0 then
191 self
.object
:setpos(self
.object
:getpos())
194 local s
= get_sign(self
._v
)
195 self
._v
= self
._v
- 0.02 * s
196 if s
~= get_sign(self
._v
) then
197 self
.object
:setvelocity({x
= 0, y
= 0, z
= 0})
201 if math
.abs(self
._v
) > 5 then
202 self
._v
= 5 * get_sign(self
._v
)
205 local p
= self
.object
:getpos()
206 p
.y
= p
.y
- boat_y_offset
208 local new_acce
= {x
= 0, y
= 0, z
= 0}
209 if not is_water(p
) then
210 local nodedef
= minetest
.registered_nodes
[minetest
.get_node(p
).name
]
211 if (not nodedef
) or nodedef
.walkable
then
213 new_acce
= {x
= 0, y
= 1, z
= 0}
215 new_acce
= {x
= 0, y
= -9.8, z
= 0}
217 new_velo
= get_velocity(self
._v
, self
.object
:getyaw(),
218 self
.object
:getvelocity().y
)
219 self
.object
:setpos(self
.object
:getpos())
223 local y
= self
.object
:getvelocity().y
227 new_acce
= {x
= 0, y
= 20, z
= 0}
229 new_acce
= {x
= 0, y
= 5, z
= 0}
231 new_velo
= get_velocity(self
._v
, self
.object
:getyaw(), y
)
232 self
.object
:setpos(self
.object
:getpos())
234 new_acce
= {x
= 0, y
= 0, z
= 0}
235 if math
.abs(self
.object
:getvelocity().y
) < 1 then
236 local pos
= self
.object
:getpos()
237 pos
.y
= math
.floor(pos
.y
) + boat_y_offset
238 self
.object
:setpos(pos
)
239 new_velo
= get_velocity(self
._v
, self
.object
:getyaw(), 0)
241 new_velo
= get_velocity(self
._v
, self
.object
:getyaw(),
242 self
.object
:getvelocity().y
)
243 self
.object
:setpos(self
.object
:getpos())
247 self
.object
:setvelocity(new_velo
)
248 self
.object
:setacceleration(new_acce
)
251 -- Register one entity for all boat types
252 minetest
.register_entity("mcl_boats:boat", boat
)
254 local boat_ids
= { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak" }
255 local names
= { "Oak Boat", "Spruce Boat", "Birch Boat", "Jungle Boat", "Acacia Boat", "Dark Oak Boat" }
256 local craftstuffs
= {}
257 if minetest
.get_modpath("mcl_core") then
258 craftstuffs
= { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood" }
260 local images
= { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak" }
262 for b
=1, #boat_ids
do
263 local itemstring
= "mcl_boats:"..boat_ids
[b
]
265 local longdesc
, usagehelp
, help
, helpname
267 -- Only create one help entry for all boats
270 longdesc
= "Boats are used to travel on the surface of water."
271 usagehelp
= "Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item."
275 minetest
.register_craftitem(itemstring
, {
276 description
= names
[b
],
277 _doc_items_create_entry
= help
,
278 _doc_items_entry_name
= helpname
,
279 _doc_items_longdesc
= longdesc
,
280 _doc_items_usagehelp
= usagehelp
,
281 inventory_image
= "mcl_boats_"..images
[b
].."_boat.png",
282 liquids_pointable
= true,
283 groups
= { boat
= 1, transport
= 1},
285 on_place
= function(itemstack
, placer
, pointed_thing
)
286 if pointed_thing
.type ~= "node" then
290 -- Call on_rightclick if the pointed node defines it
291 local node
= minetest
.get_node(pointed_thing
.under
)
292 if placer
and not placer
:get_player_control().sneak
then
293 if minetest
.registered_nodes
[node
.name
] and minetest
.registered_nodes
[node
.name
].on_rightclick
then
294 return minetest
.registered_nodes
[node
.name
].on_rightclick(pointed_thing
.under
, node
, placer
, itemstack
) or itemstack
298 if not is_water(pointed_thing
.under
) then
301 pointed_thing
.under
.y
= pointed_thing
.under
.y
+ boat_y_offset
302 local boat
= minetest
.add_entity(pointed_thing
.under
, "mcl_boats:boat")
303 boat
:get_luaentity()._itemstring
= itemstring
304 boat
:set_properties({textures
= { "mcl_boats_texture_"..images
[b
].."_boat.png" }})
305 boat
:set_yaw(placer
:get_look_horizontal())
306 if not minetest
.settings
:get_bool("creative_mode") then
307 itemstack
:take_item()
311 _on_dispense
= function(stack
, pos
, droppos
, dropnode
, dropdir
)
312 local below
= {x
=droppos
.x
, y
=droppos
.y
-1, z
=droppos
.z
}
313 local belownode
= minetest
.get_node(below
)
314 -- Place boat as entity on or in water
315 if minetest
.get_item_group(dropnode
.name
, "water") ~= 0 or (dropnode
.name
== "air" and minetest
.get_item_group(belownode
.name
, "water") ~= 0) then
316 minetest
.add_entity(droppos
, "mcl_boats:boat")
318 minetest
.add_item(droppos
, stack
)
323 local c
= craftstuffs
[b
]
324 minetest
.register_craft({
333 minetest
.register_craft({
335 recipe
= "group:boat",
339 if minetest
.get_modpath("doc_identifier") ~= nil then
340 doc
.sub
.identifier
.register_object("mcl_boats:boat", "craftitems", "mcl_boats:boat")