]> git.lizzy.rs Git - Crafter.git/blob - mods/minecart/init.lua
2a1828e576ebb8d0edf8945b59986fb0897f3df7
[Crafter.git] / mods / minecart / init.lua
1 local path = minetest.get_modpath("minecart")
2 dofile(path.."/rail.lua")
3
4
5 --get if rail
6 local function rail(pos)
7       return(minetest.get_node_group(minetest.get_node(pos).name,"rail")>0)
8 end
9
10 --check if on rail
11 local function on_rail(self,pos)
12       if not rail(pos) and not self.slope then
13             self.axis = nil
14             return(false)
15       else
16             return(true)
17       end
18 end
19
20 --set physical state
21 local function physical(self,pos)
22       if on_rail(self,pos) then
23             self.object:set_properties({physical = false})
24             self.object:setacceleration(vector.new(0,0,0))
25       elseif not self.slope then
26             self.object:set_properties({physical = true})
27             self.object:setacceleration(vector.new(0,-9.81,0))
28       end
29 end
30
31 --get if node in minecarts direction
32 local function node_ahead(self,pos)
33       local vel = self.object:getvelocity()
34       local dir = vector.normalize(vel)
35       return(rail(vector.add(pos,dir)))
36 end
37
38 --get current axis (prefers x)
39 local function axis(pos)
40       if rail(pos) then
41             if rail(vector.new(pos.x-1,pos.y,pos.z)) or rail(vector.new(pos.x+1,pos.y,pos.z)) then return("x") end
42             if rail(vector.new(pos.x,pos.y,pos.z-1)) or rail(vector.new(pos.x,pos.y,pos.z+1)) then return("z") end
43       end
44 end
45
46 --snap object to rail
47 local function snap_rail(self,pos)
48       local slopy = self.slope
49       if not slopy then print("the slope is nil") else
50             print("the slope is ".. slopy)
51       end
52       local railpos = vector.floor(vector.add(pos, 0.5))
53       local vel = self.object:getvelocity()
54       if self.axis == "x" and pos.x ~= railpos.x then
55             self.object:moveto(vector.new(pos.x,railpos.y,railpos.z))
56             self.object:setvelocity(vector.new(vel.x,0,0))
57             print("snapped to x")
58             return
59       end
60       if self.axis == "z" and pos.z ~= railpos.z then
61             self.object:moveto(vector.new(railpos.x,railpos.y,pos.z))
62             self.object:setvelocity(vector.new(0,0,vel.z))
63             print("snapped to z")
64             return
65       end
66 end
67
68 --check if entering new position
69 local function newnode(self,pos)
70       local pos = vector.floor(vector.add(pos,0.5))
71       
72       pos.y = 0
73       
74       local equals = false
75       
76       
77       if self.oldpos then
78             equals = vector.equals(pos,self.oldpos)
79       end
80       
81       self.oldpos = pos
82       return(not equals)
83 end
84
85 --check if past center - used for turning
86 local function pastcenter(self,pos)
87       
88       local center = vector.floor(vector.add(pos,0.5))
89       center.y = 0
90       local pos2d = vector.new(pos.x,0,pos.z)
91       
92       local vel = self.object:getvelocity()
93       local dir = vector.normalize(vel)
94       dir.y = 0
95       local checker = vector.round(vector.normalize(vector.subtract(pos2d,center)))
96       checker.y = 0
97       local past = vector.equals(checker, dir)
98       return(past)
99 end
100
101 --check if node ahead
102 local function node_forward(self,pos)
103       local vel = self.object:getvelocity()
104       local dir = vector.normalize(vel)
105       return(rail(vector.add(pos,dir)))
106 end
107
108 --check if node above or below
109 local function check_hill(self,pos)
110       local vel = self.object:getvelocity()
111       local dirup = vector.normalize(vel)
112       
113       dirup.y = dirup.y + 1
114       
115       print(dump(dirup))
116       
117       minetest.add_particlespawner({
118             amount = 5,
119             time = 0,
120             minpos = vector.add(pos,dirup),
121             maxpos = vector.add(pos,dirup),
122             minvel = vector.new(0,0,0),
123             maxvel = vector.new(0,0,0),
124             minacc = {x=0, y=0, z=0},
125             maxacc = {x=0, y=0, z=0},
126             minexptime = 5,
127             maxexptime = 5,
128             minsize = 1,
129             maxsize = 1,
130             attached = player,
131             collisiondetection = true,
132             vertical = false,
133             texture = "treecapitator.png"
134       })
135       
136       local dirdown = vector.new(0,-0.5,0)
137       
138       if rail(vector.add(pos,dirup)) then
139             self.slope = "up"
140             return("up")
141       elseif rail(vector.add(pos,dirdown)) then
142             self.slope = "down"
143             return("down")
144       else
145             self.slope = nil
146             return(nil)
147       end
148 end
149
150 local function gravity(self,pos)
151       
152       if self.slope == up then
153             local vel = vector.multiply(self.object:getvelocity(), 0.95)
154             self.object:set_velocity(vel)
155       end
156       if self.slope == up then
157             local vel = vector.multiply(self.object:getvelocity(), 1.05)
158             self.object:set_velocity(vel)
159       end
160
161 end
162
163 --make the minecart go up and down hills
164 local function navigate_hill(self)
165       if self.slope then
166             local vel = self.object:getvelocity()
167             if self.slope == "up" then
168             
169                   local yvel = 0
170                   if self.axis == "x" then
171                         yvel = math.abs(vel.x)*1.1
172                   end
173                   if self.axis == "z" then
174                         yvel = math.abs(vel.z)*1.1
175                   end
176                   self.object:setvelocity(vector.new(vel.x,yvel,vel.z))
177             elseif self.slope == "down" then
178             
179                   local yvel = 0
180                   if self.axis == "x" then
181                         yvel = math.abs(vel.x)*-1
182                   end
183                   if self.axis == "z" then
184                         yvel = math.abs(vel.z)*-1
185                   end
186                   
187                   self.object:setvelocity(vector.new(vel.x,yvel,vel.z))
188             end
189       end
190 end
191
192 --swap axis and speed 90 degrees
193 local function turn_check(self,pos)
194       local axis = self.axis
195       local vel = self.object:getvelocity()
196       vel.x = math.abs(vel.x)
197       vel.y = math.abs(vel.y)
198       vel.z = math.abs(vel.z)
199       
200       if axis == "x" then
201             if rail(vector.new(pos.x,pos.y,pos.z-1)) then
202                   print("-x")
203                   self.object:setvelocity(vector.new(0,0,vel.x*-1))
204                   self.axis = "z"
205                   snap_rail(self,pos)
206                   self.turn_timer = 0
207                   return
208             elseif rail(vector.new(pos.x,pos.y,pos.z+1)) then 
209                   print("+x")
210                   self.object:setvelocity(vector.new(0,0,vel.x))
211                   self.axis = "z"
212                   snap_rail(self,pos)
213                   self.turn_timer = 0
214                   return
215             end
216       end
217       if axis == "z" then
218             if rail(vector.new(pos.x-1,pos.y,pos.z)) then
219                   print("-z")
220                   self.object:setvelocity(vector.new(vel.z*-1,0,0))
221                   self.axis = "x"
222                   snap_rail(self,pos)
223                   self.turn_timer = 0
224                   return
225             elseif rail(vector.new(pos.x+1,pos.y,pos.z)) then 
226                   print("+z")
227                   self.object:setvelocity(vector.new(vel.z,0,0))
228                   self.axis = "x"
229                   snap_rail(self,pos)
230                   self.turn_timer = 0
231                   return
232             end
233       end
234 end
235 --try to turn
236 local function turn(self,pos)
237       if pastcenter(self,pos) then
238             if not node_forward(self,pos) and self.axis then
239                   turn_check(self,pos)
240             end
241       end
242 end
243
244 --the main mechanics of the minecart
245 local function minecart_brain(self,dtime)
246       if self.turn_timer < 5 then
247             self.turn_timer = self.turn_timer + dtime
248       end
249       local pos = self.object:getpos()
250       pos.y = pos.y - 0.5
251
252       
253       if not self.axis then
254             self.axis = axis(pos)
255       end
256       
257       if newnode(self,pos) then
258             snap_rail(self,pos)
259       end
260       --check_hill(self,pos)
261       --navigate_hill(self)
262
263       
264       turn(self,pos)
265       
266       on_rail(self,pos)
267       physical(self,pos)
268       --print(self.axis)
269       
270       --check if falling and then fall at the same speed to go down
271 end
272       
273       
274       
275       
276       
277       
278       
279       
280       
281       
282       
283       
284       
285       
286       
287       
288       
289       
290       
291       
292       
293       
294       
295       
296       
297       
298       
299       
300       
301       
302       
303       
304       
305       
306       
307       
308       
309       
310       
311
312 minetest.register_entity("minecart:minecart", {
313       initial_properties = {
314             physical = true, -- otherwise going uphill breaks
315             collisionbox = {-0.4, -0.5, -0.4, 0.4, 0.45, 0.4},--{-0.5, -0.4, -0.5, 0.5, 0.25, 0.5},
316             visual = "mesh",
317             mesh = "minecart.obj",
318             visual_size = {x=1, y=1},
319             textures = {"minecart.png"},
320             automatic_face_movement_dir = 90.0,
321             automatic_face_movement_max_rotation_per_sec = 600,
322       },
323
324       rider = nil,
325       punched = false,
326       speed = 0,
327       turn_timer = 0,
328       incline = nil,
329       turn_timer = 5,
330
331       on_rightclick = function(self,clicker)
332             if not clicker or not clicker:is_player() then
333                   return
334             end
335             local player_name = clicker:get_player_name()
336             
337             if self.rider and player_name == self.rider then
338                   self.rider = nil
339                   --carts:manage_attachment(clicker, nil)
340             elseif not self.rider then
341                   self.rider = player_name
342                   clicker:set_attach(self.object, "", {x=0, y=-4.5, z=0}, {x=0, y=0, z=0})
343                   --player:set_eye_offset({x=0, y=-4, z=0},{x=0, y=-4, z=0})
344                   --carts:manage_attachment(clicker, self.object)
345
346                   -- player_api does not update the animation
347                   -- when the player is attached, reset to default animation
348                   
349                   --player_api.set_animation(clicker, "stand")
350             end
351       end,
352       
353       on_activate = function(self,staticdata, dtime_s)
354             self.object:set_armor_groups({immortal=1})
355             if string.sub(staticdata, 1, string.len("return")) ~= "return" then
356                   return
357             end
358             local data = minetest.deserialize(staticdata)
359             if type(data) ~= "table" then
360                   return
361             end
362             self.railtype = data.railtype
363             if data.old_dir then
364                   self.old_dir = data.old_dir
365             end
366       end,
367
368       get_staticdata = function(self)
369             return minetest.serialize({
370             })
371       end,
372       
373       on_punch = function(self,puncher, time_from_last_punch, tool_capabilities, dir, damage)
374             local obj = minetest.add_item(self.object:getpos(), "minecart:minecart")
375             obj:get_luaentity().collection_timer = 2
376             self.object:remove()
377       end,
378
379       --repel from players on track "push"
380       push = function(self)
381             if self.turn_timer > 0.3 then
382                   local pos = self.object:getpos()
383                   local radius = 1.2
384                   for _,object in ipairs(minetest.get_objects_inside_radius(pos, radius)) do
385                         if object:is_player() and object:get_player_name() ~= self.rider then
386                               local player_pos = object:getpos()
387                               pos.y = 0
388                               player_pos.y = 0
389                               
390                               local currentvel = self.object:getvelocity()
391                               local vel = vector.subtract(pos, player_pos)
392                               vel = vector.normalize(vel)
393                               local distance = vector.distance(pos,player_pos)
394                               distance = (radius-distance)*20
395                               vel = vector.multiply(vel,distance)
396                               local acceleration = vector.new(vel.x-currentvel.x,0,vel.z-currentvel.z)
397                               
398                               
399                               if self.axis == "x"      then
400                                     self.object:add_velocity(vector.new(acceleration.x,0,0))
401                               elseif self.axis == "z" then
402                                     self.object:add_velocity(vector.new(0,0,acceleration.z))
403                               else
404                                     self.object:add_velocity(acceleration)
405                               end
406                               
407                               acceleration = vector.multiply(acceleration, -0.5)
408                               object:add_player_velocity(acceleration)
409                         end
410                   end
411             end
412       end,
413
414       --slows the minecart down
415       slowdown = function(self)
416             if not self.moving == true then
417                   local vel = self.object:getvelocity()
418                   local deceleration = vector.multiply(vel, -0.01)
419                   self.object:add_velocity(deceleration)
420             end
421       end,
422
423       --mechanics to follow rails
424       ride_rail = function(self,dtime)
425             minecart_brain(self,dtime)
426       end,
427
428       on_step = function(self,dtime)
429             self.push(self)
430             self.slowdown(self)
431             self.ride_rail(self,dtime)
432       end,
433       
434 })
435
436 minetest.register_craftitem("minecart:minecart", {
437       description = "Minecart",
438       inventory_image = "minecartitem.png",
439       wield_image = "minecartitem.png",
440       on_place = function(itemstack, placer, pointed_thing)
441             if not pointed_thing.type == "node" then
442                   return
443             end
444             
445             if minetest.get_item_group(minetest.get_node(pointed_thing.under).name, "rail")>0 then
446                   minetest.add_entity(pointed_thing.under, "minecart:minecart")
447             else
448                   return
449             end
450
451             itemstack:take_item()
452
453             return itemstack
454       end,
455 })
456
457 minetest.register_craft({
458       output = "minecart:minecart",
459       recipe = {
460             {"main:iron", "", "main:iron"},
461             {"main:iron", "main:iron", "main:iron"},
462       },
463 })