8d61dc57a039b13e0c2864a965b26fffbf1cdef6
[oweals/minetest_game.git] / mods / boats / init.lua
1
2 --
3 -- Helper functions
4 --
5
6 local function is_water(pos)
7         local nn = minetest.get_node(pos).name
8         return minetest.get_item_group(nn, "water") ~= 0
9 end
10
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
18
19 local function get_velocity(v, yaw, y)
20         local x = -math.sin(yaw) * v
21         local z =  math.cos(yaw) * v
22         return {x = x, y = y, z = z}
23 end
24
25 local function get_v(v)
26         return math.sqrt(v.x ^ 2 + v.z ^ 2)
27 end
28
29 --
30 -- Boat entity
31 --
32
33 local boat = {
34         physical = true,
35         collisionbox = {-0.5, -0.4, -0.5, 0.5, 0.3, 0.5},
36         visual = "mesh",
37         mesh = "boat.obj",
38         textures = {"default_wood.png"},
39
40         driver = nil,
41         v = 0,
42         last_v = 0,
43         removed = false
44 }
45
46 function boat.on_rightclick(self, clicker)
47         if not clicker or not clicker:is_player() then
48                 return
49         end
50         local name = clicker:get_player_name()
51         if self.driver and clicker == self.driver then
52                 self.driver = nil
53                 clicker:set_detach()
54                 default.player_attached[name] = false
55                 default.player_set_animation(clicker, "stand" , 30)
56         elseif not self.driver then
57                 self.driver = clicker
58                 clicker:set_attach(self.object, "", {x = 0, y = 11, z = -3}, {x = 0, y = 0, z = 0})
59                 default.player_attached[name] = true
60                 minetest.after(0.2, function()
61                         default.player_set_animation(clicker, "sit" , 30)
62                 end)
63                 self.object:setyaw(clicker:get_look_yaw() - math.pi / 2)
64         end
65 end
66
67 function boat.on_activate(self, staticdata, dtime_s)
68         self.object:set_armor_groups({immortal = 1})
69         if staticdata then
70                 self.v = tonumber(staticdata)
71         end
72         self.last_v = self.v
73 end
74
75 function boat.get_staticdata(self)
76         return tostring(self.v)
77 end
78
79 function boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, direction)
80         if not puncher or not puncher:is_player() or self.removed then
81                 return
82         end
83         if self.driver and puncher == self.driver then
84                 self.driver = nil
85                 puncher:set_detach()
86                 default.player_attached[puncher:get_player_name()] = false
87         end
88         if not self.driver then
89                 self.removed = true
90                 -- delay remove to ensure player is detached
91                 minetest.after(0.1, function()
92                         self.object:remove()
93                 end)
94                 if not minetest.setting_getbool("creative_mode") then
95                         puncher:get_inventory():add_item("main", "boats:boat")
96                 end
97         end
98 end
99
100 function boat.on_step(self, dtime)
101         self.v = get_v(self.object:getvelocity()) * get_sign(self.v)
102         if self.driver then
103                 local ctrl = self.driver:get_player_control()
104                 local yaw = self.object:getyaw()
105                 if ctrl.up then
106                         self.v = self.v + 0.1
107                 elseif ctrl.down then
108                         self.v = self.v - 0.1
109                 end
110                 if ctrl.left then
111                         if self.v < 0 then
112                                 self.object:setyaw(yaw - (1 + dtime) * 0.03)
113                         else
114                                 self.object:setyaw(yaw + (1 + dtime) * 0.03)
115                         end
116                 elseif ctrl.right then
117                         if self.v < 0 then
118                                 self.object:setyaw(yaw + (1 + dtime) * 0.03)
119                         else
120                                 self.object:setyaw(yaw - (1 + dtime) * 0.03)
121                         end
122                 end
123         end
124         local velo = self.object:getvelocity()
125         if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
126                 self.object:setpos(self.object:getpos())
127                 return
128         end
129         local s = get_sign(self.v)
130         self.v = self.v - 0.02 * s
131         if s ~= get_sign(self.v) then
132                 self.object:setvelocity({x = 0, y = 0, z = 0})
133                 self.v = 0
134                 return
135         end
136         if math.abs(self.v) > 4.5 then
137                 self.v = 4.5 * get_sign(self.v)
138         end
139
140         local p = self.object:getpos()
141         p.y = p.y - 0.5
142         local new_velo = {x = 0, y = 0, z = 0}
143         local new_acce = {x = 0, y = 0, z = 0}
144         if not is_water(p) then
145                 local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
146                 if (not nodedef) or nodedef.walkable then
147                         self.v = 0
148                         new_acce = {x = 0, y = 1, z = 0}
149                 else
150                         new_acce = {x = 0, y = -9.8, z = 0}
151                 end
152                 new_velo = get_velocity(self.v, self.object:getyaw(), self.object:getvelocity().y)
153                 self.object:setpos(self.object:getpos())
154         else
155                 p.y = p.y + 1
156                 if is_water(p) then
157                         local y = self.object:getvelocity().y
158                         if y >= 4.5 then
159                                 y = 4.5
160                         elseif y < 0 then
161                                 new_acce = {x = 0, y = 20, z = 0}
162                         else
163                                 new_acce = {x = 0, y = 5, z = 0}
164                         end
165                         new_velo = get_velocity(self.v, self.object:getyaw(), y)
166                         self.object:setpos(self.object:getpos())
167                 else
168                         new_acce = {x = 0, y = 0, z = 0}
169                         if math.abs(self.object:getvelocity().y) < 1 then
170                                 local pos = self.object:getpos()
171                                 pos.y = math.floor(pos.y) + 0.5
172                                 self.object:setpos(pos)
173                                 new_velo = get_velocity(self.v, self.object:getyaw(), 0)
174                         else
175                                 new_velo = get_velocity(self.v, self.object:getyaw(), self.object:getvelocity().y)
176                                 self.object:setpos(self.object:getpos())
177                         end
178                 end
179         end
180         self.object:setvelocity(new_velo)
181         self.object:setacceleration(new_acce)
182 end
183
184 minetest.register_entity("boats:boat", boat)
185
186 minetest.register_craftitem("boats:boat", {
187         description = "Boat",
188         inventory_image = "boat_inventory.png",
189         wield_image = "boat_wield.png",
190         wield_scale = {x = 2, y = 2, z = 1},
191         liquids_pointable = true,
192
193         on_place = function(itemstack, placer, pointed_thing)
194                 if pointed_thing.type ~= "node" then
195                         return
196                 end
197                 if not is_water(pointed_thing.under) then
198                         return
199                 end
200                 pointed_thing.under.y = pointed_thing.under.y + 0.5
201                 minetest.add_entity(pointed_thing.under, "boats:boat")
202                 if not minetest.setting_getbool("creative_mode") then
203                         itemstack:take_item()
204                 end
205                 return itemstack
206         end,
207 })
208
209 minetest.register_craft({
210         output = "boats:boat",
211         recipe = {
212                 {"",           "",           ""          },
213                 {"group:wood", "",           "group:wood"},
214                 {"group:wood", "group:wood", "group:wood"},
215         },
216 })
217