]> git.lizzy.rs Git - Crafter.git/blobdiff - mods/minecart/init.lua
Add in self check
[Crafter.git] / mods / minecart / init.lua
index 9eeb8a22de0b862e06357d847f676ef49232c0e1..4156aabf6b85cc3de2ef070fdfa73b522c4abe57 100644 (file)
-local path = minetest.get_modpath("minecart")
-dofile(path.."/rail.lua")
+local pool = {}
 
---this begins the minecart library
-local minecart = {}
+local player_pool = {}
+
+
+local dirs = {
+       {x= 1,y= 0,z= 0},
+       {x=-1,y= 0,z= 0},
 
+       {x= 1,y= 1,z= 0}, 
+       {x=-1,y= 1,z= 0},
 
---these are the variables for the minecart
-minecart.max_speed = 15
-minecart.speed = 0
-minecart.rider = nil
+       {x= 1,y=-1,z= 0},
+       {x=-1,y=-1,z= 0},
 
---binary direction
-minecart.get_dir = function(pos,pos2)
-       return(minetest.facedir_to_dir(minetest.dir_to_facedir(vector.direction(pos2,pos))))
+       {x= 0,y= 0,z= 1},
+       {x= 0,y= 0,z=-1},
+
+       {x= 0,y= 1,z= 1},
+       {x= 0,y= 1,z=-1},
+
+       {x= 0,y=-1,z= 1},
+       {x= 0,y=-1,z=-1},
+}
+
+local function coupling_particles(pos,truth)
+       local color = "red"
+       if truth then
+               color = "green"
+       end
+
+       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
---this gets the node position that the minecart is in
-minecart.round_pos = function(pos)
-       return(vector.round(pos))
+
+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
 
---this is called when a player is standing next to the minecart
---it will take their position and convert it into binary then
---will begin movement 
-minecart.set_direction = function(self,dir)
-       if not self.goal then
-               --reset the y to 0 since we will be checking up and down anyways
-               dir.y = 0
-               local pos = vector.add(minecart.round_pos(self.object:get_pos()),dir)   
-               local node = minetest.get_node(pos).name
-               local node_above = minetest.get_node(vector.new(pos.x,pos.y+1,pos.z)).name
-               local node_under = minetest.get_node(vector.new(pos.x,pos.y-1,pos.z)).name
-               
-               --next to
-               if node == "minecart:rail" and node_under ~= "minecart:rail" then
-                       self.dir = dir
-                       self.goal = pos
-               --downhill
-               elseif node == "air" and node_under == "minecart:rail" then
-                       self.dir = vector.new(dir.x,dir.y-1,dir.z)
-                       self.goal = vector.new(pos.x,pos.y-1,pos.z)
-               --uphill
-               elseif node ~= "minecart:rail" and node_above == "minecart:rail" then
-                       self.dir = vector.new(dir.x,dir.y+1,dir.z)
-                       self.goal = vector.new(pos.x,pos.y+1,pos.z)
-               --rail not found
-               else
-                       self.goal = nil
-               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
 
---this will turn the z into x and x into z
-minecart.flip_direction_axis = function(self)
-       self.dir = vector.new(math.abs(self.dir.z),0,math.abs(self.dir.x))
+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
 
---this makes the minecart move in "blocks"
---it will go to it's goal then when it has reached the goal, move to another goal
-minecart.movement = function(self)
-       if self.dir and self.goal then
-               local pos = self.object:get_pos()
-               local movement = vector.direction(pos,self.goal)
-               self.object:set_velocity(vector.multiply(movement,self.speed))
-               local distance_from_goal = vector.distance(pos,self.goal)
-               
-               --this checks how far the minecart is from the "goal node"
-               --aka the node that the minecart was supposed to go to
-               if distance_from_goal < 0.2 then
-                       self.object:set_velocity(vector.new(0,0,0))
-                       self.goal = nil
-                       --self.object:move_to(minecart.round_pos(self.object:get_pos()))
-                       
-                       --if the minecart is slowed down below 1 nph (node per hour)
-                       --try to flip direction if pointing up
-                       --then stop it if failed
-                       if self.speed < 1 then
-                               if self.dir.y == 1 then
-                                       self.dir = vector.multiply(self.dir, -1)
-                                       minecart.set_direction(self,self.dir)
-                               else
-                                       self.speed = 0
-                                       self.dir = nil
-                                       self.goal = nil
-                               end
-                               
-                       --otherwise try to keep going
-                       else
-                               --test to see if minecart will keep moving
-                               minecart.set_direction(self,self.dir)
-                               
-                               --if not rail ahead then we'll try to turn
-                               if not self.goal then                                   
-                                       minecart.flip_direction_axis(self)
-                                       
-                                       minecart.set_direction(self,self.dir)
-                                       
-                                       --if trying to turn that direcion failed we'll try the other
-                                       if not self.goal then
-                                               self.dir = vector.multiply(self.dir, -1)
-                                               minecart.set_direction(self,self.dir)
-                                       end
-                               end
-                               
-                               --and if everything fails, give up and stop
-                               if not self.goal then
-                                       self.speed = 0
-                               end
+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
-               
-               --make minecart slow down, but only so much
-               
-               --speed up going downhill
-               if self.dir and (self.dir.y == -1 or self.rider) and self.speed < 10 then
-                       self.speed = self.speed + 0.05
-               --slow down going uphill
-               elseif self.dir and self.speed > 1 and self.dir.y == 1 then
-                       self.speed = self.speed - 0.05
-               --normal flat friction slowdown
-               elseif self.speed > 1 then
-                       self.speed = self.speed - 0.01
+       end
+end
+
+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
+
+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
+       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
+
+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
-       --stop the minecart from flying off into the distance
-       elseif not vector.equals(self.object:get_velocity(), vector.new(0,0,0)) and (self.speed == 0 or not self.speed) then
-               self.object:set_velocity(vector.new(0,0,0))
-       --this is when the minecart is stopped
-       --gotta figure out some way to apply gravity and make it physical
-       --without breaking the rest of it
-       elseif self.speed == 0 then
-               --self.object:add_velocity(vector.new(0,-10,0))
        end
