Fix potential crash when few loot items are registered
[oweals/minetest_game.git] / mods / carts / functions.lua
1 function carts:get_sign(z)
2         if z == 0 then
3                 return 0
4         else
5                 return z / math.abs(z)
6         end
7 end
8
9 function carts:manage_attachment(player, obj)
10         if not player then
11                 return
12         end
13         local status = obj ~= nil
14         local player_name = player:get_player_name()
15         if player_api.player_attached[player_name] == status then
16                 return
17         end
18         player_api.player_attached[player_name] = status
19
20         if status then
21                 player:set_attach(obj, "", {x=0, y=-4.5, z=0}, {x=0, y=0, z=0})
22                 player:set_eye_offset({x=0, y=-4, z=0},{x=0, y=-4, z=0})
23         else
24                 player:set_detach()
25                 player:set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0})
26         end
27 end
28
29 function carts:velocity_to_dir(v)
30         if math.abs(v.x) > math.abs(v.z) then
31                 return {x=carts:get_sign(v.x), y=carts:get_sign(v.y), z=0}
32         else
33                 return {x=0, y=carts:get_sign(v.y), z=carts:get_sign(v.z)}
34         end
35 end
36
37 function carts:is_rail(pos, railtype)
38         local node = minetest.get_node(pos).name
39         if node == "ignore" then
40                 local vm = minetest.get_voxel_manip()
41                 local emin, emax = vm:read_from_map(pos, pos)
42                 local area = VoxelArea:new{
43                         MinEdge = emin,
44                         MaxEdge = emax,
45                 }
46                 local data = vm:get_data()
47                 local vi = area:indexp(pos)
48                 node = minetest.get_name_from_content_id(data[vi])
49         end
50         if minetest.get_item_group(node, "rail") == 0 then
51                 return false
52         end
53         if not railtype then
54                 return true
55         end
56         return minetest.get_item_group(node, "connect_to_raillike") == railtype
57 end
58
59 function carts:check_front_up_down(pos, dir_, check_up, railtype)
60         local dir = vector.new(dir_)
61         local cur
62
63         -- Front
64         dir.y = 0
65         cur = vector.add(pos, dir)
66         if carts:is_rail(cur, railtype) then
67                 return dir
68         end
69         -- Up
70         if check_up then
71                 dir.y = 1
72                 cur = vector.add(pos, dir)
73                 if carts:is_rail(cur, railtype) then
74                         return dir
75                 end
76         end
77         -- Down
78         dir.y = -1
79         cur = vector.add(pos, dir)
80         if carts:is_rail(cur, railtype) then
81                 return dir
82         end
83         return nil
84 end
85
86 function carts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
87         local pos = vector.round(pos_)
88         local cur
89         local left_check, right_check = true, true
90
91         -- Check left and right
92         local left = {x=0, y=0, z=0}
93         local right = {x=0, y=0, z=0}
94         if dir.z ~= 0 and dir.x == 0 then
95                 left.x = -dir.z
96                 right.x = dir.z
97         elseif dir.x ~= 0 and dir.z == 0 then
98                 left.z = dir.x
99                 right.z = -dir.x
100         end
101
102         local straight_priority = ctrl and dir.y ~= 0
103
104         -- Normal, to disallow rail switching up- & downhill
105         if straight_priority then
106                 cur = self:check_front_up_down(pos, dir, true, railtype)
107                 if cur then
108                         return cur
109                 end
110         end
111
112         if ctrl then
113                 if old_switch == 1 then
114                         left_check = false
115                 elseif old_switch == 2 then
116                         right_check = false
117                 end
118                 if ctrl.left and left_check then
119                         cur = self:check_front_up_down(pos, left, false, railtype)
120                         if cur then
121                                 return cur, 1
122                         end
123                         left_check = false
124                 end
125                 if ctrl.right and right_check then
126                         cur = self:check_front_up_down(pos, right, false, railtype)
127                         if cur then
128                                 return cur, 2
129                         end
130                         right_check = true
131                 end
132         end
133
134         -- Normal
135         if not straight_priority then
136                 cur = self:check_front_up_down(pos, dir, true, railtype)
137                 if cur then
138                         return cur
139                 end
140         end
141
142         -- Left, if not already checked
143         if left_check then
144                 cur = carts:check_front_up_down(pos, left, false, railtype)
145                 if cur then
146                         return cur
147                 end
148         end
149
150         -- Right, if not already checked
151         if right_check then
152                 cur = carts:check_front_up_down(pos, right, false, railtype)
153                 if cur then
154                         return cur
155                 end
156         end
157
158         -- Backwards
159         if not old_switch then
160                 cur = carts:check_front_up_down(pos, {
161                                 x = -dir.x,
162                                 y = dir.y,
163                                 z = -dir.z
164                         }, true, railtype)
165                 if cur then
166                         return cur
167                 end
168         end
169
170         return {x=0, y=0, z=0}
171 end
172
173 function carts:pathfinder(pos_, old_pos, old_dir, distance, ctrl,
174                 pf_switch, railtype)
175
176         local pos = vector.round(pos_)
177         if vector.equals(old_pos, pos) then
178                 return
179         end
180
181         local pf_pos = vector.round(old_pos)
182         local pf_dir = vector.new(old_dir)
183         distance = math.min(carts.path_distance_max,
184                 math.floor(distance + 1))
185
186         for i = 1, distance do
187                 pf_dir, pf_switch = self:get_rail_direction(
188                         pf_pos, pf_dir, ctrl, pf_switch or 0, railtype)
189
190                 if vector.equals(pf_dir, {x=0, y=0, z=0}) then
191                         -- No way forwards
192                         return pf_pos, pf_dir
193                 end
194
195                 pf_pos = vector.add(pf_pos, pf_dir)
196
197                 if vector.equals(pf_pos, pos) then
198                         -- Success! Cart moved on correctly
199                         return
200                 end
201         end
202         -- Not found. Put cart to predicted position
203         return pf_pos, pf_dir
204 end
205
206 function carts:register_rail(name, def_overwrite, railparams)
207         local def = {
208                 drawtype = "raillike",
209                 paramtype = "light",
210                 sunlight_propagates = true,
211                 is_ground_content = false,
212                 walkable = false,
213                 selection_box = {
214                         type = "fixed",
215                         fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
216                 },
217                 sounds = default.node_sound_metal_defaults()
218         }
219         for k, v in pairs(def_overwrite) do
220                 def[k] = v
221         end
222         if not def.inventory_image then
223                 def.wield_image = def.tiles[1]
224                 def.inventory_image = def.tiles[1]
225         end
226
227         if railparams then
228                 carts.railparams[name] = table.copy(railparams)
229         end
230
231         minetest.register_node(name, def)
232 end
233
234 function carts:get_rail_groups(additional_groups)
235         -- Get the default rail groups and add more when a table is given
236         local groups = {
237                 dig_immediate = 2,
238                 attached_node = 1,
239                 rail = 1,
240                 connect_to_raillike = minetest.raillike_group("rail")
241         }
242         if type(additional_groups) == "table" then
243                 for k, v in pairs(additional_groups) do
244                         groups[k] = v
245                 end
246         end
247         return groups
248 end