---[[
+local pool = {}
-Basic idealogy
+local player_pool = {}
-goal - > go to goal - > check all this and repeat
+local dirs = {
+ {x= 1,y= 0,z= 0},
+ {x=-1,y= 0,z= 0},
-minecart does check axis dir then -1 1 on opposite axis (x and z)
+ {x= 1,y= 1,z= 0},
+ {x=-1,y= 1,z= 0},
-minecart checks in front and above
+ {x= 1,y=-1,z= 0},
+ {x=-1,y=-1,z= 0},
-minecart checks in front and below
+ {x= 0,y= 0,z= 1},
+ {x= 0,y= 0,z=-1},
-if in front above start moving up
-
-if in front below start moving down
-
-minecart checks if rail in front then if not check sides and if none then stop
-
-if a rail in front of minecart then when past center of node center, turn towards the available rail and then recenter self onto rail on the new axis
+ {x= 0,y= 1,z= 1},
+ {x= 0,y= 1,z=-1},
+ {x= 0,y=-1,z= 1},
+ {x= 0,y=-1,z=-1},
+}
-keep it simple stupid
+local function coupling_particles(pos,truth)
+ local color = "red"
+ if truth then
+ color = "green"
+ end
-make cart make noise
+ minetest.add_particlespawner({
+ amount = 15,
+ time = 0.001,
+ minpos = pos,
+ maxpos = pos,
+ minvel = vector.new(-10,-10,-10),
+ maxvel = vector.new(10,10,10),
+ minacc = {x=0, y=0, z=0},
+ maxacc = {x=0, y=0, z=0},
+ minexptime = 1.1,
+ maxexptime = 1.5,
+ minsize = 1,
+ maxsize = 2,
+ collisiondetection = false,
+ collision_removal = false,
+ vertical = false,
+ texture = "couple_particle.png^[colorize:"..color..":200",
+ glow = 14,
+ })
+end
-]]--
-local path = minetest.get_modpath("minecart")
-dofile(path.."/rail.lua")
+local function data_injection(pos,data)
+ if data then
+ pool[minetest.hash_node_position(pos)] = true
+ else
+ pool[minetest.hash_node_position(pos)] = nil
+ end
+end
+local function speed_limiter(self,speed)
+ local test = self.object:get_velocity()--vector.multiply(self.velocity,new_vel)
+ if test.x > speed then
+ test.x = speed
+ elseif test.x < -speed then
+ test.x = -speed
+ end
+ if test.z > speed then
+ test.z = speed
+ elseif test.z < -speed then
+ test.z = -speed
+ end
+ self.object:set_velocity(test)
+end
-local function is_rail(x,y,z)
- return(minetest.get_node_group(minetest.get_node(vector.new(x,y,z)).name,"rail")>0)
+local function create_axis(pos)
+ local possible_dirs = {}
+ for _,dir in pairs(dirs) do
+ local pos2 = vector.add(pos,dir)
+ if pool[minetest.hash_node_position(pos2)] then
+ table.insert(possible_dirs,dir)
+ end
+ end
+ return(possible_dirs)
end
-local minecart = {
- initial_properties = {
- physical = false, -- otherwise going uphill breaks
- collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},--{-0.5, -0.4, -0.5, 0.5, 0.25, 0.5},
- visual = "mesh",
- mesh = "minecart.b3d",
- visual_size = {x=1, y=1},
- textures = {"minecart.png"},
- automatic_face_movement_dir = 90.0,
- },
+local function collision_detect(self)
+ if not self.axis_lock then return end
+ local pos = self.object:get_pos()
+ for _,object in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
+ if object:is_player() then
+ local pos2 = object:get_pos()
+ if self.axis_lock == "x" then
+
+ local velocity = (1-vector.distance(vector.new(pos.x,0,0),vector.new(pos2.x,0,0)))*5
+ local dir = vector.direction(vector.new(pos2.x,0,0),vector.new(pos.x,0,0))
+ local new_vel = vector.multiply(dir,velocity)
+ self.object:add_velocity(new_vel)
+ self.dir = dir
+ elseif self.axis_lock == "z" then
+ local velocity = (1-vector.distance(vector.new(0,0,pos.z),vector.new(0,0,pos2.z)))*5
+ local dir = vector.direction(vector.new(0,0,pos2.z),vector.new(0,0,pos.z))
+ local new_vel = vector.multiply(dir,velocity)
+ self.object:add_velocity(new_vel)
+ self.dir = dir
+ end
+ return
+ end
+ end
+end
- rider = nil,
- punched = false,
-
-}
+local function direction_snap(self)
+ local dir = self.dir
+ local pitch = 0
+ if dir.y == 1 then pitch = math.pi/4 end
+ if dir.y == -1 then pitch = -math.pi/4 end
+ local yaw = minetest.dir_to_yaw(dir)
+ self.object:set_rotation(vector.new(pitch,yaw,0))
+end
-function minecart:on_rightclick(clicker)
- if not clicker or not clicker:is_player() then
- return
+local function turn_snap(pos,self,dir,dir2)
+ if self.axis_lock == "x" then
+ if dir.x ~= 0 and dir2.z ~= 0 then
+ local velocity = self.object:get_velocity()
+ local inertia = math.abs(velocity.x)
+ self.object:set_velocity(vector.multiply(dir2,inertia))
+ self.dir = dir2
+ self.axis_lock = "z"
+ self.object:set_pos(pos)
+ direction_snap(self)
+ return(true)
+ end
end
- local player_name = clicker:get_player_name()
- if self.rider and player_name == self.rider then
- self.rider = nil
- carts:manage_attachment(clicker, nil)
- elseif not self.rider then
- self.rider = player_name
- carts:manage_attachment(clicker, self.object)
-
- -- player_api does not update the animation
- -- when the player is attached, reset to default animation
-
- --player_api.set_animation(clicker, "stand")
+ if self.axis_lock == "z" then
+ if dir.z ~= 0 and dir2.x ~= 0 then
+ local velocity = self.object:get_velocity()
+ local inertia = math.abs(velocity.z)
+ self.object:set_velocity(vector.multiply(dir2,inertia))
+ self.dir = dir2
+ self.axis_lock = "x"
+ self.object:set_pos(pos)
+ direction_snap(self)
+ return(true)
+ end
end
+ return(false)
end
-function minecart:on_activate(staticdata, dtime_s)
- self.object:set_armor_groups({immortal=1})
- if string.sub(staticdata, 1, string.len("return")) ~= "return" then
- return
- end
- local data = minetest.deserialize(staticdata)
- if type(data) ~= "table" then
- return
+local function climb_snap(pos,self,dir,dir2)
+ if self.axis_lock == "x" then
+ if dir.x == dir2.x and dir2.y ~= 0 then
+ local velocity = self.object:get_velocity()
+ local inertia = math.abs(velocity.x)
+ self.object:set_velocity(vector.multiply(dir2,inertia))
+ self.dir = dir2
+ self.axis_lock = "x"
+ self.object:set_pos(pos)
+ direction_snap(self)
+ return(true)
+ end
end
- self.railtype = data.railtype
- if data.old_dir then
- self.old_dir = data.old_dir
+ if self.axis_lock == "z" then
+ if dir.z == dir2.z and dir2.y ~= 0 then
+ local velocity = self.object:get_velocity()
+ local inertia = math.abs(velocity.z)
+ self.object:set_velocity(vector.multiply(dir2,inertia))
+ self.dir = dir2
+ self.axis_lock = "z"
+ self.object:set_pos(pos)
+ direction_snap(self)
+ return(true)
+ end
end
+ return(false)
end
-function minecart:get_staticdata()
- return minetest.serialize({
- })
-end
-function minecart:on_punch(puncher, time_from_last_punch, tool_capabilities, dir, damage)
- self.object:remove()
+local function straight_snap(pos,self,dir)
+ if self.axis_lock == "x" then
+ if dir.x ~= 0 and pool[minetest.hash_node_position(vector.add(pos,vector.new(dir.x,0,0)))] then
+ local velocity = self.object:get_velocity()
+ self.object:set_velocity(vector.new(velocity.x,0,0))
+ self.dir = vector.new(dir.x,0,0)
+ self.axis_lock = "x"
+ self.object:set_pos(pos)
+ direction_snap(self)
+ return(true)
+ end
+ end
+ if self.axis_lock == "z" then
+ if dir.z ~= 0 and pool[minetest.hash_node_position(vector.add(pos,vector.new(0,0,dir.z)))] then
+ local velocity = self.object:get_velocity()
+ self.object:set_velocity(vector.new(0,0,velocity.z))
+ self.dir = vector.new(0,0,dir.z)
+ self.axis_lock = "z"
+ self.object:set_pos(pos)
+ direction_snap(self)
+ return(true)
+ end
+ end
+ return(false)
end
+local function coupling_logic(self)
+
+ if not self.axis_lock then return end
+ if not self.coupler1 then return end
+ if self.dir.y ~= 0 then return end
-
---repel from players on track "push"
-function minecart:push(self)
- local pos = self.object:getpos()
- for _,object in ipairs(minetest.get_objects_inside_radius(pos, 2)) do
- if object:is_player() then
- local player_pos = object:getpos()
- pos.y = 0
- player_pos.y = 0
-
- local vel = vector.subtract(pos, player_pos)
- vel = vector.normalize(vel)
- local distance = vector.distance(pos,player_pos)
-
- distance = (2-distance)*3
-
- vel = vector.multiply(vel,distance)
-
- self.object:setvelocity(vel)
-
+ local pos = self.object:get_pos()
+
+ local pos2 = self.coupler1:get_pos()
+
+ if self.axis_lock == "x" then
+ local velocity_real = self.object:get_velocity()
+ local distance = vector.distance(pos,pos2)
+ local new_vel = vector.new(0,0,0)
+ if distance > 1.5 then
+ local velocity = (distance-1)
+ local dir = vector.direction(vector.new(pos.x,0,0),vector.new(pos2.x,0,0))
+ self.dir = dir
+ new_vel = vector.multiply(dir,velocity)
+ else
+ new_vel = vector.multiply(velocity_real,-1)
+ end
+ self.object:add_velocity(new_vel)
+ elseif self.axis_lock == "z" then
+ local velocity_real = self.object:get_velocity()
+ local distance = vector.distance(pos,pos2)
+ local new_vel = vector.new(0,0,0)
+ if distance > 1.5 then
+ local velocity = (distance-1)
+ local dir = vector.direction(vector.new(0,0,pos.z),vector.new(0,0,pos2.z))
+ self.dir = dir
+ new_vel = vector.multiply(dir,velocity)
+ else
+ new_vel = vector.multiply(velocity_real,-1)
end
+ self.object:add_velocity(new_vel)
end
+
+ return
end
-function minecart:ride_rail(self)
- --floor position!!!!
-
- local pos = vector.floor(vector.add(self.object:getpos(),0.5))
- local speed = 5 --change to the cart speed soon
-
- local vel = self.object:getvelocity()
- local x = math.abs(vel.x)
- local z = math.abs(vel.z)
- local xdir
- local zer
- local dir = {x=0,y=0,z=0}
-
- --check direction
- --x axis
- if x > z then
- if vel.x>0 then xdir=1 elseif vel.x<0 then xdir=-1 end
-
- --print(minetest.get_node(vector.new(pos.x,pos.y,pos.z)).name)
-
- --go up
- if is_rail(pos.x+xdir,pos.y+1,pos.z) or (not is_rail(pos.x,pos.y,pos.z) and is_rail(pos.x+xdir,pos.y,pos.z)) then
- print("up")
- dir.y = speed
- dir.x = xdir*speed
- end
-
- --go down
- if dir.y == 0 and is_rail(pos.x,pos.y-1,pos.z) then
- print("down")
- dir.y = -speed
- dir.x = xdir*speed
- end
-
- --go flat
- if is_rail(pos.x,pos.y,pos.z) then --currently on rail
- print("forward inside")
- --correct y position
- if dir.y == 0 and self.object:getpos().y ~= pos.y then
- print("correcting y")
- local posser = self.object:getpos()
- self.object:moveto(vector.new(posser.x,pos.y,posser.z))
- end
- dir.x = xdir*speed
- end
- --z axis
- elseif z > x then
- if vel.z>0 then zdir=1 elseif vel.z<0 then zdir=-1 end
-
- --print(minetest.get_node(vector.new(pos.x,pos.y,pos.z)).name)
-
- --go up
- if is_rail(pos.x,pos.y+1,pos.z+zdir) or (not is_rail(pos.x,pos.y,pos.z) and is_rail(pos.x,pos.y,pos.z+zdir)) then
- print("up")
- dir.y = speed
- dir.z = zdir*speed
- end
-
- --go down
- if dir.y == 0 and is_rail(pos.x,pos.y-1,pos.z) then
- print("down")
- dir.y = -speed
- dir.z = zdir*speed
+local function rail_brain(self,pos)
+
+
+ if not self.dir then self.dir = vector.new(0,0,0) end
+
+ local pos2 = self.object:get_pos()
+
+ local dir = self.dir
+
+ speed_limiter(self,6)
+
+ if not pool[minetest.hash_node_position(vector.add(pos,dir))] then
+
+ if straight_snap(pos,self,dir) then
+ return
end
-
- --go flat
- if is_rail(pos.x,pos.y,pos.z) then --currently on rail
- print("forward inside")
- --correct y position
- if dir.y == 0 and self.object:getpos().y ~= pos.y then
- print("correcting y")
- local posser = self.object:getpos()
- self.object:moveto(vector.new(posser.x,pos.y,posser.z))
+
+ local possible_dirs = create_axis(pos)
+
+ if table.getn(possible_dirs) == 0 then
+ --stop slow down become physical
+ else
+ for _,dir2 in pairs(possible_dirs) do
+ if turn_snap(pos,self,dir,dir2) then
+ return
+ end
+ if climb_snap(pos,self,dir,dir2) then
+ return
+ end
end
- dir.z = zdir*speed
end
+ else
+ coupling_logic(self)
end
- --turn
- if vector.equals(dir,vector.new(0,0,0)) then
- if x > z then
- if is_rail(pos.x,pos.y,pos.z+1) then
- dir.z = 1
- --recenter on the rail
- self.object:moveto(pos)
- elseif is_rail(pos.x,pos.y,pos.z-1) then
- dir.z = -1
- --recenter on the rail
- self.object:moveto(pos)
- end
- --[[
- elseif z > x then
- if is_rail(pos.x+1,pos.y,pos.z) then
- dir.x = 1
- --recenter on the rail
- self.object:moveto(pos)
- elseif is_rail(pos.x-1,pos.y,pos.z) then
- dir.x = -1
- --recenter on the rail
- self.object:moveto(pos)
+
+end
+
+local minecart = {}
+
+minecart.on_step = function(self,dtime)
+ local pos = vector.round(self.object:get_pos())
+ if not self.axis_lock then
+ local possible_dirs = create_axis(pos)
+ for _,dir in pairs(possible_dirs) do
+ if dir.x ~=0 then
+ self.axis_lock = "x"
+ self.dir = dir
+ direction_snap(self)
+ break
+ elseif dir.z ~= 0 then
+ self.axis_lock = "z"
+ self.dir = dir
+ direction_snap(self)
+ break
end
- ]]--
end
+ else
+ rail_brain(self,pos)
+ --collision_detect(self)
end
- --apply
- --if not vector.equals(dir,vector.new(0,0,0)) then
- self.object:setvelocity(dir)
- --end
- self.olddir=dir
end
+minecart.on_punch = function(self, puncher)
+ if not puncher:get_wielded_item():get_name() == "minecart:wrench" then
+ return
+ end
+ if self.furnace then
+ self.object:set_velocity(vector.multiply(self.dir,6))
+ minetest.add_particlespawner({
+ amount = 30,
+ time = 0,
+ minpos = vector.new(0,0.5,0),
+ maxpos = vector.new(0,0.5,0),
+ minvel = vector.new(0,0,0),
+ maxvel = vector.new(0,0,0),
+ minacc = {x=0, y=3, z=0},
+ maxacc = {x=0, y=5, z=0},
+ minexptime = 1.1,
+ maxexptime = 1.5,
+ minsize = 1,
+ maxsize = 2,
+ collisiondetection = false,
+ collision_removal = false,
+ vertical = false,
+ texture = "smoke.png",
+ attached = self.object
+ })
+ end
-function minecart:on_step(dtime)
- minecart:push(self)
- minecart:ride_rail(self)
end
-minetest.register_entity("minecart:minecart", minecart)
+minecart.on_rightclick = function(self,clicker)
+ local pos = self.object:get_pos()
+ if clicker:get_wielded_item():get_name() == "utility:furnace" then
+ local obj = minetest.add_entity(pos, "minecart:furnace")
+ obj:set_attach(self.object,"",vector.new(0,0,0),vector.new(0,0,0))
+ minetest.sound_play("wrench",{
+ object = self.object,
+ gain = 1.0,
+ max_hear_distance = 64,
+ })
+ coupling_particles(pos,true)
+ self.furnace = true
+ return
+ end
+ if not clicker:get_wielded_item():get_name() == "minecart:wrench" then
+ return
+ end
+ local name = clicker:get_player_name()
+ if not pool[name] then
+ if not self.coupler2 then
+ pool[name] = self.object
+ minetest.sound_play("wrench",{
+ object = self.object,
+ gain = 1.0,
+ max_hear_distance = 64,
+ })
+ coupling_particles(pos,true)
+ else
+ minetest.sound_play("wrench",{
+ object = self.object,
+ gain = 1.0,
+ max_hear_distance = 64,
+ pitch = 0.7,
+ })
+ coupling_particles(pos,false)
+ end
+ else
+ if not (pool[name]:get_luaentity().coupler1 and pool[name]:get_luaentity().coupler1 == self.object or self.coupler2) then
+ self.coupler1 = pool[name]
+ pool[name]:get_luaentity().coupler2 = self.object
+ minetest.sound_play("wrench",{
+ object = self.object,
+ gain = 1.0,
+ max_hear_distance = 64,
+ })
+ coupling_particles(pos,true)
+ else
+ minetest.sound_play("wrench",{
+ object = self.object,
+ gain = 1.0,
+ max_hear_distance = 64,
+ pitch = 0.7,
+ })
+ coupling_particles(pos,false)
+ end
+ pool[name] = nil
+ end
+end
+--get old data
+minecart.on_activate = function(self,staticdata, dtime_s)
+ self.object:set_armor_groups({immortal=1})
+ if string.sub(staticdata, 1, string.len("return")) ~= "return" then
+ return
+ end
+ local data = minetest.deserialize(staticdata)
+ if type(data) ~= "table" then
+ return
+ end
+ self.old_pos = self.object:get_pos()
+ self.velocity = vector.new(0,0,0)
+end
+minecart.get_staticdata = function(self)
+ return minetest.serialize({
+ })
+end
+minecart.initial_properties = {
+ physical = false, -- otherwise going uphill breaks
+ 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},
+ visual = "mesh",
+ mesh = "minecart.x",
+ visual_size = {x=1, y=1},
+ textures = {"minecart.png"},
+}
+
+
+
+minetest.register_entity("minecart:minecart", minecart)
return
end
+ local sneak = placer:get_player_control().sneak
+ local noddef = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
+ if not sneak and noddef.on_rightclick then
+ minetest.item_place(itemstack, placer, pointed_thing)
+ return
+ end
+
if minetest.get_item_group(minetest.get_node(pointed_thing.under).name, "rail")>0 then
minetest.add_entity(pointed_thing.under, "minecart:minecart")
else
{"main:iron", "main:iron", "main:iron"},
},
})
+
+
+
+
+
+minetest.register_node("minecart:rail",{
+ description = "Rail",
+ wield_image = "rail.png",
+ tiles = {
+ "rail.png", "railcurve.png",
+ "railt.png", "railcross.png"
+ },
+ drawtype = "raillike",
+ paramtype = "light",
+ sunlight_propagates = true,
+ is_ground_content = false,
+ walkable = false,
+ node_placement_prediction = "",
+ selection_box = {
+ type = "fixed",
+ fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
+ },
+ sounds = main.stoneSound(),
+ after_place_node = function(pos)
+ data_injection(pos,true)
+ end,
+ after_destruct = function(pos)
+ data_injection(pos)
+ end,
+ groups={stone=1,wood=1,rail=1,attached_node=1},
+})
+
+
+minetest.register_lbm({
+ name = "minecart:rail",
+ nodenames = {"minecart:rail"},
+ run_at_every_load = true,
+ action = function(pos)
+ data_injection(pos,true)
+ end,
+})
+
+minetest.register_craft({
+ output = "minecart:rail 32",
+ recipe = {
+ {"main:iron","","main:iron"},
+ {"main:iron","main:stick","main:iron"},
+ {"main:iron","","main:iron"}
+ }
+})
+
+
+minetest.register_food("minecart:wrench",{
+ description = "Train Wrench",
+ texture = "wrench.png",
+})
+
+minetest.register_craft({
+ output = "minecart:wrench",
+ recipe = {
+ {"main:iron", "", "main:iron"},
+ {"main:iron", "main:lapis", "main:iron"},
+ {"", "main:lapis", ""}
+ }
+})
+
+
+
+minetest.register_entity("minecart:furnace", {
+ initial_properties = {
+ visual = "wielditem",
+ visual_size = {x = 0.6, y = 0.6},
+ textures = {},
+ physical = true,
+ is_visible = false,
+ collide_with_objects = false,
+ pointable=false,
+ collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
+ },
+ set_node = function(self)
+ self.object:set_properties({
+ is_visible = true,
+ textures = {"utility:furnace"},
+ })
+ end,
+
+
+ on_activate = function(self, staticdata)
+ self.object:set_armor_groups({immortal = 1})
+
+ self:set_node()
+ end,
+})