X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=mods%2Fminecart%2Finit.lua;h=a989390bd26ca021ac58923519246932105a50b5;hb=f5d30e9b7bbb32ce9634432ed8973d6fd8d392dd;hp=ccff607230ba516c5513fa13f841a6db773fbf26;hpb=630c740dc1401193f2f4a2c3aaa4cffaf02be7ce;p=Crafter.git diff --git a/mods/minecart/init.lua b/mods/minecart/init.lua index ccff607..a989390 100644 --- a/mods/minecart/init.lua +++ b/mods/minecart/init.lua @@ -1,273 +1,442 @@ ---[[ +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.obj", - 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 zdir - 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 - local turnx = 0 - local turnz = 0 - - if vel.x>0 then turnx=1 elseif vel.x<0 then turnx=-1 end - if vel.z>0 then turnz=1 elseif vel.z<0 then turnz=-1 end - - if turnx and turnz and dir.y == 0 and not vector.equals(dir, vector.new(0,0,0)) and not is_rail(pos.x+turnx,pos.y-1,pos.z+turnz) and not is_rail(pos.x+turnx,pos.y,pos.z+turnz) and not is_rail(pos.x+turnx,pos.y+1,pos.z+turnz) then - if x > z then - if is_rail(pos.x,pos.y,pos.z+1) then - dir.z = speed - dir.x = 0 - --recenter on the rail - self.object:moveto(pos) - elseif is_rail(pos.x,pos.y,pos.z-1) then - dir.z = -speed - dir.x = 0 - --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 = speed - dir.z = 0 - --recenter on the rail - self.object:moveto(pos) - elseif is_rail(pos.x-1,pos.y,pos.z) then - dir.x = -speed - dir.z = 0 - --recenter on the rail - self.object:moveto(pos) +end + +local minecart = {} + +minecart.on_step = function(self,dtime) + 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 - + 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.oldpos=self.object:getpos() - - - self.object:set_properties({mesh="minecart.obj"}) - if vel.y <0 then - print("down") - self.object:set_properties({mesh="minecart_down.obj"}) - elseif vel.y > 0 then - print("up") - self.object:set_properties({mesh="minecart_up.obj"}) - end - return(self.object:set_animation(anim, 1, 0)) + 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 -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) @@ -289,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 @@ -308,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, +})