From b1508fc3a00fdda16c7cf7ac136680c0de191c7b Mon Sep 17 00:00:00 2001 From: Kilarin Date: Tue, 6 May 2014 20:47:05 -0500 Subject: [PATCH 1/4] new controls, touring rails, and some patches --- README.txt | 1 + functions.lua | 33 ++ init.lua | 473 +++++++++++++++++------- textures/carts_rail_crossing_tour.png | Bin 0 -> 648 bytes textures/carts_rail_curved_tour.png | Bin 0 -> 608 bytes textures/carts_rail_t_junction_tour.png | Bin 0 -> 641 bytes textures/carts_rail_tour.png | Bin 0 -> 569 bytes 7 files changed, 383 insertions(+), 124 deletions(-) create mode 100644 textures/carts_rail_crossing_tour.png create mode 100644 textures/carts_rail_curved_tour.png create mode 100644 textures/carts_rail_t_junction_tour.png create mode 100644 textures/carts_rail_tour.png diff --git a/README.txt b/README.txt index 58673ce..afa6868 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,7 @@ Minetest 0.4 mod: carts ======================= by PilzAdam +This version is modified by Kilarin (Donald Hines), all changes CC-0 License of source code: ----------------------- diff --git a/functions.lua b/functions.lua index 8a7da47..0c26f6c 100644 --- a/functions.lua +++ b/functions.lua @@ -25,9 +25,24 @@ end function cart_func:is_rail(p) local nn = minetest.env:get_node(p).name + --print("*!* is_rail p x=",p.x," y=",p.y," z=",p.z," nn=",nn," group rail=", minetest.get_item_group(nn, "rail")) + if nn == "ignore" then + nn=cart_func:get_content_voxel(p) + print("carts: is_rail hit ignore, trying again with get_content: now nn=",nn," group rail=", minetest.get_item_group(nn, "rail"),"p x=",p.x," y=",p.y," z=",p.z) + end return minetest.get_item_group(nn, "rail") ~= 0 end +function cart_func:check_rail_in_direction(pos, dir) + local p = cart_func.v3:add(pos, dir) + if cart_func:is_rail(p) then + return true + else + return false + end +end -- cart:check_rail_in_direction + + function cart_func:is_int(z) z = math.abs(z) return math.abs(math.floor(z+0.5)-z) <= 0.1 @@ -54,3 +69,21 @@ end function cart_func.v3:equal(v1, v2) return v1.x == v2.x and v1.y == v2.y and v1.z == v2.z end + + +function cart_func:get_content_voxel(pos) + local t1 = os.clock() + local vm = minetest.get_voxel_manip() + local pos1, pos2 = vm:read_from_map(vector.add(pos, {x=-1,y=-1,z=-1}),vector.add(pos, {x=1,y=1,z=1})) + local a = VoxelArea:new{ + MinEdge=pos1, + MaxEdge=pos2, + } + + local data = vm:get_data() + local vi = a:indexp(pos) + local railid = data[vi] + local real_name = minetest.get_name_from_content_id(railid) + print(string.format("voxel-ing rail: elapsed time: %.2fms", (os.clock() - t1) * 1000)) + return real_name +end \ No newline at end of file diff --git a/init.lua b/init.lua index 03bc1a3..6e6d411 100644 --- a/init.lua +++ b/init.lua @@ -12,13 +12,20 @@ local cart = { mesh = "cart.x", visual_size = {x=1, y=1}, textures = {"cart.png"}, - + driver = nil, velocity = {x=0, y=0, z=0}, old_pos = nil, old_velocity = nil, pre_stop_dir = nil, - MAX_V = 8, -- Limit of the velocity + MAX_V = 8, -- Limit of the velocity -- + TARGET_TOUR_V = 4.5, -- target touring velocity -- + railcount=0, + ignorekeypos=nil, + lockyaw=false, + yawtarget=nil, + YAW_STEP=math.pi/12 + --smaller YAW_STEP makes for smoother camera turning. BUT, takes much longer and can fall behind } function cart:on_rightclick(clicker) @@ -61,8 +68,9 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi if not puncher or not puncher:is_player() then return end - + if puncher:get_player_control().sneak then + print("cart: end pos="..pos_to_string(vector.round(self.object:getpos())).." railcount="..self.railcount) self.object:remove() local inv = puncher:get_inventory() if minetest.setting_getbool("creative_mode") then @@ -74,12 +82,26 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi end return end - - if puncher == self.driver then + +-- if puncher == self.driver then + if puncher == self.driver and (math.abs(self.velocity.x)>1 or math.abs(self.velocity.z)>1) then return end - + local d = cart_func:velocity_to_dir(direction) + if d.x==0 and d.z==0 then + local fd = minetest.dir_to_facedir(puncher:get_look_dir()) + if fd == 0 then + d.x = 1 + elseif fd == 1 then + d.z = -1 + elseif fd == 2 then + d.x = -1 + elseif fd == 3 then + d.z = 1 + end + end + local s = self.velocity if time_from_last_punch > tool_capabilities.full_punch_interval then time_from_last_punch = tool_capabilities.full_punch_interval @@ -96,105 +118,125 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi self.velocity.z = 6*cart_func:get_sign(self.velocity.z) end end -end +end + + + + -- Returns the direction as a unit vector function cart:get_rail_direction(pos, dir) - local d = cart_func.v3:copy(dir) - - -- Check front - d.y = 0 - local p = cart_func.v3:add(cart_func.v3:copy(pos), d) - if cart_func:is_rail(p) then - return d - end - - -- Check downhill - d.y = -1 - p = cart_func.v3:add(cart_func.v3:copy(pos), d) - if cart_func:is_rail(p) then - return d - end - - -- Check uphill - d.y = 1 - p = cart_func.v3:add(cart_func.v3:copy(pos), d) - if cart_func:is_rail(p) then - return d - end - d.y = 0 - - -- Check left and right - local view_dir - local other_dir - local a - - if d.x == 0 and d.z ~= 0 then - view_dir = "z" - other_dir = "x" - if d.z < 0 then - a = {1, -1} - else - a = {-1, 1} - end - elseif d.z == 0 and d.x ~= 0 then - view_dir = "x" - other_dir = "z" - if d.x > 0 then - a = {1, -1} - else - a = {-1, 1} - end - else - return {x=0, y=0, z=0} - end - - d[view_dir] = 0 - d[other_dir] = a[1] - p = cart_func.v3:add(cart_func.v3:copy(pos), d) - if cart_func:is_rail(p) then - return d - end - d.y = -1 - p = cart_func.v3:add(cart_func.v3:copy(pos), d) - if cart_func:is_rail(p) then - return d - end - d.y = 0 - d[other_dir] = a[2] - p = cart_func.v3:add(cart_func.v3:copy(pos), d) - if cart_func:is_rail(p) then - return d - end - d.y = -1 - p = cart_func.v3:add(cart_func.v3:copy(pos), d) - if cart_func:is_rail(p) then - return d - end - d.y = 0 - - return {x=0, y=0, z=0} -end + --print("get_rail_direction top pos="..pos_to_string(pos).." dir="..pos_to_string(dir)) + local fwd=cart_func.v3:copy(dir) + fwd.y=0 + local up=cart_func.v3:copy(dir) + up.y=1 + local down=cart_func.v3:copy(dir) + down.y=-1 + -- figure out left and right + local left={x=0,y=0,z=0} + local right={x=0,y=0,z=0} + if dir.z ~= 0 and dir.x == 0 then + left.x=-dir.z --left is opposite sign from z + right.x=dir.z --right is same sign as z + elseif dir.x ~= 0 and dir.z == 0 then + left.z=dir.x --left is same sign as x + right.z=-dir.x --right is opposite sign from x + end --left and right + local leftdown=cart_func.v3:copy(left) + leftdown.y=-1 + local rightdown=cart_func.v3:copy(right) + rightdown.y=-1 + --print(" fwd="..pos_to_string(fwd)) + --print(" down="..pos_to_string(down)) + --print(" up="..pos_to_string(up)) + --print(" left="..pos_to_string(left)) + --print(" leftdown="..pos_to_string(leftdown)) + --print(" right="..pos_to_string(right)) + --print(" rightdown="..pos_to_string(rightdown)) + + local ignorekeys=false + --ignorekeypos stores a position where we changed direction because the + --player was pressing left or right keys. once we have changed direction, + --we want to ignore those keys until you have changed to a different rail + --(otherwise you end up reversing) + --I use the ignorekeys boolean to make the key logic more readable + if self.ignorekeypos then --ignorekeypos was set + if cart_func.v3:equal(self.ignorekeypos,pos) then + ignorekeys=true --if still at same position, ignore left and right keys + else + self.ignorekeypos=nil --if ignorekeypos was set but pos does not match anymore, clear it + end + end + + local ctrl=nil + if self.driver and not ignorekeys then + ctrl = self.driver:get_player_control() + end + + if ctrl and ctrl.left then --left key pressed, check left first + if cart_func:check_rail_in_direction(pos,left) then + self.ignorekeypos=cart_func.v3:copy(pos) --ignore keys until pos changes + return left + elseif cart_func:check_rail_in_direction(pos,leftdown) then + self.ignorekeypos=cart_func.v3:copy(pos) + return leftdown + end + elseif ctrl and ctrl.right then --right key pressed, check right first + if cart_func:check_rail_in_direction(pos,right) then + self.ignorekeypos=cart_func.v3:copy(pos) + return right + elseif cart_func:check_rail_in_direction(pos,rightdown) then + self.ignorekeypos=cart_func.v3:copy(pos) + return rightdown + end + end --ctrl.left ctrl.right + + --now for normal checks + if cart_func:check_rail_in_direction(pos,fwd) then + return fwd + elseif cart_func:check_rail_in_direction(pos,down) then + return down + elseif cart_func:check_rail_in_direction(pos,up) then + return up + elseif (not ctrl or not ctrl.left) --only check left if we didnt above + and cart_func:check_rail_in_direction(pos,left) then + return left + elseif (not ctrl or not ctrl.left) + and cart_func:check_rail_in_direction(pos,leftdown) then + return leftdown + elseif (not ctrl or not ctrl.right) --only check right if we didnt above + and cart_func:check_rail_in_direction(pos,right) then + return right + elseif (not ctrl or not ctrl.right) + and cart_func:check_rail_in_direction(pos,rightdown) then + return rightdown + else + return {x=0, y=0, z=0} + end --normal rail checks +end --get_rail_direction + + function cart:calc_rail_direction(pos, vel) local velocity = cart_func.v3:copy(vel) local p = cart_func.v3:copy(pos) if cart_func:is_int(p.x) and cart_func:is_int(p.z) then - + local dir = cart_func:velocity_to_dir(velocity) local dir_old = cart_func.v3:copy(dir) - + dir = self:get_rail_direction(cart_func.v3:round(p), dir) - + local v = math.max(math.abs(velocity.x), math.abs(velocity.z)) velocity = { x = v * dir.x, y = v * dir.y, z = v * dir.z, } - + if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) then - + -- First try this HACK -- Move the cart on the rail if above or under it if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=1, z=0})) and vel.y >= 0 then @@ -214,27 +256,67 @@ function cart:calc_rail_direction(pos, vel) p = cart_func.v3:add(p, {x=0, y=-1, z=0}) return self:calc_rail_direction(p, vel) end - + return {x=0, y=0, z=0}, p end - - if not cart_func.v3:equal(dir, dir_old) then + + if not cart_func.v3:equal(dir, dir_old) then return velocity, cart_func.v3:round(p) end - + end return velocity, p end + + +--because built in pos_to_string doesn't handle nil +function pos_to_string(pos) + if pos==nil then return "(nil)" + else return minetest.pos_to_string(pos) + end --poss==nill +end --pos_to_string + + + function cart:on_step(dtime) - + local pos = self.object:getpos() local dir = cart_func:velocity_to_dir(self.velocity) - + + --*!*debug + if self.old_pos then + local cmp_old=vector.round(self.old_pos) + local cmp_new=vector.round(pos) + if cmp_old.x~=cmp_new.x or cmp_old.y~=cmp_new.y or cmp_old.z~=cmp_new.z then + self.railcount=self.railcount+1 + --local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration")) + --local railtype="" + --if a and a>0 then railtype="power" end + --minetest.chat_send_all("-- cart pos="..pos_to_string(vector.round(pos)).." railcount="..self.railcount.." vel="..pos_to_string(self.velocity).." "..railtype) --*!*debug + end + end + --*!*debug + + local ctrl=nil + if self.driver then + ctrl = self.driver:get_player_control() + if ctrl and ctrl.jump and not self.lockyaw then + self.lockyaw=true + self.yawtarget=self.object:getyaw() + minetest.chat_send_player(self.driver:get_player_name(),"cart: player view locked to cart, hit SNEAK to unlock") + elseif ctrl and ctrl.sneak and self.lockyaw then + self.lockyaw=false + minetest.chat_send_player(self.driver:get_player_name(),"cart: player view NOT locked to cart, hit JUMP to lock") + end + end --check lockyaw if self.driver + + + if not cart_func.v3:equal(self.velocity, {x=0,y=0,z=0}) then self.pre_stop_dir = cart_func:velocity_to_dir(self.velocity) end - + -- Stop the cart if the velocity is nearly 0 -- Only if on a flat railway if dir.y == 0 then @@ -271,12 +353,12 @@ function cart:on_step(dtime) z = 0, } self.old_velocity = self.velocity - return + return end end end end - + self.velocity = {x=0, y=0, z=0} self.object:setvelocity(self.velocity) self.old_velocity = self.velocity @@ -284,11 +366,11 @@ function cart:on_step(dtime) return end end - + -- -- Set the new moving direction -- - + -- Recalcualte the rails that are passed since the last server step local old_dir = cart_func:velocity_to_dir(self.old_velocity) if old_dir.x ~= 0 then @@ -326,17 +408,70 @@ function cart:on_step(dtime) end end end - + -- Calculate the new step self.velocity, pos = self:calc_rail_direction(pos, self.velocity) self.object:setpos(pos) dir = cart_func:velocity_to_dir(self.velocity) - + -- Accelerate or decelerate the cart according to the pitch and acceleration of the rail node - local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration")) - if not a then - a = 0 - end + local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration")) + if not a then + a = 0 + end + local t = tonumber(minetest.env:get_meta(pos):get_string("cart_touring_velocity")) + if not t then t=0 end + if t>0 then + local vx=math.abs(self.velocity.x) + local vy=math.abs(self.velocity.y) + local vz=math.abs(self.velocity.z) + -- make v the largest of the 3 velocities + local v=vx + if vy>v then v=vy end + if vz>v then v=vz end + -- + local diff=0 + local acelordecl=0 + if v>t then + diff=v-t + acelordecl=-1 + elseif vt + --print(" on_step t1 v="..v.." t="..t.." diff="..diff.." a="..a.." acelordecl="..acelordecl) + --adjust for grav + if self.velocity.y<0 then --going downhill so grav will acel by extra 0.13 + --if we are decel then add an extra 0.13 to how much we need to decel + --if we are accel then subtract an extra 0.13 from how much we need to acel + diff=diff-(0.13*acelordecl) + elseif self.velocity.y>0 then --going uphill grav will decl by extra 0.10 + --if we are decel then subtract 0.1 from how much we need to decel + --if we are acel then add 0.1 to how much we need to acel + diff=diff+(0.1*acelordecl) + end -- self.velocity.y<0 + --so now diff is the difference between cart velocity (after this turns grav) + --and our target touring velocity + --print("*!* on_step t2 grav v="..v.." diff="..diff.." a="..a) + if diffa*4 then + a=a*2 --if big difference, play catchup fast! + elseif diff>a*3 then + a=a*1.5 --if big difference, play catchup fast! + end --diff0 + + --check if down arrow is being pressed (hand break) + if self.driver then + local ctrl = self.driver:get_player_control() + if ctrl and ctrl.down then + a=a-0.1 --same as uphill + + end --if hand break + end --if self.driver + if self.velocity.y < 0 then self.velocity = { x = self.velocity.x + (a+0.13)*cart_func:get_sign(self.velocity.x), @@ -354,15 +489,15 @@ function cart:on_step(dtime) x = self.velocity.x + (a-0.03)*cart_func:get_sign(self.velocity.x), y = self.velocity.y + (a-0.03)*cart_func:get_sign(self.velocity.y), z = self.velocity.z + (a-0.03)*cart_func:get_sign(self.velocity.z), - } - + } + -- Place the cart exactly on top of the rail - if cart_func:is_rail(cart_func.v3:round(pos)) then + if cart_func:is_rail(cart_func.v3:round(pos)) then self.object:setpos({x=pos.x, y=math.floor(pos.y+0.5), z=pos.z}) pos = self.object:getpos() end end - + -- Dont switch moving direction -- Only if on flat railway if dir.y == 0 then @@ -376,7 +511,7 @@ function cart:on_step(dtime) self.velocity.z = 0 end end - + -- Allow only one moving direction (multiply the other one with 0) dir = cart_func:velocity_to_dir(self.velocity) self.velocity = { @@ -384,7 +519,8 @@ function cart:on_step(dtime) y = self.velocity.y, z = math.abs(self.velocity.z) * dir.z, } - + + -- Move cart exactly on the rail if dir.x ~= 0 and not cart_func:is_int(pos.z) then pos.z = math.floor(0.5+pos.z) @@ -393,7 +529,7 @@ function cart:on_step(dtime) pos.x = math.floor(0.5+pos.x) self.object:setpos(pos) end - + -- Limit the velocity if math.abs(self.velocity.x) > self.MAX_V then self.velocity.x = self.MAX_V*cart_func:get_sign(self.velocity.x) @@ -404,12 +540,13 @@ function cart:on_step(dtime) if math.abs(self.velocity.z) > self.MAX_V then self.velocity.z = self.MAX_V*cart_func:get_sign(self.velocity.z) end - + self.object:setvelocity(self.velocity) - + self.old_pos = self.object:getpos() self.old_velocity = cart_func.v3:copy(self.velocity) - + + local oldyaw=self.object:getyaw() if dir.x < 0 then self.object:setyaw(math.pi/2) elseif dir.x > 0 then @@ -419,7 +556,34 @@ function cart:on_step(dtime) elseif dir.z > 0 then self.object:setyaw(0) end - + + local newyaw=self.object:getyaw() + --now if driver and lockyaw, change drivers direction. + if self.driver and self.lockyaw then + if oldyaw~=newyaw then + self.yawtarget=newyaw --set new target + --print("--Cart yawtarget set "..self.yawtarget) + end + local playeryaw=self.driver:get_look_yaw()-1.57 + if playeryaw<0 then playeryaw=playeryaw+(math.pi*2) end + if self.yawtarget and playeryaw ~= self.yawtarget then + local diff = self.yawtarget - playeryaw + if diff>math.pi then + diff=diff-(2*math.pi) + elseif diff<(-math.pi) then + diff=diff+(2*math.pi) + end + yawdir=cart_func:get_sign(diff) + local step=self.YAW_STEP + if math.abs(diff)<=self.YAW_STEP then + step=diff + self.yawtarget=nil + end + local setyaw=playeryaw+(step*yawdir) + self.driver:set_look_yaw(setyaw) + end --move yaw + end --lockyaw set + if dir.y == -1 then self.object:set_animation({x=1, y=1}, 1, 0) elseif dir.y == 1 then @@ -427,7 +591,7 @@ function cart:on_step(dtime) else self.object:set_animation({x=0, y=0}, 1, 0) end - + end minetest.register_entity("carts:cart", cart) @@ -437,19 +601,22 @@ minetest.register_craftitem("carts:cart", { description = "Minecart", inventory_image = minetest.inventorycube("cart_top.png", "cart_side.png", "cart_side.png"), wield_image = "cart_side.png", - + on_place = function(itemstack, placer, pointed_thing) if not pointed_thing.type == "node" then return end if cart_func:is_rail(pointed_thing.under) then - minetest.env:add_entity(pointed_thing.under, "carts:cart") + local obj=minetest.env:add_entity(pointed_thing.under, "carts:cart") + print("cart: begin pos="..pos_to_string(vector.round(obj:getpos()))) + minetest.chat_send_player(placer:get_player_name(),"cart: right click to ride, left click to push, SNEAK-left click to put back in inventory, JUMP to lock view to cart, SNEAK to unlock view from cart, LEFT and RIGHT ARROWS to switch tracks, DOWN ARROW to apply hand break") if not minetest.setting_getbool("creative_mode") then itemstack:take_item() end return itemstack elseif cart_func:is_rail(pointed_thing.above) then minetest.env:add_entity(pointed_thing.above, "carts:cart") + print("cart: begin pos="..pos_to_string(self.object:getpos())) if not minetest.setting_getbool("creative_mode") then itemstack:take_item() end @@ -503,19 +670,19 @@ minetest.register_node("carts:powerrail", { fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, }, groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1}, - + after_place_node = function(pos, placer, itemstack) if not mesecon then minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5") end end, - + mesecons = { effector = { action_on = function(pos, node) minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5") end, - + action_off = function(pos, node) minetest.env:get_meta(pos):set_string("cart_acceleration", "0") end, @@ -538,19 +705,19 @@ minetest.register_node("carts:brakerail", { fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, }, groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1}, - + after_place_node = function(pos, placer, itemstack) if not mesecon then minetest.env:get_meta(pos):set_string("cart_acceleration", "-0.2") end end, - + mesecons = { effector = { action_on = function(pos, node) minetest.env:get_meta(pos):set_string("cart_acceleration", "-0.2") end, - + action_off = function(pos, node) minetest.env:get_meta(pos):set_string("cart_acceleration", "0") end, @@ -558,6 +725,44 @@ minetest.register_node("carts:brakerail", { }, }) + +minetest.register_node("carts:touringrail", { + description = "Touring Rail", + drawtype = "raillike", + tiles = {"carts_rail_tour.png", "carts_rail_curved_tour.png", "carts_rail_t_junction_tour.png", "carts_rail_crossing_tour.png"}, + inventory_image = "carts_rail_tour.png", + wield_image = "carts_rail_tour.png", + paramtype = "light", + is_ground_content = true, + walkable = false, + selection_box = { + type = "fixed", + -- but how to specify the dimensions for curved and sideways rails? + fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, + }, + groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1}, + + after_place_node = function(pos, placer, itemstack) + if not mesecon then + minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5") + minetest.env:get_meta(pos):set_string("cart_touring_velocity", cart.TARGET_TOUR_V) + end + end, + + mesecons = { + effector = { + action_on = function(pos, node) + minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5") + end, + + action_off = function(pos, node) + minetest.env:get_meta(pos):set_string("cart_acceleration", "0") + end, + }, + }, +}) + + minetest.register_craft({ output = "carts:powerrail 2", recipe = { @@ -593,3 +798,23 @@ minetest.register_craft({ {"default:steel_ingot", "default:coal_lump", "default:steel_ingot"}, } }) + + +minetest.register_craft({ + output = "carts:touringrail 7", + recipe = { + {"default:steel_ingot", "default:coal_lump", "default:steel_ingot"}, + {"default:steel_ingot", "default:stick", "default:steel_ingot"}, + {"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}, + } +}) + + +minetest.register_craft({ + output = "carts:touringrail 7", + recipe = { + {"default:coal_lump"}, + {"carts:powerrail"}, + {"carts:powerrail"}, + } +}) diff --git a/textures/carts_rail_crossing_tour.png b/textures/carts_rail_crossing_tour.png new file mode 100644 index 0000000000000000000000000000000000000000..3572256948deff283965167ddff9e40ce568552e GIT binary patch literal 648 zcmV;30(bq1P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i^n{ z0VOxO{PFYv00Ia}L_t(I%YBniNYh~$$3O3WnrjG2K{$;fis%xBV?*c?iHGPi9Ba@u z%D}o9kxEcT9cnQ;bm@<$#F(~)hYX6kb%`t}S*TRve)~;PvY^K1_S@58?&ojQ^@SJS z=Y76>-{*av?+a^q^=3kV@~Q;^WQ_}e!=4R>0*Jtst`->?@LHscC!1wtz$YRljvaM& zHGtP|C+O%|tEz*l9KevhViX{D)v;)dT5+wth*<9uK+d1oDUUCCM5Gj7J=H2t`n*!r z0}(+qVDO;nyYAY1jBGB;g$^eN_Uypr)Oi208UN8%cDbAQ{Ws6)*or3dI<&S#*)SRHn(8)Kn9=cR^sMpio7XgawfN9X>N_`{fpCaEP<#&hS6fPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i^n{ z0VWAhu)mxD00H1hL_t(I%e|6IYg17ag};-;+msN{h_M*k1e9XwAYv39IMZ|>I4D|6 zQIJ|%pEw9bD?*@H@BzNCpxk&To%o0qL5L8@50IcBI4#nmsF0$jdDxrBb`X;$nwC0o z5BFiO{hhTJ{Li|2Vw+4|+$r@v+rJ2Wna>h9dW(1uH4(Mi;dmk@01@2)Ao^r_bp(G7 zwwp+zt0#?#H4Ck{o7cdSF{$5O&gq? zy8TuGgnB!~)3&xk5I)u^6NBBd9Lcrltjgw0vbj7{W4id;K2rizVzW zRt6?sD-q<<5g#*_0K+3g&4YCF`5a1BgcV4k1e#3|4eyqRqeW&cK88nzcr|&F?VW8a uNBQ2%kE$+6VznsHd;oNBadZ57QvCvMYp&nV%DS2W0000Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i^n{ z0VOKg`kgHR00IF?L_t(I%Z-ywXcJKsg}<8&G1i}@AYu^}T?iHwX%}uJ*hP0`m`)2W z6j7mWtW*$`&_x$Tlq!mZ#MuefrcptN5Qz(SA|kkSA&ga2XwjDDx1C%U$xJ69h!@`C zy?5U^=iYlBw4zYVE8wrLp^93zsjXZ941gy$`xU^lO%l7m5HliLs(A>|P%xsx`bLA) zl}G?KJAPCr9)1!7;O)on44-OVe0G=F(POZ@QfK>CgG$Y#Cmy9*Z&0pzuya2EN6Z-t zW=L(Jm{-d-RkeEwg<@X)9LqKpn&n71VK6b|Z>6I*WYSImcH!cA1`|W5b};w~Zj2q& z$JbA^F}9Q`(AGq{P2rl^tKq$0R<|ic!XTDyq6(+SQ)JQ((`OH}kQ_jyS&|53pXS<# zBb`cKzsZwHJIv4JSo%}J{k6iq%YD8AEZh7y05n-d;O>jB@?)vY>RJ_N^0@w&f=t@+ zyDI>D5VFaBeKffdJbv}y60^@2#pv|G{M=>_?!WvNC=@}X5rNpg=4U#v b3s~eYm#PhtXYgh~00000NkvXXu0mjfU6B=* literal 0 HcmV?d00001 diff --git a/textures/carts_rail_tour.png b/textures/carts_rail_tour.png new file mode 100644 index 0000000000000000000000000000000000000000..49dd1f9f0dc34d610fde2121adbc36fd6bd4e59d GIT binary patch literal 569 zcmV-90>=G`P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i^n{ z0VNz3KnSz|00Fs4L_t(I%gvHKYZOrwhM#-qW;W}BAE2a&MZiL^34W!JbXtjpuq+F9 zqFn?4D5u{Qr%gpR_-eUk>j1A@&4{r4Hc-Tm{(If)Cui0u@ ztP{qr$2n|r{vnbk1_yH@0=BYO0~>|-p^Q@Nbiv%o>BzOnEgdV)OG~Th}y<+0G{7HCznRw zq#k_y+Mp*Re)!TLY$bg9=AjfA4RJ?k+XPQ5D5ib2h2T#Q{^lF^#)GNRft>j0)=4Hu zv}{F=g0DGxxJW$+@w+tRLoR>#WcajPyZ<)zU~XZVi|5nAQoVukG_AJ7Q?MNPgt5a~ zApt049xMj`Qw}C>pUQJ^<-`xd$VuFB{Mu~O*HgkeVRb#iXvNFbV*rc~^~=qN@3(XC zlfI1j>fLv`3z}A(u& Date: Tue, 6 May 2014 21:35:57 -0500 Subject: [PATCH 2/4] update to readme --- README.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.txt b/README.txt index afa6868..61efae6 100644 --- a/README.txt +++ b/README.txt @@ -2,6 +2,16 @@ Minetest 0.4 mod: carts ======================= by PilzAdam This version is modified by Kilarin (Donald Hines), all changes CC-0 +modifications include: + New Controls:LEFT or RIGHT to switch track at junction + DOWN for hand break + JUMP lock the users view to the view of the cart + SNEAK unlock the users view from the cart + Touring Rails: Try to maintain a speed of 4.5 + Chat notification of controls when cart is placed + Track rail count in debug.txt + punch by driver patch from spillz + get_voxel when hit ignore patch by minermoder27 License of source code: ----------------------- From cf5b17db319bc94a5d49f3725bc7e0f717f487f3 Mon Sep 17 00:00:00 2001 From: Kilarin Date: Wed, 7 May 2014 12:39:48 -0500 Subject: [PATCH 3/4] corrected infinite loop problem in calc_rail_direction --- init.lua | 67 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/init.lua b/init.lua index 6e6d411..40c8a4b 100644 --- a/init.lua +++ b/init.lua @@ -20,7 +20,7 @@ local cart = { pre_stop_dir = nil, MAX_V = 8, -- Limit of the velocity -- TARGET_TOUR_V = 4.5, -- target touring velocity -- - railcount=0, + railcount=0, ignorekeypos=nil, lockyaw=false, yawtarget=nil, @@ -70,7 +70,7 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi end if puncher:get_player_control().sneak then - print("cart: end pos="..pos_to_string(vector.round(self.object:getpos())).." railcount="..self.railcount) + print("cart: end pos="..pos_to_string(vector.round(self.object:getpos())).." railcount="..self.railcount) self.object:remove() local inv = puncher:get_inventory() if minetest.setting_getbool("creative_mode") then @@ -118,7 +118,7 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi self.velocity.z = 6*cart_func:get_sign(self.velocity.z) end end -end +end @@ -164,16 +164,16 @@ function cart:get_rail_direction(pos, dir) if self.ignorekeypos then --ignorekeypos was set if cart_func.v3:equal(self.ignorekeypos,pos) then ignorekeys=true --if still at same position, ignore left and right keys - else + else self.ignorekeypos=nil --if ignorekeypos was set but pos does not match anymore, clear it end end - - local ctrl=nil + + local ctrl=nil if self.driver and not ignorekeys then ctrl = self.driver:get_player_control() end - + if ctrl and ctrl.left then --left key pressed, check left first if cart_func:check_rail_in_direction(pos,left) then self.ignorekeypos=cart_func.v3:copy(pos) --ignore keys until pos changes @@ -221,6 +221,7 @@ end --get_rail_direction function cart:calc_rail_direction(pos, vel) local velocity = cart_func.v3:copy(vel) local p = cart_func.v3:copy(pos) + if cart_func:is_int(p.x) and cart_func:is_int(p.z) then local dir = cart_func:velocity_to_dir(velocity) @@ -235,7 +236,8 @@ function cart:calc_rail_direction(pos, vel) z = v * dir.z, } - if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) then + --if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) then + if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) and not cart_func:is_rail(p) then -- First try this HACK -- Move the cart on the rail if above or under it @@ -260,7 +262,7 @@ function cart:calc_rail_direction(pos, vel) return {x=0, y=0, z=0}, p end - if not cart_func.v3:equal(dir, dir_old) then + if not cart_func.v3:equal(dir, dir_old) then return velocity, cart_func.v3:round(p) end @@ -283,8 +285,10 @@ function cart:on_step(dtime) local pos = self.object:getpos() local dir = cart_func:velocity_to_dir(self.velocity) + --minetest.chat_send_all("*!*carts: on_step top pos="..pos_to_string(pos).." dir="..pos_to_string(dir).." vel="..pos_to_string(self.velocity)) + --print("*!*carts:on_step top pos="..pos_to_string(pos).." dir="..pos_to_string(dir).." vel="..pos_to_string(self.velocity)) - --*!*debug + --count rails to put in debug.txt if self.old_pos then local cmp_old=vector.round(self.old_pos) local cmp_new=vector.round(pos) @@ -293,25 +297,24 @@ function cart:on_step(dtime) --local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration")) --local railtype="" --if a and a>0 then railtype="power" end - --minetest.chat_send_all("-- cart pos="..pos_to_string(vector.round(pos)).." railcount="..self.railcount.." vel="..pos_to_string(self.velocity).." "..railtype) --*!*debug + --minetest.chat_send_all("-- cart pos="..pos_to_string(vector.round(pos)).." railcount="..self.railcount.." vel="..pos_to_string(self.velocity).." "..railtype) end end - --*!*debug - - local ctrl=nil + + local ctrl=nil if self.driver then ctrl = self.driver:get_player_control() if ctrl and ctrl.jump and not self.lockyaw then self.lockyaw=true self.yawtarget=self.object:getyaw() - minetest.chat_send_player(self.driver:get_player_name(),"cart: player view locked to cart, hit SNEAK to unlock") + minetest.chat_send_player(self.driver:get_player_name(),"cart: player view locked to cart, hit SNEAK to unlock") elseif ctrl and ctrl.sneak and self.lockyaw then self.lockyaw=false - minetest.chat_send_player(self.driver:get_player_name(),"cart: player view NOT locked to cart, hit JUMP to lock") + minetest.chat_send_player(self.driver:get_player_name(),"cart: player view NOT locked to cart, hit JUMP to lock") end - end --check lockyaw if self.driver - - + end --check lockyaw if self.driver + + if not cart_func.v3:equal(self.velocity, {x=0,y=0,z=0}) then self.pre_stop_dir = cart_func:velocity_to_dir(self.velocity) @@ -319,6 +322,7 @@ function cart:on_step(dtime) -- Stop the cart if the velocity is nearly 0 -- Only if on a flat railway + if dir.y == 0 then if math.abs(self.velocity.x) < 0.1 and math.abs(self.velocity.z) < 0.1 then -- Start the cart if powered from mesecons @@ -463,14 +467,14 @@ function cart:on_step(dtime) a=a*acelordecl end -- if t>0 - --check if down arrow is being pressed (hand break) + --check if down arrow is being pressed (hand break) if self.driver then local ctrl = self.driver:get_player_control() if ctrl and ctrl.down then - a=a-0.1 --same as uphill - - end --if hand break - end --if self.driver + a=a-0.1 --same as uphill + + end --if hand break + end --if self.driver if self.velocity.y < 0 then self.velocity = { @@ -489,7 +493,7 @@ function cart:on_step(dtime) x = self.velocity.x + (a-0.03)*cart_func:get_sign(self.velocity.x), y = self.velocity.y + (a-0.03)*cart_func:get_sign(self.velocity.y), z = self.velocity.z + (a-0.03)*cart_func:get_sign(self.velocity.z), - } + } -- Place the cart exactly on top of the rail if cart_func:is_rail(cart_func.v3:round(pos)) then @@ -520,7 +524,6 @@ function cart:on_step(dtime) z = math.abs(self.velocity.z) * dir.z, } - -- Move cart exactly on the rail if dir.x ~= 0 and not cart_func:is_int(pos.z) then pos.z = math.floor(0.5+pos.z) @@ -556,33 +559,33 @@ function cart:on_step(dtime) elseif dir.z > 0 then self.object:setyaw(0) end - + local newyaw=self.object:getyaw() --now if driver and lockyaw, change drivers direction. if self.driver and self.lockyaw then if oldyaw~=newyaw then self.yawtarget=newyaw --set new target --print("--Cart yawtarget set "..self.yawtarget) - end + end local playeryaw=self.driver:get_look_yaw()-1.57 if playeryaw<0 then playeryaw=playeryaw+(math.pi*2) end if self.yawtarget and playeryaw ~= self.yawtarget then local diff = self.yawtarget - playeryaw if diff>math.pi then diff=diff-(2*math.pi) - elseif diff<(-math.pi) then + elseif diff<(-math.pi) then diff=diff+(2*math.pi) - end + end yawdir=cart_func:get_sign(diff) local step=self.YAW_STEP if math.abs(diff)<=self.YAW_STEP then step=diff self.yawtarget=nil - end + end local setyaw=playeryaw+(step*yawdir) self.driver:set_look_yaw(setyaw) end --move yaw - end --lockyaw set + end --lockyaw set if dir.y == -1 then self.object:set_animation({x=1, y=1}, 1, 0) From 8f1b30ac728e6a96a4c25f5801baba9379e0140d Mon Sep 17 00:00:00 2001 From: Kilarin Date: Wed, 14 May 2014 16:01:41 -0500 Subject: [PATCH 4/4] hack loop to fix cart above-below rails --- README.txt | 6 +- functions.lua | 64 ++++-- init.lua | 565 +++++++++++++++++++++++++------------------------- 3 files changed, 327 insertions(+), 308 deletions(-) diff --git a/README.txt b/README.txt index 61efae6..94b6fdd 100644 --- a/README.txt +++ b/README.txt @@ -9,9 +9,11 @@ modifications include: SNEAK unlock the users view from the cart Touring Rails: Try to maintain a speed of 4.5 Chat notification of controls when cart is placed - Track rail count in debug.txt + Track rail count in debug.txt punch by driver patch from spillz - get_voxel when hit ignore patch by minermoder27 + get_voxel when hit ignore patch by minermoder27 + searches further up and down for rail when cart falls off rails in order to + avoid cart getting stuck above or below rails. License of source code: ----------------------- diff --git a/functions.lua b/functions.lua index 0c26f6c..685257b 100644 --- a/functions.lua +++ b/functions.lua @@ -25,20 +25,21 @@ end function cart_func:is_rail(p) local nn = minetest.env:get_node(p).name - --print("*!* is_rail p x=",p.x," y=",p.y," z=",p.z," nn=",nn," group rail=", minetest.get_item_group(nn, "rail")) - if nn == "ignore" then - nn=cart_func:get_content_voxel(p) - print("carts: is_rail hit ignore, trying again with get_content: now nn=",nn," group rail=", minetest.get_item_group(nn, "rail"),"p x=",p.x," y=",p.y," z=",p.z) - end + if nn == "ignore" then + nn=cart_func:get_content_voxel(p) + print("carts: is_rail hit ignore, trying again with get_content: now nn=",nn," group rail=", minetest.get_item_group(nn, "rail")," p="..cart_func:pos_to_string(p)) + end return minetest.get_item_group(nn, "rail") ~= 0 end +--dir is a vector that indicates the direction to the rail to be checked. +--so, for example, pos={10,20,30} dir={-1,0,0} will check for rail pos {9,20,30} function cart_func:check_rail_in_direction(pos, dir) local p = cart_func.v3:add(pos, dir) if cart_func:is_rail(p) then return true else - return false + return false end end -- cart:check_rail_in_direction @@ -48,16 +49,20 @@ function cart_func:is_int(z) return math.abs(math.floor(z+0.5)-z) <= 0.1 end + cart_func.v3 = {} + function cart_func.v3:add(v1, v2) return {x=v1.x+v2.x, y=v1.y+v2.y, z=v1.z+v2.z} end + function cart_func.v3:copy(v) return {x=v.x, y=v.y, z=v.z} end + function cart_func.v3:round(v) return { x = math.floor(v.x+0.5), @@ -66,24 +71,41 @@ function cart_func.v3:round(v) } end + function cart_func.v3:equal(v1, v2) return v1.x == v2.x and v1.y == v2.y and v1.z == v2.z end +--by minermoder27, used by is_rail ONLY when it gets "ignore" function cart_func:get_content_voxel(pos) - local t1 = os.clock() - local vm = minetest.get_voxel_manip() - local pos1, pos2 = vm:read_from_map(vector.add(pos, {x=-1,y=-1,z=-1}),vector.add(pos, {x=1,y=1,z=1})) - local a = VoxelArea:new{ - MinEdge=pos1, - MaxEdge=pos2, - } - - local data = vm:get_data() - local vi = a:indexp(pos) - local railid = data[vi] - local real_name = minetest.get_name_from_content_id(railid) - print(string.format("voxel-ing rail: elapsed time: %.2fms", (os.clock() - t1) * 1000)) - return real_name -end \ No newline at end of file + local t1 = os.clock() + local vm = minetest.get_voxel_manip() + local pos1, pos2 = vm:read_from_map(vector.add(pos, {x=-1,y=-1,z=-1}),vector.add(pos, {x=1,y=1,z=1})) + local a = VoxelArea:new{ + MinEdge=pos1, + MaxEdge=pos2, + } + + local data = vm:get_data() + local vi = a:indexp(pos) + local railid = data[vi] + local real_name = minetest.get_name_from_content_id(railid) + print(string.format("voxel-ing rail: elapsed time: %.2fms", (os.clock() - t1) * 1000)) + return real_name +end --get_content_voxel + + +function cart_func:round_digits(num,digits) + return math.floor(num*(10^digits)+0.5)/(10^digits) +end --round_digits + + +--because built in pos_to_string doesn't handle nil +--this also rounds to 2 digits +function cart_func:pos_to_string(pos) + if pos==nil then return "(nil)" + else return "(" .. cart_func:round_digits(pos.x,2) .. "," .. cart_func:round_digits(pos.y,2) .. "," .. cart_func:round_digits(pos.z,2) .. ")" + end --pos==nill +end --pos_to_string + diff --git a/init.lua b/init.lua index 40c8a4b..661aa44 100644 --- a/init.lua +++ b/init.lua @@ -18,16 +18,21 @@ local cart = { old_pos = nil, old_velocity = nil, pre_stop_dir = nil, - MAX_V = 8, -- Limit of the velocity -- - TARGET_TOUR_V = 4.5, -- target touring velocity -- - railcount=0, - ignorekeypos=nil, - lockyaw=false, - yawtarget=nil, - YAW_STEP=math.pi/12 - --smaller YAW_STEP makes for smoother camera turning. BUT, takes much longer and can fall behind + MAX_V = 8, -- Limit of the velocity -- + TARGET_TOUR_V = 4.5, -- target touring velocity + railcount=0, --stores how many rails a cart has passed over + --ignorekeypos stores a position where we changed direction because the + --player was pressing left or right keys. once we have changed direction, + --we want to ignore those keys until you have changed to a different rail + --(otherwise you end up reversing) + ignorekeypos=nil, -- so will not keep turning when pressing an arrow on track fork + lockyaw=false, -- is player view locked to cart. + yawtarget=nil, -- target yaw for player view when locked to cart + YAW_STEP=math.pi/12 -- step to turn player view when view locked to cart + --smaller YAW_STEP makes for smoother camera turning. BUT, takes much longer and can fall behind } + function cart:on_rightclick(clicker) if not clicker or not clicker:is_player() then return @@ -41,6 +46,7 @@ function cart:on_rightclick(clicker) end end + function cart:on_activate(staticdata, dtime_s) self.object:set_armor_groups({immortal=1}) if staticdata then @@ -56,6 +62,7 @@ function cart:on_activate(staticdata, dtime_s) self.old_velocity = self.velocity end + function cart:get_staticdata() return minetest.serialize({ velocity = self.velocity, @@ -63,6 +70,7 @@ function cart:get_staticdata() }) end + -- Remove the cart if holding a tool or accelerate it function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) if not puncher or not puncher:is_player() then @@ -70,7 +78,7 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi end if puncher:get_player_control().sneak then - print("cart: end pos="..pos_to_string(vector.round(self.object:getpos())).." railcount="..self.railcount) + print("cart: end pos="..cart_func:pos_to_string(vector.round(self.object:getpos())).." railcount="..self.railcount) self.object:remove() local inv = puncher:get_inventory() if minetest.setting_getbool("creative_mode") then @@ -81,10 +89,11 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi inv:add_item("main", "carts:cart") end return - end + end --- if puncher == self.driver then - if puncher == self.driver and (math.abs(self.velocity.x)>1 or math.abs(self.velocity.z)>1) then + --patch by spillz to allow punching a cart you are riding. + --if puncher == self.driver then + if puncher == self.driver and (math.abs(self.velocity.x)>1 or math.abs(self.velocity.z)>1) then return end @@ -121,103 +130,93 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi end - - - -- Returns the direction as a unit vector function cart:get_rail_direction(pos, dir) - --print("get_rail_direction top pos="..pos_to_string(pos).." dir="..pos_to_string(dir)) - local fwd=cart_func.v3:copy(dir) - fwd.y=0 - local up=cart_func.v3:copy(dir) - up.y=1 - local down=cart_func.v3:copy(dir) - down.y=-1 + --print("get_rail_direction top pos="..cart_func:pos_to_string(pos).." dir="..cart_func:pos_to_string(dir)) + local fwd=cart_func.v3:copy(dir) + fwd.y=0 + local up=cart_func.v3:copy(dir) + up.y=1 + local down=cart_func.v3:copy(dir) + down.y=-1 -- figure out left and right - local left={x=0,y=0,z=0} - local right={x=0,y=0,z=0} - if dir.z ~= 0 and dir.x == 0 then - left.x=-dir.z --left is opposite sign from z - right.x=dir.z --right is same sign as z - elseif dir.x ~= 0 and dir.z == 0 then - left.z=dir.x --left is same sign as x - right.z=-dir.x --right is opposite sign from x - end --left and right - local leftdown=cart_func.v3:copy(left) - leftdown.y=-1 - local rightdown=cart_func.v3:copy(right) - rightdown.y=-1 - --print(" fwd="..pos_to_string(fwd)) - --print(" down="..pos_to_string(down)) - --print(" up="..pos_to_string(up)) - --print(" left="..pos_to_string(left)) - --print(" leftdown="..pos_to_string(leftdown)) - --print(" right="..pos_to_string(right)) - --print(" rightdown="..pos_to_string(rightdown)) - - local ignorekeys=false - --ignorekeypos stores a position where we changed direction because the - --player was pressing left or right keys. once we have changed direction, - --we want to ignore those keys until you have changed to a different rail - --(otherwise you end up reversing) - --I use the ignorekeys boolean to make the key logic more readable - if self.ignorekeypos then --ignorekeypos was set - if cart_func.v3:equal(self.ignorekeypos,pos) then - ignorekeys=true --if still at same position, ignore left and right keys - else - self.ignorekeypos=nil --if ignorekeypos was set but pos does not match anymore, clear it - end - end - - local ctrl=nil - if self.driver and not ignorekeys then - ctrl = self.driver:get_player_control() - end - - if ctrl and ctrl.left then --left key pressed, check left first - if cart_func:check_rail_in_direction(pos,left) then - self.ignorekeypos=cart_func.v3:copy(pos) --ignore keys until pos changes - return left - elseif cart_func:check_rail_in_direction(pos,leftdown) then - self.ignorekeypos=cart_func.v3:copy(pos) - return leftdown - end - elseif ctrl and ctrl.right then --right key pressed, check right first - if cart_func:check_rail_in_direction(pos,right) then - self.ignorekeypos=cart_func.v3:copy(pos) - return right - elseif cart_func:check_rail_in_direction(pos,rightdown) then - self.ignorekeypos=cart_func.v3:copy(pos) - return rightdown - end - end --ctrl.left ctrl.right - - --now for normal checks - if cart_func:check_rail_in_direction(pos,fwd) then - return fwd - elseif cart_func:check_rail_in_direction(pos,down) then - return down + local left={x=0,y=0,z=0} + local right={x=0,y=0,z=0} + if dir.z ~= 0 and dir.x == 0 then + left.x=-dir.z --left is opposite sign from z + right.x=dir.z --right is same sign as z + elseif dir.x ~= 0 and dir.z == 0 then + left.z=dir.x --left is same sign as x + right.z=-dir.x --right is opposite sign from x + end --left and right + local leftdown=cart_func.v3:copy(left) + leftdown.y=-1 + local rightdown=cart_func.v3:copy(right) + rightdown.y=-1 + + local ignorekeys=false + --ignorekeypos stores a position where we changed direction because the + --player was pressing left or right keys. once we have changed direction, + --we want to ignore those keys until you have changed to a different rail + --(otherwise you end up reversing) + --I use the ignorekeys boolean to make the key logic more readable + if self.ignorekeypos then --ignorekeypos was set + if cart_func.v3:equal(self.ignorekeypos,pos) then + ignorekeys=true --if still at same position, ignore left and right keys + else + self.ignorekeypos=nil --if ignorekeypos was set but pos does not match anymore, clear it + end + end + + local ctrl=nil + if self.driver and not ignorekeys then + ctrl = self.driver:get_player_control() + end + + --if player is pressing left or right arrows, check left or right first + if ctrl and ctrl.left then --left key pressed, check left first + if cart_func:check_rail_in_direction(pos,left) then + self.ignorekeypos=cart_func.v3:copy(pos) --ignore keys until pos changes + return left + elseif cart_func:check_rail_in_direction(pos,leftdown) then + self.ignorekeypos=cart_func.v3:copy(pos) + return leftdown + end + elseif ctrl and ctrl.right then --right key pressed, check right first + if cart_func:check_rail_in_direction(pos,right) then + self.ignorekeypos=cart_func.v3:copy(pos) + return right + elseif cart_func:check_rail_in_direction(pos,rightdown) then + self.ignorekeypos=cart_func.v3:copy(pos) + return rightdown + end + end --ctrl.left + + --now for normal checks + if cart_func:check_rail_in_direction(pos,fwd) then + return fwd + elseif cart_func:check_rail_in_direction(pos,down) then + return down elseif cart_func:check_rail_in_direction(pos,up) then - return up + return up elseif (not ctrl or not ctrl.left) --only check left if we didnt above - and cart_func:check_rail_in_direction(pos,left) then - return left - elseif (not ctrl or not ctrl.left) - and cart_func:check_rail_in_direction(pos,leftdown) then - return leftdown - elseif (not ctrl or not ctrl.right) --only check right if we didnt above - and cart_func:check_rail_in_direction(pos,right) then - return right - elseif (not ctrl or not ctrl.right) - and cart_func:check_rail_in_direction(pos,rightdown) then - return rightdown - else - return {x=0, y=0, z=0} - end --normal rail checks + and cart_func:check_rail_in_direction(pos,left) then + return left + elseif (not ctrl or not ctrl.left) + and cart_func:check_rail_in_direction(pos,leftdown) then + return leftdown + elseif (not ctrl or not ctrl.right) --only check right if we didnt above + and cart_func:check_rail_in_direction(pos,right) then + return right + elseif (not ctrl or not ctrl.right) + and cart_func:check_rail_in_direction(pos,rightdown) then + return rightdown + else + return {x=0, y=0, z=0} + end --normal rail checks end --get_rail_direction - function cart:calc_rail_direction(pos, vel) local velocity = cart_func.v3:copy(vel) local p = cart_func.v3:copy(pos) @@ -236,31 +235,33 @@ function cart:calc_rail_direction(pos, vel) z = v * dir.z, } - --if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) then + --if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) then if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) and not cart_func:is_rail(p) then - -- First try this HACK - -- Move the cart on the rail if above or under it - if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=1, z=0})) and vel.y >= 0 then - p = cart_func.v3:add(p, {x=0, y=1, z=0}) - return self:calc_rail_direction(p, vel) - end - if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=-1, z=0})) and vel.y <= 0 then - p = cart_func.v3:add(p, {x=0, y=-1, z=0}) - return self:calc_rail_direction(p, vel) - end - -- Now the HACK gets really dirty - if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=2, z=0})) and vel.y >= 0 then - p = cart_func.v3:add(p, {x=0, y=1, z=0}) - return self:calc_rail_direction(p, vel) - end - if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=-2, z=0})) and vel.y <= 0 then - p = cart_func.v3:add(p, {x=0, y=-1, z=0}) - return self:calc_rail_direction(p, vel) - end - + --this loop tries to find the rail if we lost it moving up or down. + --it is almost always within 2 nodes, but SOMETIMES 3 or more away, and if you + --dont check those, then the cart gets stuck above or below the rail. + local upordown=1 + if vel.y<0 then upordown=-1 end + for i=1,2 do + local deltay=0 + while deltay<6 do + deltay=deltay+1 + if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=deltay*upordown, z=0})) then + p = cart_func.v3:add(p, {x=0, y=deltay*upordown, z=0}) + --print("cart: off rails"..i.." old_pos="..cart_func:pos_to_string(self.old_pos).." old_vel="..cart_func:pos_to_string(old_velocity).." pos in="..cart_func:pos_to_string(pos).." vel in="..cart_func:pos_to_string(vel).." dir="..cart_func:pos_to_string(dir)) + --print(" v="..v.." velocity="..cart_func:pos_to_string(velocity).." rail found at p="..cart_func:pos_to_string(p).." deltay="..deltay*upordown) + return self:calc_rail_direction(p,vel) + end --if + end --while + upordown=-upordown --if that direction failed, try the other direction. + end --for + + --if we got through all of that + --print("cart: off rails old_pos="..cart_func:pos_to_string(self.old_pos).." old_vel="..cart_func:pos_to_string(old_velocity).." pos in="..cart_func:pos_to_string(pos).." vel in="..cart_func:pos_to_string(vel).." dir="..cart_func:pos_to_string(dir)) + --print(" v="..v.." velocity="..cart_func:pos_to_string(velocity)) return {x=0, y=0, z=0}, p - end + end --hack if not cart_func.v3:equal(dir, dir_old) then return velocity, cart_func.v3:round(p) @@ -268,53 +269,44 @@ function cart:calc_rail_direction(pos, vel) end return velocity, p -end - - - ---because built in pos_to_string doesn't handle nil -function pos_to_string(pos) - if pos==nil then return "(nil)" - else return minetest.pos_to_string(pos) - end --poss==nill -end --pos_to_string - +end --calc_rail_direction function cart:on_step(dtime) local pos = self.object:getpos() local dir = cart_func:velocity_to_dir(self.velocity) - --minetest.chat_send_all("*!*carts: on_step top pos="..pos_to_string(pos).." dir="..pos_to_string(dir).." vel="..pos_to_string(self.velocity)) - --print("*!*carts:on_step top pos="..pos_to_string(pos).." dir="..pos_to_string(dir).." vel="..pos_to_string(self.velocity)) - - --count rails to put in debug.txt - if self.old_pos then - local cmp_old=vector.round(self.old_pos) - local cmp_new=vector.round(pos) - if cmp_old.x~=cmp_new.x or cmp_old.y~=cmp_new.y or cmp_old.z~=cmp_new.z then - self.railcount=self.railcount+1 - --local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration")) - --local railtype="" - --if a and a>0 then railtype="power" end - --minetest.chat_send_all("-- cart pos="..pos_to_string(vector.round(pos)).." railcount="..self.railcount.." vel="..pos_to_string(self.velocity).." "..railtype) - end - end - - local ctrl=nil - if self.driver then - ctrl = self.driver:get_player_control() - if ctrl and ctrl.jump and not self.lockyaw then - self.lockyaw=true - self.yawtarget=self.object:getyaw() - minetest.chat_send_player(self.driver:get_player_name(),"cart: player view locked to cart, hit SNEAK to unlock") - elseif ctrl and ctrl.sneak and self.lockyaw then - self.lockyaw=false - minetest.chat_send_player(self.driver:get_player_name(),"cart: player view NOT locked to cart, hit JUMP to lock") - end - end --check lockyaw if self.driver - + --minetest.chat_send_all("cart: on_step top pos="..cart_func:pos_to_string(pos).." dir="..cart_func:pos_to_string(dir).." vel="..cart_func:pos_to_string(self.velocity)) + --print("cart: on_step top pos="..cart_func:pos_to_string(pos).." dir="..cart_func:pos_to_string(dir).." vel="..cart_func:pos_to_string(self.velocity)) + + --count rails to put in debug.txt + if self.old_pos then + local cmp_old=vector.round(self.old_pos) + local cmp_new=vector.round(pos) + if cmp_old.x~=cmp_new.x or cmp_old.y~=cmp_new.y or cmp_old.z~=cmp_new.z then + self.railcount=self.railcount+1 + --local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration")) + --local railtype="" + --if a and a>0 then railtype="power" end + --minetest.chat_send_all("-- cart pos="..cart_func:pos_to_string(vector.round(pos)).." railcount="..self.railcount.." vel="..cart_func:pos_to_string(self.velocity).." "..railtype) + end + end + --check to see if we should lock or unlock player view to cart + local ctrl=nil + if self.driver then + ctrl = self.driver:get_player_control() + --if user pressed jump while driving cart, and lockyaw not set, then lock view to cart + if ctrl and ctrl.jump and not self.lockyaw then + self.lockyaw=true + self.yawtarget=self.object:getyaw() + minetest.chat_send_player(self.driver:get_player_name(),"cart: player view locked to cart, hit SNEAK to unlock") + --if user pressed sneak while driving cart, and lockyaw is set, then unlock view from cart + elseif ctrl and ctrl.sneak and self.lockyaw then + self.lockyaw=false + minetest.chat_send_player(self.driver:get_player_name(),"cart: player view NOT locked to cart, hit JUMP to lock") + end + end --check lockyaw if self.driver if not cart_func.v3:equal(self.velocity, {x=0,y=0,z=0}) then self.pre_stop_dir = cart_func:velocity_to_dir(self.velocity) @@ -322,7 +314,6 @@ function cart:on_step(dtime) -- Stop the cart if the velocity is nearly 0 -- Only if on a flat railway - if dir.y == 0 then if math.abs(self.velocity.x) < 0.1 and math.abs(self.velocity.z) < 0.1 then -- Start the cart if powered from mesecons @@ -357,7 +348,7 @@ function cart:on_step(dtime) z = 0, } self.old_velocity = self.velocity - return + return end end end @@ -419,62 +410,63 @@ function cart:on_step(dtime) dir = cart_func:velocity_to_dir(self.velocity) -- Accelerate or decelerate the cart according to the pitch and acceleration of the rail node - local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration")) - if not a then - a = 0 - end - local t = tonumber(minetest.env:get_meta(pos):get_string("cart_touring_velocity")) - if not t then t=0 end - if t>0 then - local vx=math.abs(self.velocity.x) - local vy=math.abs(self.velocity.y) - local vz=math.abs(self.velocity.z) - -- make v the largest of the 3 velocities - local v=vx - if vy>v then v=vy end - if vz>v then v=vz end - -- - local diff=0 - local acelordecl=0 - if v>t then - diff=v-t - acelordecl=-1 - elseif vt - --print(" on_step t1 v="..v.." t="..t.." diff="..diff.." a="..a.." acelordecl="..acelordecl) - --adjust for grav - if self.velocity.y<0 then --going downhill so grav will acel by extra 0.13 - --if we are decel then add an extra 0.13 to how much we need to decel - --if we are accel then subtract an extra 0.13 from how much we need to acel - diff=diff-(0.13*acelordecl) - elseif self.velocity.y>0 then --going uphill grav will decl by extra 0.10 - --if we are decel then subtract 0.1 from how much we need to decel - --if we are acel then add 0.1 to how much we need to acel - diff=diff+(0.1*acelordecl) - end -- self.velocity.y<0 - --so now diff is the difference between cart velocity (after this turns grav) - --and our target touring velocity - --print("*!* on_step t2 grav v="..v.." diff="..diff.." a="..a) - if diffa*4 then - a=a*2 --if big difference, play catchup fast! - elseif diff>a*3 then - a=a*1.5 --if big difference, play catchup fast! - end --diff0 - - --check if down arrow is being pressed (hand break) - if self.driver then - local ctrl = self.driver:get_player_control() - if ctrl and ctrl.down then - a=a-0.1 --same as uphill - - end --if hand break - end --if self.driver + local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration")) + if not a then + a = 0 + end + + --touring rail adjustments + --this needs to be redone, if I moved this to AFTER the gravity section, + --then there would be no need for the messy logic to adjust for gravity here. + local t = tonumber(minetest.env:get_meta(pos):get_string("cart_touring_velocity")) + if not t then t=0 end + if t>0 then + local vx=math.abs(self.velocity.x) + local vy=math.abs(self.velocity.y) + local vz=math.abs(self.velocity.z) + -- make v the largest of the 3 velocities + local v=vx + if vy>v then v=vy end + if vz>v then v=vz end + -- + local diff=0 + local acelordecl=0 + if v>t then + diff=v-t + acelordecl=-1 + elseif vt + --adjust for grav + if self.velocity.y<0 then --going downhill so grav will acel by extra 0.13 + --if we are decel then add an extra 0.13 to how much we need to decel + --if we are accel then subtract an extra 0.13 from how much we need to acel + diff=diff-(0.13*acelordecl) + elseif self.velocity.y>0 then --going uphill grav will decl by extra 0.10 + --if we are decel then subtract 0.1 from how much we need to decel + --if we are acel then add 0.1 to how much we need to acel + diff=diff+(0.1*acelordecl) + end -- self.velocity.y<0 + --so now diff is the difference between cart velocity (after this turns grav) + --and our target touring velocity + if diffa*4 then + a=a*2 --if big difference, play catchup fast! + elseif diff>a*3 then + a=a*1.5 --if big difference, play catchup fast! + end --diff0 + + --check if down arrow is being pressed (hand break) + if self.driver then + local ctrl = self.driver:get_player_control() + if ctrl and ctrl.down then + a=a-0.1 --same as uphill + end --if hand break + end --if self.driver if self.velocity.y < 0 then self.velocity = { @@ -549,7 +541,7 @@ function cart:on_step(dtime) self.old_pos = self.object:getpos() self.old_velocity = cart_func.v3:copy(self.velocity) - local oldyaw=self.object:getyaw() + local oldyaw=self.object:getyaw() if dir.x < 0 then self.object:setyaw(math.pi/2) elseif dir.x > 0 then @@ -560,32 +552,31 @@ function cart:on_step(dtime) self.object:setyaw(0) end - local newyaw=self.object:getyaw() - --now if driver and lockyaw, change drivers direction. - if self.driver and self.lockyaw then - if oldyaw~=newyaw then - self.yawtarget=newyaw --set new target - --print("--Cart yawtarget set "..self.yawtarget) - end - local playeryaw=self.driver:get_look_yaw()-1.57 - if playeryaw<0 then playeryaw=playeryaw+(math.pi*2) end - if self.yawtarget and playeryaw ~= self.yawtarget then - local diff = self.yawtarget - playeryaw - if diff>math.pi then - diff=diff-(2*math.pi) - elseif diff<(-math.pi) then - diff=diff+(2*math.pi) - end - yawdir=cart_func:get_sign(diff) - local step=self.YAW_STEP - if math.abs(diff)<=self.YAW_STEP then - step=diff - self.yawtarget=nil - end - local setyaw=playeryaw+(step*yawdir) - self.driver:set_look_yaw(setyaw) - end --move yaw - end --lockyaw set + local newyaw=self.object:getyaw() + --now if driver and lockyaw, change drivers viewing direction. + if self.driver and self.lockyaw then + if oldyaw~=newyaw then + self.yawtarget=newyaw --set new target + end + local playeryaw=self.driver:get_look_yaw()-1.57 + if playeryaw<0 then playeryaw=playeryaw+(math.pi*2) end + if self.yawtarget and playeryaw ~= self.yawtarget then + local diff = self.yawtarget - playeryaw + if diff>math.pi then + diff=diff-(2*math.pi) + elseif diff<(-math.pi) then + diff=diff+(2*math.pi) + end + yawdir=cart_func:get_sign(diff) + local step=self.YAW_STEP + if math.abs(diff)<=self.YAW_STEP then + step=diff + self.yawtarget=nil + end + local setyaw=playeryaw+(step*yawdir) + self.driver:set_look_yaw(setyaw) + end --move yaw + end --lockyaw set if dir.y == -1 then self.object:set_animation({x=1, y=1}, 1, 0) @@ -595,7 +586,8 @@ function cart:on_step(dtime) self.object:set_animation({x=0, y=0}, 1, 0) end -end +end --on_step + minetest.register_entity("carts:cart", cart) @@ -611,15 +603,15 @@ minetest.register_craftitem("carts:cart", { end if cart_func:is_rail(pointed_thing.under) then local obj=minetest.env:add_entity(pointed_thing.under, "carts:cart") - print("cart: begin pos="..pos_to_string(vector.round(obj:getpos()))) - minetest.chat_send_player(placer:get_player_name(),"cart: right click to ride, left click to push, SNEAK-left click to put back in inventory, JUMP to lock view to cart, SNEAK to unlock view from cart, LEFT and RIGHT ARROWS to switch tracks, DOWN ARROW to apply hand break") + print("cart: begin pos="..cart_func:pos_to_string(vector.round(obj:getpos()))) + minetest.chat_send_player(placer:get_player_name(),"cart: right click to ride, left click to push, SNEAK-left click to put back in inventory, JUMP to lock view to cart, SNEAK to unlock view from cart, LEFT and RIGHT ARROWS to switch tracks, DOWN ARROW to apply hand break") if not minetest.setting_getbool("creative_mode") then itemstack:take_item() end return itemstack elseif cart_func:is_rail(pointed_thing.above) then minetest.env:add_entity(pointed_thing.above, "carts:cart") - print("cart: begin pos="..pos_to_string(self.object:getpos())) + print("cart: begin pos="..cart_func:pos_to_string(self.object:getpos())) if not minetest.setting_getbool("creative_mode") then itemstack:take_item() end @@ -693,6 +685,7 @@ minetest.register_node("carts:powerrail", { }, }) + minetest.register_node("carts:brakerail", { description = "Brake Rail", drawtype = "raillike", @@ -730,39 +723,39 @@ minetest.register_node("carts:brakerail", { minetest.register_node("carts:touringrail", { - description = "Touring Rail", - drawtype = "raillike", - tiles = {"carts_rail_tour.png", "carts_rail_curved_tour.png", "carts_rail_t_junction_tour.png", "carts_rail_crossing_tour.png"}, - inventory_image = "carts_rail_tour.png", - wield_image = "carts_rail_tour.png", - paramtype = "light", - is_ground_content = true, - walkable = false, - selection_box = { - type = "fixed", - -- but how to specify the dimensions for curved and sideways rails? - fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, - }, - groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1}, - - after_place_node = function(pos, placer, itemstack) - if not mesecon then - minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5") - minetest.env:get_meta(pos):set_string("cart_touring_velocity", cart.TARGET_TOUR_V) - end - end, - - mesecons = { - effector = { - action_on = function(pos, node) - minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5") - end, - - action_off = function(pos, node) - minetest.env:get_meta(pos):set_string("cart_acceleration", "0") - end, - }, - }, + description = "Touring Rail", + drawtype = "raillike", + tiles = {"carts_rail_tour.png", "carts_rail_curved_tour.png", "carts_rail_t_junction_tour.png", "carts_rail_crossing_tour.png"}, + inventory_image = "carts_rail_tour.png", + wield_image = "carts_rail_tour.png", + paramtype = "light", + is_ground_content = true, + walkable = false, + selection_box = { + type = "fixed", + -- but how to specify the dimensions for curved and sideways rails? + fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, + }, + groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1}, + + after_place_node = function(pos, placer, itemstack) + if not mesecon then + minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5") + minetest.env:get_meta(pos):set_string("cart_touring_velocity", cart.TARGET_TOUR_V) + end + end, + + mesecons = { + effector = { + action_on = function(pos, node) + minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5") + end, + + action_off = function(pos, node) + minetest.env:get_meta(pos):set_string("cart_acceleration", "0") + end, + }, + }, }) @@ -775,6 +768,7 @@ minetest.register_craft({ } }) + minetest.register_craft({ output = "carts:powerrail 2", recipe = { @@ -784,6 +778,7 @@ minetest.register_craft({ } }) + minetest.register_craft({ output = "carts:brakerail 2", recipe = {