Replace getpos() with get_pos()
[MineClone/MineClone2.git] / mods / ENTITIES / mcl_boats / init.lua
blobc347efb0a70c80ce6ca8bd1015796af0b4fe78ff
1 --
2 -- Helper functions
3 --
5 local function is_water(pos)
6 local nn = minetest.get_node(pos).name
7 return minetest.get_item_group(nn, "water") ~= 0
8 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
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}
24 end
27 local function get_v(v)
28 return math.sqrt(v.x ^ 2 + v.z ^ 2)
29 end
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
38 -- Boat entity
41 local boat = {
42 physical = true,
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},
46 visual = "mesh",
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
52 _v = 0, -- Speed
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
61 return
62 end
63 local name = clicker:get_player_name()
64 if self._driver and clicker == self._driver then
65 self._driver = nil
66 clicker:set_detach()
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}
72 clicker:setpos(pos)
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
79 end
80 clicker:set_detach()
81 clicker:set_properties({visual_size = {x=1, y=1}})
82 end
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)
90 if player then
91 mcl_player.player_set_animation(player, "sit" , 30)
92 end
93 end, name)
94 clicker:set_look_horizontal(self.object:getyaw())
95 end
96 end
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
103 self._v = data.v
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({
113 v = self._v,
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
122 return
124 if self._driver and puncher == self._driver then
125 self._driver = nil
126 puncher:set_detach()
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
131 self._removed = true
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)
136 self.object:remove()
140 function boat.on_step(self, dtime)
141 self._v = get_v(self.object:getvelocity()) * get_sign(self._v)
142 if self._driver then
143 local ctrl = self._driver:get_player_control()
144 local yaw = self.object:getyaw()
145 if ctrl.up then
146 -- Forwards
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)
152 self._animation = 1
154 elseif ctrl.down then
155 -- Backwards
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)
161 self._animation = -1
163 else
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)
167 self._animation = 0
170 if ctrl.left then
171 if self._v < 0 then
172 self.object:setyaw(yaw - (1 + dtime) * 0.03)
173 else
174 self.object:setyaw(yaw + (1 + dtime) * 0.03)
176 elseif ctrl.right then
177 if self._v < 0 then
178 self.object:setyaw(yaw + (1 + dtime) * 0.03)
179 else
180 self.object:setyaw(yaw - (1 + dtime) * 0.03)
183 else
184 -- Stop paddling without driver
185 if self._animation ~= 0 then
186 self.object:set_animation({x=0, y=40}, 0, 0, true)
187 self._animation = 0
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())
193 return
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})
199 self._v = 0
200 return
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
208 local new_velo
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
213 self._v = 0
214 new_acce = {x = 0, y = 1, z = 0}
215 else
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())
221 else
222 p.y = p.y + 1
223 if is_water(p) then
224 local y = self.object:getvelocity().y
225 if y >= 5 then
226 y = 5
227 elseif y < 0 then
228 new_acce = {x = 0, y = 20, z = 0}
229 else
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())
234 else
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)
241 else
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
267 help = false
268 -- Only create one help entry for all boats
269 if b == 1 then
270 help = true
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."
273 helpname = "Boat"
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},
285 stack_max = 1,
286 on_place = function(itemstack, placer, pointed_thing)
287 if pointed_thing.type ~= "node" then
288 return
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
300 return
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()
310 return itemstack
311 end,
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")
318 else
319 minetest.add_item(droppos, stack)
321 end,
324 local c = craftstuffs[b]
325 minetest.register_craft({
326 output = itemstring,
327 recipe = {
328 {c, "", c},
329 {c, c, c},
334 minetest.register_craft({
335 type = "fuel",
336 recipe = "group:boat",
337 burntime = 20,
340 if minetest.get_modpath("doc_identifier") ~= nil then
341 doc.sub.identifier.register_object("mcl_boats:boat", "craftitems", "mcl_boats:boat")