+       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
+
+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
+
+       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
 
---this simply sets the mesh based on if the minecart is moving up or down
---this will be replaced by set animation in the future
-minecart.set_mesh = function(self)
-       if self.dir and self.dir.y < 0  then
-               self.object:set_animation({x=2,y=2}, 15, 0, true)
-       elseif self.dir and self.dir.y > 0 then
-               self.object:set_animation({x=1,y=1}, 15, 0, true)
+
+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
+
+               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
+               end
        else
-               self.object:set_animation({x=0,y=0}, 15, 0, true)
+               coupling_logic(self)
        end
+
 end
 
+local minecart = {}
+
 minecart.on_step = function(self,dtime)
-       local pos = self.object:get_pos()
-       
-       --get player input (standing next to the minecart)
-       for _,object in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
-               if self.object ~= object and object:is_player() and object:get_player_name() ~= self.rider then
-                       local pos2 = object:get_pos()
-                       minecart.set_direction(self, minecart.get_dir(pos,pos2))
-                       self.speed = 7
+       if dtime > 0.1 then
+               self.object:set_pos(self.old_pos)
+       end
+       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
-       end     
-       
-       --this makes the minecart actually move, it is also
-       --the begining of it's logic
-       minecart.movement(self)
-       
-       --set the minecart's mesh
-       minecart.set_mesh(self)
+       else
+               rail_brain(self,pos)
+               --collision_detect(self)
+       end
+       self.old_pos = self.object:get_pos()
+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
+
 end
 
---make the player ride the minecart
---or make the player get off
+
 minecart.on_rightclick = function(self,clicker)
-       if not clicker or not clicker:is_player() then return end
+       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()
-       
-       --get on the minecart
-       if not self.rider then
-               self.rider = name
-               clicker:set_attach(self.object, "", {x=0, y=0, z=0}, {x=0, y=0, z=0})
-       --get off the minecart
-       elseif name == self.rider then
-               self.rider = nil
-               clicker:set_detach()
+       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 pool[name] ~= self.object and 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
 
@@ -193,33 +414,12 @@ minecart.on_activate = function(self,staticdata, dtime_s)
        if type(data) ~= "table" then
                return
        end
-       self.dir = data.dir
-       self.goal = data.goal
-       self.speed = data.speed
-       
-       --run through if there was a rider then check if they exist and put them back on
-       --and if they don't exist then nillify the rider value
-       if data.rider then
-               if minetest.player_exists(data.rider) then
-                       self.rider = data.rider
-                       local player = minetest.get_player_by_name(data.rider)
-                       player:set_attach(self.object, "", {x=0, y=0, z=0}, {x=0, y=0, z=0})
-               else
-                       self.rider = nil
-               end
-       else
-               self.rider = nil
-       end
-       
-       
+       self.old_pos = self.object:get_pos()
+       self.velocity = vector.new(0,0,0)
 end
---remember data
+
 minecart.get_staticdata = function(self)
        return minetest.serialize({
-               dir = self.dir,
-               goal = self.goal,
-               speed = self.speed,
-               rider = self.rider
        })
 end
 
@@ -232,16 +432,8 @@ minecart.initial_properties = {
        mesh = "minecart.x",
        visual_size = {x=1, y=1},
        textures = {"minecart.png"},
-       automatic_face_movement_dir = 90.0,
-       automatic_face_movement_max_rotation_per_sec = 1200,
 }
 
-
-minecart.on_punch = function(self,puncher, time_from_last_punch, tool_capabilities, dir, damage)
-       local obj = minetest.add_item(self.object:getpos(), "minecart:minecart")
-       self.object:remove()
-end
-
        
 
 minetest.register_entity("minecart:minecart", minecart)
@@ -266,6 +458,13 @@ minetest.register_craftitem("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
@@ -285,3 +484,96 @@ minetest.register_craft({
                {"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,
+})