1 local path = minetest.get_modpath("minecart")
2 dofile(path.."/rail.lua")
6 local function rail(pos)
7 return(minetest.get_node_group(minetest.get_node(pos).name,"rail")>0)
11 local function on_rail(self,pos)
12 if not rail(pos) and not self.slope then
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))
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)))
38 --get current axis (prefers x)
39 local function axis(pos)
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
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)
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))
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))
68 --check if entering new position
69 local function newnode(self,pos)
70 local pos = vector.floor(vector.add(pos,0.5))
78 equals = vector.equals(pos,self.oldpos)
85 --check if past center - used for turning
86 local function pastcenter(self,pos)
88 local center = vector.floor(vector.add(pos,0.5))
90 local pos2d = vector.new(pos.x,0,pos.z)
92 local vel = self.object:getvelocity()
93 local dir = vector.normalize(vel)
95 local checker = vector.round(vector.normalize(vector.subtract(pos2d,center)))
97 local past = vector.equals(checker, dir)
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)))
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)
113 dirup.y = dirup.y + 1
117 minetest.add_particlespawner({
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},
131 collisiondetection = true,
133 texture = "treecapitator.png"
136 local dirdown = vector.new(0,-0.5,0)
138 if rail(vector.add(pos,dirup)) then
141 elseif rail(vector.add(pos,dirdown)) then
150 local function gravity(self,pos)
152 if self.slope == up then
153 local vel = vector.multiply(self.object:getvelocity(), 0.95)
154 self.object:set_velocity(vel)
156 if self.slope == up then
157 local vel = vector.multiply(self.object:getvelocity(), 1.05)
158 self.object:set_velocity(vel)
163 --make the minecart go up and down hills
164 local function navigate_hill(self)
166 local vel = self.object:getvelocity()
167 if self.slope == "up" then
170 if self.axis == "x" then
171 yvel = math.abs(vel.x)*1.1
173 if self.axis == "z" then
174 yvel = math.abs(vel.z)*1.1
176 self.object:setvelocity(vector.new(vel.x,yvel,vel.z))
177 elseif self.slope == "down" then
180 if self.axis == "x" then
181 yvel = math.abs(vel.x)*-1
183 if self.axis == "z" then
184 yvel = math.abs(vel.z)*-1
187 self.object:setvelocity(vector.new(vel.x,yvel,vel.z))
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)
201 if rail(vector.new(pos.x,pos.y,pos.z-1)) then
203 self.object:setvelocity(vector.new(0,0,vel.x*-1))
208 elseif rail(vector.new(pos.x,pos.y,pos.z+1)) then
210 self.object:setvelocity(vector.new(0,0,vel.x))
218 if rail(vector.new(pos.x-1,pos.y,pos.z)) then
220 self.object:setvelocity(vector.new(vel.z*-1,0,0))
225 elseif rail(vector.new(pos.x+1,pos.y,pos.z)) then
227 self.object:setvelocity(vector.new(vel.z,0,0))
236 local function turn(self,pos)
237 if pastcenter(self,pos) then
238 if not node_forward(self,pos) and self.axis then
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
249 local pos = self.object:getpos()
253 if not self.axis then
254 self.axis = axis(pos)
257 if newnode(self,pos) then
260 --check_hill(self,pos)
261 --navigate_hill(self)
270 --check if falling and then fall at the same speed to go down
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},
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,
331 on_rightclick = function(self,clicker)
332 if not clicker or not clicker:is_player() then
335 local player_name = clicker:get_player_name()
337 if self.rider and player_name == self.rider then
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)
346 -- player_api does not update the animation
347 -- when the player is attached, reset to default animation
349 --player_api.set_animation(clicker, "stand")
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
358 local data = minetest.deserialize(staticdata)
359 if type(data) ~= "table" then
362 self.railtype = data.railtype
364 self.old_dir = data.old_dir
368 get_staticdata = function(self)
369 return minetest.serialize({
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")
378 --repel from players on track "push"
379 push = function(self)
380 if self.turn_timer > 0.3 then
381 local pos = self.object:getpos()
383 for _,object in ipairs(minetest.get_objects_inside_radius(pos, radius)) do
384 if object:is_player() and object:get_player_name() ~= self.rider then
385 local player_pos = object:getpos()
389 local currentvel = self.object:getvelocity()
390 local vel = vector.subtract(pos, player_pos)
391 vel = vector.normalize(vel)
392 local distance = vector.distance(pos,player_pos)
393 distance = (radius-distance)*20
394 vel = vector.multiply(vel,distance)
395 local acceleration = vector.new(vel.x-currentvel.x,0,vel.z-currentvel.z)
397 --note : set a maximum velocity that can be added to the cart to limit extreme glitches
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))
404 self.object:add_velocity(acceleration)
407 --acceleration = vector.multiply(acceleration, -0.5)
408 --object:add_player_velocity(acceleration)
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)
423 --mechanics to follow rails
424 ride_rail = function(self,dtime)
425 minecart_brain(self,dtime)
428 on_step = function(self,dtime)
431 self.ride_rail(self,dtime)
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
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")
451 itemstack:take_item()
457 minetest.register_craft({
458 output = "minecart:minecart",
460 {"main:iron", "", "main:iron"},
461 {"main:iron", "main:iron", "main:iron"},