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
:get_pos()
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(name
)
89 local player
= minetest
.get_player_by_name(name
)
91 mcl_player
.player_set_animation(player
, "sit" , 30)
94 clicker
:set_look_horizontal(self
.object
:getyaw())
99 function boat
.on_activate(self
, staticdata
, dtime_s
)
100 self
.object
:set_armor_groups({immortal
= 1})
101 local data
= minetest
.deserialize(staticdata
)
102 if type(data
) == "table" then
104 self
._last_v
= self
._v
105 self
._itemstring
= data
.itemstring
106 self
.object
:set_properties({textures
=data
.textures
})
111 function boat
.get_staticdata(self
)
112 return minetest
.serialize({
114 itemstring
= self
._itemstring
,
115 textures
= self
.object
:get_properties().textures
120 function boat
.on_punch(self
, puncher
)
121 if not puncher
or not puncher
:is_player() or self
._removed
then
124 if self
._driver
and puncher
== self
._driver
then
127 puncher
:set_properties({visual_size
= {x
=1, y
=1}})
128 mcl_player
.player_attached
[puncher
:get_player_name()] = false
130 if not self
._driver
then
132 -- Drop boat as item on the ground after punching
133 if not minetest
.settings
:get_bool("creative_mode") then
134 minetest
.add_item(self
.object
:get_pos(), self
._itemstring
)
140 function boat
.on_step(self
, dtime
)
141 self
._v
= get_v(self
.object
:getvelocity()) * get_sign(self
._v
)
143 local ctrl
= self
._driver
:get_player_control()
144 local yaw
= self
.object
:getyaw()
147 self
._v
= self
._v
+ 0.1
149 -- Paddling animation
150 if self
._animation
~= 1 then
151 self
.object
:set_animation({x
=0, y
=40}, paddling_speed
, 0, true)
154 elseif ctrl
.down
then
156 self
._v
= self
._v
- 0.1
158 -- Paddling animation, reversed
159 if self
._animation
~= -1 then
160 self
.object
:set_animation({x
=0, y
=40}, -paddling_speed
, 0, true)
164 -- Stop paddling animation if no control pressed
165 if self
._animation
~= 0 then
166 self
.object
:set_animation({x
=0, y
=40}, 0, 0, true)
172 self
.object
:setyaw(yaw
- (1 + dtime
) * 0.03)
174 self
.object
:setyaw(yaw
+ (1 + dtime
) * 0.03)
176 elseif ctrl
.right
then
178 self
.object
:setyaw(yaw
+ (1 + dtime
) * 0.03)
180 self
.object
:setyaw(yaw
- (1 + dtime
) * 0.03)
184 -- Stop paddling without driver
185 if self
._animation
~= 0 then
186 self
.object
:set_animation({x
=0, y
=40}, 0, 0, true)
190 local velo
= self
.object
:getvelocity()
191 if self
._v
== 0 and velo
.x
== 0 and velo
.y
== 0 and velo
.z
== 0 then
192 self
.object
:setpos(self
.object
:get_pos())
195 local s
= get_sign(self
._v
)
196 self
._v
= self
._v
- 0.02 * s
197 if s
~= get_sign(self
._v
) then
198 self
.object
:setvelocity({x
= 0, y
= 0, z
= 0})
202 if math
.abs(self
._v
) > 5 then
203 self
._v
= 5 * get_sign(self
._v
)
206 local p
= self
.object
:get_pos()
207 p
.y
= p
.y
- boat_y_offset
209 local new_acce
= {x
= 0, y
= 0, z
= 0}
210 if not is_water(p
) then
211 local nodedef
= minetest
.registered_nodes
[minetest
.get_node(p
).name
]
212 if (not nodedef
) or nodedef
.walkable
then
214 new_acce
= {x
= 0, y
= 1, z
= 0}
216 new_acce
= {x
= 0, y
= -9.8, z
= 0}
218 new_velo
= get_velocity(self
._v
, self
.object
:getyaw(),
219 self
.object
:getvelocity().y
)
220 self
.object
:setpos(self
.object
:get_pos())
224 local y
= self
.object
:getvelocity().y
228 new_acce
= {x
= 0, y
= 20, z
= 0}
230 new_acce
= {x
= 0, y
= 5, z
= 0}
232 new_velo
= get_velocity(self
._v
, self
.object
:getyaw(), y
)
233 self
.object
:setpos(self
.object
:get_pos())
235 new_acce
= {x
= 0, y
= 0, z
= 0}
236 if math
.abs(self
.object
:getvelocity().y
) < 1 then
237 local pos
= self
.object
:get_pos()
238 pos
.y
= math
.floor(pos
.y
) + boat_y_offset
239 self
.object
:setpos(pos
)
240 new_velo
= get_velocity(self
._v
, self
.object
:getyaw(), 0)
242 new_velo
= get_velocity(self
._v
, self
.object
:getyaw(),
243 self
.object
:getvelocity().y
)
244 self
.object
:setpos(self
.object
:get_pos())
248 self
.object
:setvelocity(new_velo
)
249 self
.object
:setacceleration(new_acce
)
252 -- Register one entity for all boat types
253 minetest
.register_entity("mcl_boats:boat", boat
)
255 local boat_ids
= { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak" }
256 local names
= { "Oak Boat", "Spruce Boat", "Birch Boat", "Jungle Boat", "Acacia Boat", "Dark Oak Boat" }
257 local craftstuffs
= {}
258 if minetest
.get_modpath("mcl_core") then
259 craftstuffs
= { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood" }
261 local images
= { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak" }
263 for b
=1, #boat_ids
do
264 local itemstring
= "mcl_boats:"..boat_ids
[b
]
266 local longdesc
, usagehelp
, help
, helpname
268 -- Only create one help entry for all boats
271 longdesc
= "Boats are used to travel on the surface of water."
272 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."
276 minetest
.register_craftitem(itemstring
, {
277 description
= names
[b
],
278 _doc_items_create_entry
= help
,
279 _doc_items_entry_name
= helpname
,
280 _doc_items_longdesc
= longdesc
,
281 _doc_items_usagehelp
= usagehelp
,
282 inventory_image
= "mcl_boats_"..images
[b
].."_boat.png",
283 liquids_pointable
= true,
284 groups
= { boat
= 1, transport
= 1},
286 on_place
= function(itemstack
, placer
, pointed_thing
)
287 if pointed_thing
.type ~= "node" then
291 -- Call on_rightclick if the pointed node defines it
292 local node
= minetest
.get_node(pointed_thing
.under
)
293 if placer
and not placer
:get_player_control().sneak
then
294 if minetest
.registered_nodes
[node
.name
] and minetest
.registered_nodes
[node
.name
].on_rightclick
then
295 return minetest
.registered_nodes
[node
.name
].on_rightclick(pointed_thing
.under
, node
, placer
, itemstack
) or itemstack
299 if not is_water(pointed_thing
.under
) then
302 pointed_thing
.under
.y
= pointed_thing
.under
.y
+ boat_y_offset
303 local boat
= minetest
.add_entity(pointed_thing
.under
, "mcl_boats:boat")
304 boat
:get_luaentity()._itemstring
= itemstring
305 boat
:set_properties({textures
= { "mcl_boats_texture_"..images
[b
].."_boat.png" }})
306 boat
:set_yaw(placer
:get_look_horizontal())
307 if not minetest
.settings
:get_bool("creative_mode") then
308 itemstack
:take_item()
312 _on_dispense
= function(stack
, pos
, droppos
, dropnode
, dropdir
)
313 local below
= {x
=droppos
.x
, y
=droppos
.y
-1, z
=droppos
.z
}
314 local belownode
= minetest
.get_node(below
)
315 -- Place boat as entity on or in water
316 if minetest
.get_item_group(dropnode
.name
, "water") ~= 0 or (dropnode
.name
== "air" and minetest
.get_item_group(belownode
.name
, "water") ~= 0) then
317 minetest
.add_entity(droppos
, "mcl_boats:boat")
319 minetest
.add_item(droppos
, stack
)
324 local c
= craftstuffs
[b
]
325 minetest
.register_craft({
334 minetest
.register_craft({
336 recipe
= "group:boat",
340 if minetest
.get_modpath("doc_identifier") ~= nil then
341 doc
.sub
.identifier
.register_object("mcl_boats:boat", "craftitems", "mcl_boats:boat")